diff --git a/.eslintrc b/.eslintrc.json similarity index 100% rename from .eslintrc rename to .eslintrc.json diff --git a/.github/calendar.yml b/.github/calendar.yml index 6d3e0f593d7..d7827edb02b 100644 --- a/.github/calendar.yml +++ b/.github/calendar.yml @@ -25,5 +25,7 @@ '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 + '2018-08-13 12:00, US/Pacific': 'development', # 1.26.0 released + '2018-08-27 18:00, US/Pacific': 'endgame', +# '2018-09-05 12:00, US/Pacific': 'development', # 1.27.0 released } diff --git a/.github/classifier.yml b/.github/classifier.yml index c1c067e5c53..a7bcb40c96f 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -15,7 +15,7 @@ css-less-scss: [ aeschli ], debug-console: [], debug: { - assignees: [ weinand ], + assignees: [ isidorn ], assignLabel: false }, diff-editor: [], diff --git a/.gitignore b/.gitignore index 08adb4af663..6a9804cd237 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ npm-debug.log Thumbs.db node_modules/ .build/ +extensions/**/dist/ out/ out-build/ out-editor/ @@ -17,4 +18,4 @@ build/node_modules coverage/ test_data/ test-results/ -yarn-error.log \ No newline at end of file +yarn-error.log diff --git a/.vscode/launch.json b/.vscode/launch.json index 2a12dea7bde..26dce40e474 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,6 +19,7 @@ "protocol": "inspector", "port": 5870, "restart": true, + "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -29,6 +30,7 @@ "name": "Attach to Shared Process", "protocol": "inspector", "port": 5871, + "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -39,6 +41,7 @@ "protocol": "inspector", "name": "Attach to Search Process", "port": 5876, + "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -49,6 +52,7 @@ "name": "Attach to CLI Process", "protocol": "inspector", "port": 5874, + "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -59,6 +63,7 @@ "name": "Attach to Main Process", "protocol": "inspector", "port": 5875, + "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -124,6 +129,7 @@ "type": "chrome", "request": "attach", "name": "Attach to VS Code", + "smartStep": true, "port": 9222 }, { @@ -143,6 +149,7 @@ "runtimeArgs": [ "--inspect=5875" ], + "smartStep": true, "skipFiles": [ "**/winjs*.js" ], @@ -201,7 +208,6 @@ "linux": { "runtimeExecutable": "${workspaceFolder}/.build/electron/code-oss" }, - "stopOnEntry": false, "outputCapture": "std", "args": [ "--delay", diff --git a/.yarnrc b/.yarnrc index 42f08fa0c02..58d37cc88fb 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "1.7.12" +target "2.0.7" runtime "electron" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0d3f7dc4bf3..f3166331378 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ Don't feel bad if the developers can't reproduce the issue right away. They will ### Follow Your Issue -Once submitted, your report will go into the [issue tracking](https://github.com/Microsoft/vscode/wiki/Issue-Tracking) work flow. Be sure to understand what will happen next, so you know what to expect, and how to continue to assist throughout the process. +Once submitted, your report will go into the [issue tracking](https://github.com/Microsoft/vscode/wiki/Issue-Tracking) workflow. Be sure to understand what will happen next, so you know what to expect, and how to continue to assist throughout the process. ## Automated Issue Management diff --git a/OSSREADME.json b/OSSREADME.json index 8f167afc904..e15cccabcad 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.7", "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 499d1df761d..e737360a6fa 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,12 +1,12 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.26.5", + "version": "1.27.5", "repo": "https://github.com/Microsoft/vscode-node-debug" }, { "name": "ms-vscode.node-debug2", - "version": "1.26.5", + "version": "1.27.1", "repo": "https://github.com/Microsoft/vscode-node-debug2" } ] diff --git a/build/dependencies.js b/build/dependencies.js index a0b1ee01dc5..2adf7e29ca7 100644 --- a/build/dependencies.js +++ b/build/dependencies.js @@ -43,7 +43,7 @@ function asYarnDependency(prefix, tree) { } function getYarnProductionDependencies(cwd) { - const raw = cp.execSync('yarn list --json', { cwd, encoding: 'utf8', env: { ...process.env, NODE_ENV: 'production' }, stdio: [null, null, 'ignore'] }); + const raw = cp.execSync('yarn list --json', { cwd, encoding: 'utf8', env: { ...process.env, NODE_ENV: 'production' }, stdio: [null, null, 'inherit'] }); const match = /^{"type":"tree".*$/m.exec(raw); if (!match || match.length !== 1) { diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 562ee1dd754..0c7a37b1f76 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -122,17 +122,25 @@ gulp.task('clean-minified-editor', util.rimraf('out-editor-min')); gulp.task('minify-editor', ['clean-minified-editor', 'optimize-editor'], common.minifyTask('out-editor')); gulp.task('clean-editor-esm', util.rimraf('out-editor-esm')); -gulp.task('extract-editor-esm', ['clean-editor-esm', 'clean-editor-distro'], function () { - standalone.createESMSourcesAndResources({ - entryPoints: [ - 'vs/editor/editor.main', - 'vs/editor/editor.worker' - ], +gulp.task('extract-editor-esm', ['clean-editor-esm', 'clean-editor-distro', 'extract-editor-src'], function () { + standalone.createESMSourcesAndResources2({ + srcFolder: './out-editor-src', outFolder: './out-editor-esm/src', outResourcesFolder: './out-monaco-editor-core/esm', - redirects: { - 'vs/base/browser/ui/octiconLabel/octiconLabel': 'vs/base/browser/ui/octiconLabel/octiconLabel.mock', - 'vs/nls': 'vs/nls.mock', + ignores: [ + 'inlineEntryPoint:0.ts', + 'inlineEntryPoint:1.ts', + 'vs/loader.js', + 'vs/nls.ts', + 'vs/nls.build.js', + 'vs/nls.d.ts', + 'vs/css.js', + 'vs/css.build.js', + 'vs/css.d.ts', + 'vs/base/worker/workerMain.ts', + ], + renames: { + 'vs/nls.mock.ts': 'vs/nls.ts' } }); }); @@ -194,7 +202,7 @@ gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify this.emit('data', new File({ path: data.path.replace(/monaco\.d\.ts/, 'editor.api.d.ts'), base: data.base, - contents: new Buffer(toExternalDTS(data.contents.toString())) + contents: Buffer.from(toExternalDTS(data.contents.toString())) })); })) .pipe(gulp.dest('out-monaco-editor-core/esm/vs/editor')), diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index acc7d90bfb7..bd38427a422 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -167,4 +167,4 @@ gulp.task('watch-extensions', tasks.map(t => t.watch)); gulp.task('clean-extensions-build', tasks.map(t => t.cleanBuild)); gulp.task('compile-extensions-build', tasks.map(t => t.compileBuild)); -gulp.task('watch-extensions-build', tasks.map(t => t.watchBuild)); \ No newline at end of file +gulp.task('watch-extensions-build', tasks.map(t => t.watchBuild)); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 090db00ffeb..b06987077c9 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -29,7 +29,6 @@ const packageJson = require('../package.json'); const product = require('../product.json'); const crypto = require('crypto'); const i18n = require('./lib/i18n'); -const glob = require('glob'); const deps = require('./dependencies'); const getElectronVersion = require('./lib/electron').getElectronVersion; const createAsar = require('./lib/asar').createAsar; @@ -44,15 +43,6 @@ const nodeModules = ['electron', 'original-fs'] .concat(baseModules); // Build -const builtInExtensions = require('./builtInExtensions.json'); - -const excludedExtensions = [ - 'vscode-api-tests', - 'vscode-colorize-tests', - 'ms-vscode.node-debug', - 'ms-vscode.node-debug2', -]; - const vscodeEntryPoints = _.flatten([ buildfile.entrypoint('vs/workbench/workbench.main'), buildfile.base, @@ -113,9 +103,9 @@ gulp.task('optimize-index-js', ['optimize-vscode'], () => { fs.writeFileSync(fullpath, newContents); }); -const baseUrl = `https://ticino.blob.core.windows.net/sourcemaps/${commit}/core`; +const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; gulp.task('clean-minified-vscode', util.rimraf('out-vscode-min')); -gulp.task('minify-vscode', ['clean-minified-vscode', 'optimize-index-js'], common.minifyTask('out-vscode', baseUrl)); +gulp.task('minify-vscode', ['clean-minified-vscode', 'optimize-index-js'], common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`)); // Package @@ -223,39 +213,20 @@ function packageTask(platform, arch, opts) { 'vs/workbench/workbench.main.js', 'vs/workbench/workbench.main.css', 'vs/workbench/electron-browser/bootstrap/index.html', - 'vs/workbench/electron-browser/bootstrap/index.js', - 'vs/workbench/electron-browser/bootstrap/preload.js' + 'vs/workbench/electron-browser/bootstrap/index.js' ]); const src = gulp.src(out + '/**', { base: '.' }) - .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + out), 'out'); })); - - const root = path.resolve(path.join(__dirname, '..')); - const localExtensionDescriptions = glob.sync('extensions/*/package.json') - .map(manifestPath => { - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); - return { name: extensionName, path: extensionPath }; - }) - .filter(({ name }) => excludedExtensions.indexOf(name) === -1) - .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); - - const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { - return ext.fromLocal(extension.path) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - })); - - const localExtensionDependencies = gulp.src('extensions/node_modules/**', { base: '.' }); - - const marketplaceExtensions = es.merge(...builtInExtensions.map(extension => { - return ext.fromMarketplace(extension.name, extension.version) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - })); - - const sources = es.merge(src, localExtensions, localExtensionDependencies, marketplaceExtensions) + .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + out), 'out'); })) .pipe(util.setExecutableBit(['**/*.sh'])) .pipe(filter(['**', '!**/*.js.map'])); + const root = path.resolve(path.join(__dirname, '..')); + + const sources = es.merge(src, ext.packageExtensionsStream({ + sourceMappingURLBase: sourceMappingURLBase + })); + let version = packageJson.version; // @ts-ignore JSON checking: quality is optional const quality = product.quality; @@ -452,16 +423,22 @@ gulp.task('vscode-translations-import', function () { // Sourcemaps -gulp.task('upload-vscode-sourcemaps', ['minify-vscode'], () => { +gulp.task('upload-vscode-sourcemaps', ['vscode-darwin-min', 'minify-vscode'], () => { const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) .pipe(es.mapSync(f => { f.path = `${f.base}/core/${f.relative}`; return f; })); - const extensions = gulp.src('extensions/**/out/**/*.map', { base: '.' }); + const extensionsOut = gulp.src('extensions/**/out/**/*.map', { base: '.' }); + const extensionsDist = gulp.src('extensions/**/dist/**/*.map', { base: '.' }); - return es.merge(vs, extensions) + return es.merge(vs, extensionsOut, extensionsDist) + .pipe(es.through(function (data) { + // debug + console.log('Uploading Sourcemap', data.relative); + this.emit('data', data); + })) .pipe(azure.upload({ account: process.env.AZURE_STORAGE_ACCOUNT, key: process.env.AZURE_STORAGE_ACCESS_KEY, @@ -506,7 +483,7 @@ function getSettingsSearchBuildId(packageJson) { const branch = process.env.BUILD_SOURCEBRANCH; const branchId = branch.indexOf('/release/') >= 0 ? 0 : /\/master$/.test(branch) ? 1 : - 2; // Some unexpected branch + 2; // Some unexpected branch const out = cp.execSync(`git rev-list HEAD --count`); const count = parseInt(out.toString()); diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 49f2cad8ad7..61b29d260cf 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -214,9 +214,6 @@ gulp.task('clean-vscode-linux-arm-rpm', util.rimraf('.build/linux/rpm/armhf')); gulp.task('clean-vscode-linux-ia32-snap', util.rimraf('.build/linux/snap/x64')); gulp.task('clean-vscode-linux-x64-snap', util.rimraf('.build/linux/snap/x64')); gulp.task('clean-vscode-linux-arm-snap', util.rimraf('.build/linux/snap/x64')); -gulp.task('clean-vscode-linux-ia32-flatpak', util.rimraf('.build/linux/flatpak/i386')); -gulp.task('clean-vscode-linux-x64-flatpak', util.rimraf('.build/linux/flatpak/x86_64')); -gulp.task('clean-vscode-linux-arm-flatpak', util.rimraf('.build/linux/flatpak/arm')); gulp.task('vscode-linux-ia32-prepare-deb', ['clean-vscode-linux-ia32-deb'], prepareDebPackage('ia32')); gulp.task('vscode-linux-x64-prepare-deb', ['clean-vscode-linux-x64-deb'], prepareDebPackage('x64')); diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index da38acec4f4..43855048c1f 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -46,7 +46,7 @@ export interface IEntryPoint { name: string; include?: string[]; exclude?: string[]; - prepend: string[]; + prepend?: string[]; append?: string[]; dest?: string; } diff --git a/build/lib/extensions.js b/build/lib/extensions.js index ce74ac25ee6..202cded3dcc 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -3,6 +3,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __assign = (this && this.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; Object.defineProperty(exports, "__esModule", { value: true }); var es = require("event-stream"); var assign = require("object-assign"); @@ -14,14 +22,19 @@ var rename = require('gulp-rename'); var util = require('gulp-util'); var buffer = require('gulp-buffer'); var json = require('gulp-json-editor'); +var webpack = require('webpack'); +var webpackGulp = require('webpack-stream'); var fs = require("fs"); var path = require("path"); var vsce = require("vsce"); var File = require("vinyl"); -function fromLocal(extensionPath) { +var glob = require("glob"); +var gulp = require("gulp"); +var util2 = require("./util"); +var root = path.resolve(path.join(__dirname, '..', '..')); +function fromLocal(extensionPath, sourceMappingURLBase) { var result = es.through(); - vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn }) - .then(function (fileNames) { + vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn }).then(function (fileNames) { var files = fileNames .map(function (fileName) { return path.join(extensionPath, fileName); }) .map(function (filePath) { return new File({ @@ -30,9 +43,58 @@ function fromLocal(extensionPath) { base: extensionPath, contents: fs.createReadStream(filePath) }); }); - es.readArray(files).pipe(result); - }) - .catch(function (err) { return result.emit('error', err); }); + var filesStream = es.readArray(files); + // check for a webpack configuration file, then invoke webpack + // and merge its output with the files stream. also rewrite the package.json + // file to a new entry point + if (fs.existsSync(path.join(extensionPath, 'extension.webpack.config.js'))) { + var packageJsonFilter = filter('package.json', { restore: true }); + var patchFilesStream = filesStream + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(json(function (data) { + // hardcoded entry point directory! + data.main = data.main.replace('/out/', /dist/); + return data; + })) + .pipe(packageJsonFilter.restore); + var webpackConfig = __assign({}, require(path.join(extensionPath, 'extension.webpack.config.js')), { mode: 'production', stats: 'errors-only' }); + var webpackStream = webpackGulp(webpackConfig, webpack) + .pipe(es.through(function (data) { + data.stat = data.stat || {}; + data.base = extensionPath; + this.emit('data', data); + })) + .pipe(es.through(function (data) { + // source map handling: + // * rewrite sourceMappingURL + // * save to disk so that upload-task picks this up + if (sourceMappingURLBase) { + var contents = data.contents.toString('utf8'); + data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { + return "\n//# sourceMappingURL=" + sourceMappingURLBase + "/extensions/" + path.basename(extensionPath) + "/dist/" + g1; + }), 'utf8'); + if (/\.js\.map$/.test(data.path)) { + if (!fs.existsSync(path.dirname(data.path))) { + fs.mkdirSync(path.dirname(data.path)); + } + fs.writeFileSync(data.path, data.contents); + } + } + this.emit('data', data); + })); + es.merge(webpackStream, patchFilesStream) + // .pipe(es.through(function (data) { + // // debug + // console.log('out', data.path, data.contents.length); + // this.emit('data', data); + // })) + .pipe(result); + } + else { + filesStream.pipe(result); + } + }).catch(function (err) { return result.emit('error', err); }); return result; } exports.fromLocal = fromLocal; @@ -117,3 +179,49 @@ function fromMarketplace(extensionName, version) { })); } exports.fromMarketplace = fromMarketplace; +var excludedExtensions = [ + 'vscode-api-tests', + 'vscode-colorize-tests', + 'ms-vscode.node-debug', + 'ms-vscode.node-debug2', +]; +var builtInExtensions = require('../builtInExtensions.json'); +function packageExtensionsStream(opts) { + opts = opts || {}; + var localExtensionDescriptions = glob.sync('extensions/*/package.json') + .map(function (manifestPath) { + var extensionPath = path.dirname(path.join(root, manifestPath)); + var extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath }; + }) + .filter(function (_a) { + var name = _a.name; + return excludedExtensions.indexOf(name) === -1; + }) + .filter(function (_a) { + var name = _a.name; + return opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true; + }) + .filter(function (_a) { + var name = _a.name; + return builtInExtensions.every(function (b) { return b.name !== name; }); + }); + var localExtensions = es.merge.apply(es, localExtensionDescriptions.map(function (extension) { + return fromLocal(extension.path, opts.sourceMappingURLBase) + .pipe(rename(function (p) { return p.dirname = "extensions/" + extension.name + "/" + p.dirname; })); + })); + var localExtensionDependencies = gulp.src('extensions/node_modules/**', { base: '.' }); + var marketplaceExtensions = es.merge.apply(es, builtInExtensions + .filter(function (_a) { + var name = _a.name; + return opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true; + }) + .map(function (extension) { + return fromMarketplace(extension.name, extension.version) + .pipe(rename(function (p) { return p.dirname = "extensions/" + extension.name + "/" + p.dirname; })); + })); + return es.merge(localExtensions, localExtensionDependencies, marketplaceExtensions) + .pipe(util2.setExecutableBit(['**/*.sh'])) + .pipe(filter(['**', '!**/*.js.map'])); +} +exports.packageExtensionsStream = packageExtensionsStream; diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 4b05bea1979..3c74455ccad 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -14,28 +14,93 @@ const rename = require('gulp-rename'); const util = require('gulp-util'); const buffer = require('gulp-buffer'); const json = require('gulp-json-editor'); +const webpack = require('webpack'); +const webpackGulp = require('webpack-stream'); import * as fs from 'fs'; import * as path from 'path'; import * as vsce from 'vsce'; import * as File from 'vinyl'; +import * as glob from 'glob'; +import * as gulp from 'gulp'; +import * as util2 from './util'; -export function fromLocal(extensionPath: string): Stream { - const result = es.through(); +const root = path.resolve(path.join(__dirname, '..', '..')); - vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn }) - .then(fileNames => { - const files = fileNames - .map(fileName => path.join(extensionPath, fileName)) - .map(filePath => new File({ - path: filePath, - stat: fs.statSync(filePath), - base: extensionPath, - contents: fs.createReadStream(filePath) as any - })); +export function fromLocal(extensionPath: string, sourceMappingURLBase?: string): Stream { + let result = es.through(); - es.readArray(files).pipe(result); - }) - .catch(err => result.emit('error', err)); + vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn }).then(fileNames => { + const files = fileNames + .map(fileName => path.join(extensionPath, fileName)) + .map(filePath => new File({ + path: filePath, + stat: fs.statSync(filePath), + base: extensionPath, + contents: fs.createReadStream(filePath) as any + })); + + const filesStream = es.readArray(files); + + // check for a webpack configuration file, then invoke webpack + // and merge its output with the files stream. also rewrite the package.json + // file to a new entry point + if (fs.existsSync(path.join(extensionPath, 'extension.webpack.config.js'))) { + const packageJsonFilter = filter('package.json', { restore: true }); + + const patchFilesStream = filesStream + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(json(data => { + // hardcoded entry point directory! + data.main = data.main.replace('/out/', /dist/); + return data; + })) + .pipe(packageJsonFilter.restore); + + const webpackConfig = { + ...require(path.join(extensionPath, 'extension.webpack.config.js')), + ...{ mode: 'production', stats: 'errors-only' } + }; + const webpackStream = webpackGulp(webpackConfig, webpack) + .pipe(es.through(function (data) { + data.stat = data.stat || {}; + data.base = extensionPath; + this.emit('data', data); + })) + .pipe(es.through(function (data: File) { + // source map handling: + // * rewrite sourceMappingURL + // * save to disk so that upload-task picks this up + if (sourceMappingURLBase) { + const contents = (data.contents).toString('utf8'); + data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { + return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/dist/${g1}`; + }), 'utf8'); + + if (/\.js\.map$/.test(data.path)) { + if (!fs.existsSync(path.dirname(data.path))) { + fs.mkdirSync(path.dirname(data.path)); + } + fs.writeFileSync(data.path, data.contents); + } + } + this.emit('data', data); + })) + ; + + es.merge(webpackStream, patchFilesStream) + // .pipe(es.through(function (data) { + // // debug + // console.log('out', data.path, data.contents.length); + // this.emit('data', data); + // })) + .pipe(result); + + } else { + filesStream.pipe(result); + } + + }).catch(err => result.emit('error', err)); return result; } @@ -131,3 +196,54 @@ export function fromMarketplace(extensionName: string, version: string): Stream })); })); } + +interface IPackageExtensionsOptions { + /** + * Set to undefined to package all of them. + */ + desiredExtensions?: string[]; + sourceMappingURLBase?: string; +} + +const excludedExtensions = [ + 'vscode-api-tests', + 'vscode-colorize-tests', + 'ms-vscode.node-debug', + 'ms-vscode.node-debug2', +]; + +const builtInExtensions: { name: string, version: string, repo: string; }[] = require('../builtInExtensions.json'); + +export function packageExtensionsStream(opts?: IPackageExtensionsOptions): NodeJS.ReadWriteStream { + opts = opts || {}; + + const localExtensionDescriptions = (glob.sync('extensions/*/package.json')) + .map(manifestPath => { + const extensionPath = path.dirname(path.join(root, manifestPath)); + const extensionName = path.basename(extensionPath); + return { name: extensionName, path: extensionPath }; + }) + .filter(({ name }) => excludedExtensions.indexOf(name) === -1) + .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) + .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); + + const localExtensions = es.merge(...localExtensionDescriptions.map(extension => { + return fromLocal(extension.path, opts.sourceMappingURLBase) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + })); + + const localExtensionDependencies = gulp.src('extensions/node_modules/**', { base: '.' }); + + const marketplaceExtensions = es.merge( + ...builtInExtensions + .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) + .map(extension => { + return fromMarketplace(extension.name, extension.version) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + }) + ); + + return es.merge(localExtensions, localExtensionDependencies, marketplaceExtensions) + .pipe(util2.setExecutableBit(['**/*.sh'])) + .pipe(filter(['**', '!**/*.js.map'])); +} \ No newline at end of file diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 4fafbd800a9..60169cf1892 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -20,6 +20,7 @@ var util = require("./util"); var gulpUtil = require("gulp-util"); var flatmap = require("gulp-flatmap"); var pump = require("pump"); +var i18n_1 = require("./i18n"); var REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix, message) { gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); @@ -164,6 +165,10 @@ function optimizeTask(opts) { addComment: true, includeContent: true })) + .pipe(opts.languages && opts.languages.length ? i18n_1.processNlsFiles({ + fileHeader: bundledFileHeader, + languages: opts.languages + }) : es.through()) .pipe(gulp.dest(out)); }; } diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 78328e3ce2c..038cf09dd21 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -22,7 +22,7 @@ import * as gulpUtil from 'gulp-util'; import * as flatmap from 'gulp-flatmap'; import * as pump from 'pump'; import * as sm from 'source-map'; -import { Language } from './i18n'; +import { processNlsFiles, Language } from './i18n'; const REPO_ROOT_PATH = path.join(__dirname, '../..'); @@ -239,6 +239,10 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr addComment: true, includeContent: true })) + .pipe(opts.languages && opts.languages.length ? processNlsFiles({ + fileHeader: bundledFileHeader, + languages: opts.languages + }) : es.through()) .pipe(gulp.dest(out)); }; } diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 885d3e789f5..3e2bacc25d5 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -10,7 +10,6 @@ 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) { @@ -76,6 +75,8 @@ function extractEditor(options) { } var tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); tsConfig.compilerOptions.noUnusedLocals = false; + tsConfig.compilerOptions.preserveConstEnums = false; + tsConfig.compilerOptions.declaration = false; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ 'vs/css.build.js', @@ -94,167 +95,148 @@ function extractEditor(options) { ].forEach(copyFile); } exports.extractEditor = extractEditor; -function createESMSourcesAndResources(options) { +function createESMSourcesAndResources2(options) { + var SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder); var OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); var OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); - var in_queue = Object.create(null); - var queue = []; - var enqueue = function (module) { - if (in_queue[module]) { - return; + var getDestAbsoluteFilePath = function (file) { + var dest = options.renames[file.replace(/\\/g, '/')] || file; + if (dest === 'tsconfig.json') { + return path.join(OUT_FOLDER, "../tsconfig.json"); } - in_queue[module] = true; - queue.push(module); + if (/\.ts$/.test(dest)) { + return path.join(OUT_FOLDER, dest); + } + return path.join(OUT_RESOURCES_FOLDER, dest); }; - var seenDir = {}; - var createDirectoryRecursive = function (dir) { - if (seenDir[dir]) { - return; + var allFiles = walkDirRecursive(SRC_FOLDER); + for (var i = 0; i < allFiles.length; i++) { + var file = allFiles[i]; + if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { + continue; } - var lastSlash = dir.lastIndexOf('/'); - if (lastSlash === -1) { - lastSlash = dir.lastIndexOf('\\'); + if (file === 'tsconfig.json') { + var tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString()); + tsConfig.compilerOptions.moduleResolution = undefined; + tsConfig.compilerOptions.baseUrl = undefined; + tsConfig.compilerOptions.module = 'es6'; + tsConfig.compilerOptions.rootDir = 'src'; + tsConfig.compilerOptions.outDir = path.relative(path.dirname(OUT_FOLDER), OUT_RESOURCES_FOLDER); + write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); + continue; } - if (lastSlash !== -1) { - createDirectoryRecursive(dir.substring(0, lastSlash)); + if (/\.d\.ts$/.test(file) || /\.css$/.test(file) || /\.js$/.test(file)) { + // Transport the files directly + write(getDestAbsoluteFilePath(file), fs.readFileSync(path.join(SRC_FOLDER, file))); + continue; } - seenDir[dir] = true; - try { - fs.mkdirSync(dir); - } - catch (err) { } - }; - seenDir[REPO_ROOT] = true; - var toggleComments = function (fileContents) { - var lines = fileContents.split(/\r\n|\r|\n/); - var mode = 0; - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (mode === 0) { - if (/\/\/ ESM-comment-begin/.test(line)) { - mode = 1; - continue; + if (/\.ts$/.test(file)) { + // Transform the .ts file + var fileContents = fs.readFileSync(path.join(SRC_FOLDER, file)).toString(); + var info = ts.preProcessFile(fileContents); + for (var i_1 = info.importedFiles.length - 1; i_1 >= 0; i_1--) { + var importedFilename = info.importedFiles[i_1].fileName; + var pos = info.importedFiles[i_1].pos; + var end = info.importedFiles[i_1].end; + var importedFilepath = void 0; + if (/^vs\/css!/.test(importedFilename)) { + importedFilepath = importedFilename.substr('vs/css!'.length) + '.css'; } - if (/\/\/ ESM-uncomment-begin/.test(line)) { - mode = 2; - continue; + else { + importedFilepath = importedFilename; } - continue; + if (/(^\.\/)|(^\.\.\/)/.test(importedFilepath)) { + importedFilepath = path.join(path.dirname(file), importedFilepath); + } + var relativePath = void 0; + if (importedFilepath === path.dirname(file)) { + relativePath = '../' + path.basename(path.dirname(file)); + } + else if (importedFilepath === path.dirname(path.dirname(file))) { + relativePath = '../../' + path.basename(path.dirname(path.dirname(file))); + } + else { + relativePath = path.relative(path.dirname(file), importedFilepath); + } + if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) { + relativePath = './' + relativePath; + } + fileContents = (fileContents.substring(0, pos + 1) + + relativePath + + fileContents.substring(end + 1)); } - if (mode === 1) { - if (/\/\/ ESM-comment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = '// ' + line; - continue; + fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { + return "import * as " + m1 + " from " + m2 + ";"; + }); + write(getDestAbsoluteFilePath(file), fileContents); + continue; + } + console.log("UNKNOWN FILE: " + file); + } + function walkDirRecursive(dir) { + if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') { + dir += '/'; + } + var result = []; + _walkDirRecursive(dir, result, dir.length); + return result; + } + function _walkDirRecursive(dir, result, trimPos) { + var files = fs.readdirSync(dir); + for (var i = 0; i < files.length; i++) { + var file = path.join(dir, files[i]); + if (fs.statSync(file).isDirectory()) { + _walkDirRecursive(file, result, trimPos); } - if (mode === 2) { - if (/\/\/ ESM-uncomment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) { - return indent; - }); + else { + result.push(file.substr(trimPos)); } } - return lines.join('\n'); - }; - var write = function (filePath, contents) { - var absoluteFilePath; - if (/\.ts$/.test(filePath)) { - absoluteFilePath = path.join(OUT_FOLDER, filePath); - } - else { - absoluteFilePath = path.join(OUT_RESOURCES_FOLDER, filePath); - } - createDirectoryRecursive(path.dirname(absoluteFilePath)); - if (/(\.ts$)|(\.js$)/.test(filePath)) { + } + function write(absoluteFilePath, contents) { + if (/(\.ts$)|(\.js$)/.test(absoluteFilePath)) { contents = toggleComments(contents.toString()); } - fs.writeFileSync(absoluteFilePath, contents); - }; - options.entryPoints.forEach(function (entryPoint) { return enqueue(entryPoint); }); - while (queue.length > 0) { - var module_1 = queue.shift(); - if (transportCSS(module_1, enqueue, write)) { - continue; + writeFile(absoluteFilePath, contents); + function toggleComments(fileContents) { + var lines = fileContents.split(/\r\n|\r|\n/); + var mode = 0; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (mode === 0) { + if (/\/\/ ESM-comment-begin/.test(line)) { + mode = 1; + continue; + } + if (/\/\/ ESM-uncomment-begin/.test(line)) { + mode = 2; + continue; + } + continue; + } + if (mode === 1) { + if (/\/\/ ESM-comment-end/.test(line)) { + mode = 0; + continue; + } + lines[i] = '// ' + line; + continue; + } + if (mode === 2) { + if (/\/\/ ESM-uncomment-end/.test(line)) { + mode = 0; + continue; + } + lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) { + return indent; + }); + } + } + return lines.join('\n'); } - if (transportResource(options, module_1, enqueue, write)) { - continue; - } - if (transportDTS(options, module_1, enqueue, write)) { - continue; - } - var filename = void 0; - if (options.redirects[module_1]) { - filename = path.join(SRC_DIR, options.redirects[module_1] + '.ts'); - } - else { - filename = path.join(SRC_DIR, module_1 + '.ts'); - } - var fileContents = fs.readFileSync(filename).toString(); - var info = ts.preProcessFile(fileContents); - for (var i = info.importedFiles.length - 1; i >= 0; i--) { - var importedFilename = info.importedFiles[i].fileName; - var pos = info.importedFiles[i].pos; - var end = info.importedFiles[i].end; - 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(module_1), importedFilepath); - } - enqueue(importedFilepath); - var relativePath = void 0; - if (importedFilepath === path.dirname(module_1)) { - relativePath = '../' + path.basename(path.dirname(module_1)); - } - else if (importedFilepath === path.dirname(path.dirname(module_1))) { - relativePath = '../../' + path.basename(path.dirname(path.dirname(module_1))); - } - else { - relativePath = path.relative(path.dirname(module_1), importedFilepath); - } - if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) { - relativePath = './' + relativePath; - } - fileContents = (fileContents.substring(0, pos + 1) - + relativePath - + fileContents.substring(end + 1)); - } - fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { - return "import * as " + m1 + " from " + m2 + ";"; - }); - fileContents = fileContents.replace(/Thenable/g, 'PromiseLike'); - write(module_1 + '.ts', fileContents); } - var esm_opts = { - "compilerOptions": { - "outDir": path.relative(path.dirname(OUT_FOLDER), OUT_RESOURCES_FOLDER), - "rootDir": "src", - "module": "es6", - "target": "es5", - "experimentalDecorators": true, - "lib": [ - "dom", - "es5", - "es2015.collection", - "es2015.promise" - ], - "types": [] - } - }; - fs.writeFileSync(path.join(path.dirname(OUT_FOLDER), 'tsconfig.json'), JSON.stringify(esm_opts, null, '\t')); - var monacodts = fs.readFileSync(path.join(SRC_DIR, 'vs/monaco.d.ts')).toString(); - fs.writeFileSync(path.join(OUT_FOLDER, 'vs/monaco.d.ts'), monacodts); } -exports.createESMSourcesAndResources = createESMSourcesAndResources; +exports.createESMSourcesAndResources2 = createESMSourcesAndResources2; function transportCSS(module, enqueue, write) { if (!/\.css/.test(module)) { return false; @@ -323,27 +305,3 @@ function transportCSS(module, enqueue, write) { return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; } } -function transportResource(options, module, enqueue, write) { - if (!/\.svg/.test(module)) { - return false; - } - write(module, fs.readFileSync(path.join(SRC_DIR, module))); - return true; -} -function transportDTS(options, module, enqueue, write) { - if (options.redirects[module] && fs.existsSync(path.join(SRC_DIR, options.redirects[module] + '.ts'))) { - return false; - } - if (!fs.existsSync(path.join(SRC_DIR, module + '.d.ts'))) { - return false; - } - write(module + '.d.ts', fs.readFileSync(path.join(SRC_DIR, module + '.d.ts'))); - var filename; - if (options.redirects[module]) { - write(module + '.js', fs.readFileSync(path.join(SRC_DIR, options.redirects[module] + '.js'))); - } - else { - write(module + '.js', fs.readFileSync(path.join(SRC_DIR, module + '.js'))); - } - return true; -} diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 7931737fadb..621b4aea6d4 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -10,7 +10,6 @@ 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; } = {}; @@ -38,7 +37,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str writeFile(path.join(options.destRoot, fileName), result[fileName]); } } - let copied: { [fileName:string]: boolean; } = {}; + let copied: { [fileName: string]: boolean; } = {}; const copyFile = (fileName: string) => { if (copied[fileName]) { return; @@ -82,6 +81,8 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); tsConfig.compilerOptions.noUnusedLocals = false; + tsConfig.compilerOptions.preserveConstEnums = false; + tsConfig.compilerOptions.declaration = false; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ @@ -101,191 +102,174 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str ].forEach(copyFile); } -export interface IOptions { - entryPoints: string[]; +export interface IOptions2 { + srcFolder: string; outFolder: string; outResourcesFolder: string; - redirects: { [module: string]: string; }; + ignores: string[]; + renames: { [filename: string]: string; }; } -export function createESMSourcesAndResources(options: IOptions): void { +export function createESMSourcesAndResources2(options: IOptions2): void { + const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder); const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); - let in_queue: { [module: string]: boolean; } = Object.create(null); - let queue: string[] = []; - - const enqueue = (module: string) => { - if (in_queue[module]) { - return; + const getDestAbsoluteFilePath = (file: string): string => { + let dest = options.renames[file.replace(/\\/g, '/')] || file; + if (dest === 'tsconfig.json') { + return path.join(OUT_FOLDER, `../tsconfig.json`); } - in_queue[module] = true; - queue.push(module); + if (/\.ts$/.test(dest)) { + return path.join(OUT_FOLDER, dest); + } + return path.join(OUT_RESOURCES_FOLDER, dest); }; - const seenDir: { [key: string]: boolean; } = {}; - const createDirectoryRecursive = (dir: string) => { - if (seenDir[dir]) { - return; - } + const allFiles = walkDirRecursive(SRC_FOLDER); + for (let i = 0; i < allFiles.length; i++) { + const file = allFiles[i]; - let lastSlash = dir.lastIndexOf('/'); - if (lastSlash === -1) { - lastSlash = dir.lastIndexOf('\\'); - } - if (lastSlash !== -1) { - createDirectoryRecursive(dir.substring(0, lastSlash)); - } - seenDir[dir] = true; - try { fs.mkdirSync(dir); } catch (err) { } - }; - - seenDir[REPO_ROOT] = true; - - const toggleComments = (fileContents: string) => { - let lines = fileContents.split(/\r\n|\r|\n/); - let mode = 0; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - - if (mode === 0) { - if (/\/\/ ESM-comment-begin/.test(line)) { - mode = 1; - continue; - } - if (/\/\/ ESM-uncomment-begin/.test(line)) { - mode = 2; - continue; - } - continue; - } - - if (mode === 1) { - if (/\/\/ ESM-comment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = '// ' + line; - continue; - } - - if (mode === 2) { - if (/\/\/ ESM-uncomment-end/.test(line)) { - mode = 0; - continue; - } - lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) { - return indent; - }); - } - } - - return lines.join('\n'); - }; - - const write = (filePath: string, contents: string | Buffer) => { - let absoluteFilePath: string; - if (/\.ts$/.test(filePath)) { - absoluteFilePath = path.join(OUT_FOLDER, filePath); - } else { - absoluteFilePath = path.join(OUT_RESOURCES_FOLDER, filePath); - } - createDirectoryRecursive(path.dirname(absoluteFilePath)); - if (/(\.ts$)|(\.js$)/.test(filePath)) { - contents = toggleComments(contents.toString()); - } - fs.writeFileSync(absoluteFilePath, contents); - }; - - options.entryPoints.forEach((entryPoint) => enqueue(entryPoint)); - - while (queue.length > 0) { - const module = queue.shift(); - if (transportCSS(module, enqueue, write)) { - continue; - } - if (transportResource(options, module, enqueue, write)) { - continue; - } - if (transportDTS(options, module, enqueue, write)) { + if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { continue; } - let filename: string; - if (options.redirects[module]) { - filename = path.join(SRC_DIR, options.redirects[module] + '.ts'); - } else { - filename = path.join(SRC_DIR, module + '.ts'); - } - let fileContents = fs.readFileSync(filename).toString(); - - const info = ts.preProcessFile(fileContents); - - for (let i = info.importedFiles.length - 1; i >= 0; i--) { - const importedFilename = info.importedFiles[i].fileName; - const pos = info.importedFiles[i].pos; - const end = info.importedFiles[i].end; - - 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(module), importedFilepath); - } - - enqueue(importedFilepath); - - let relativePath: string; - if (importedFilepath === path.dirname(module)) { - relativePath = '../' + path.basename(path.dirname(module)); - } else if (importedFilepath === path.dirname(path.dirname(module))) { - relativePath = '../../' + path.basename(path.dirname(path.dirname(module))); - } else { - relativePath = path.relative(path.dirname(module), importedFilepath); - } - if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) { - relativePath = './' + relativePath; - } - fileContents = ( - fileContents.substring(0, pos + 1) - + relativePath - + fileContents.substring(end + 1) - ); + if (file === 'tsconfig.json') { + const tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString()); + tsConfig.compilerOptions.moduleResolution = undefined; + tsConfig.compilerOptions.baseUrl = undefined; + tsConfig.compilerOptions.module = 'es6'; + tsConfig.compilerOptions.rootDir = 'src'; + tsConfig.compilerOptions.outDir = path.relative(path.dirname(OUT_FOLDER), OUT_RESOURCES_FOLDER); + write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); + continue; } - fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { - return `import * as ${m1} from ${m2};`; - }); - fileContents = fileContents.replace(/Thenable/g, 'PromiseLike'); + if (/\.d\.ts$/.test(file) || /\.css$/.test(file) || /\.js$/.test(file)) { + // Transport the files directly + write(getDestAbsoluteFilePath(file), fs.readFileSync(path.join(SRC_FOLDER, file))); + continue; + } - write(module + '.ts', fileContents); + if (/\.ts$/.test(file)) { + // Transform the .ts file + let fileContents = fs.readFileSync(path.join(SRC_FOLDER, file)).toString(); + + const info = ts.preProcessFile(fileContents); + + for (let i = info.importedFiles.length - 1; i >= 0; i--) { + const importedFilename = info.importedFiles[i].fileName; + const pos = info.importedFiles[i].pos; + const end = info.importedFiles[i].end; + + 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(file), importedFilepath); + } + + let relativePath: string; + if (importedFilepath === path.dirname(file)) { + relativePath = '../' + path.basename(path.dirname(file)); + } else if (importedFilepath === path.dirname(path.dirname(file))) { + relativePath = '../../' + path.basename(path.dirname(path.dirname(file))); + } else { + relativePath = path.relative(path.dirname(file), importedFilepath); + } + if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) { + relativePath = './' + relativePath; + } + fileContents = ( + fileContents.substring(0, pos + 1) + + relativePath + + fileContents.substring(end + 1) + ); + } + + fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) { + return `import * as ${m1} from ${m2};`; + }); + + write(getDestAbsoluteFilePath(file), fileContents); + continue; + } + + console.log(`UNKNOWN FILE: ${file}`); } - const esm_opts = { - "compilerOptions": { - "outDir": path.relative(path.dirname(OUT_FOLDER), OUT_RESOURCES_FOLDER), - "rootDir": "src", - "module": "es6", - "target": "es5", - "experimentalDecorators": true, - "lib": [ - "dom", - "es5", - "es2015.collection", - "es2015.promise" - ], - "types": [ - ] + + function walkDirRecursive(dir: string): string[] { + if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') { + dir += '/'; } - }; - fs.writeFileSync(path.join(path.dirname(OUT_FOLDER), 'tsconfig.json'), JSON.stringify(esm_opts, null, '\t')); + let result: string[] = []; + _walkDirRecursive(dir, result, dir.length); + return result; + } - const monacodts = fs.readFileSync(path.join(SRC_DIR, 'vs/monaco.d.ts')).toString(); - fs.writeFileSync(path.join(OUT_FOLDER, 'vs/monaco.d.ts'), monacodts); + function _walkDirRecursive(dir: string, result: string[], trimPos: number): void { + const files = fs.readdirSync(dir); + for (let i = 0; i < files.length; i++) { + const file = path.join(dir, files[i]); + if (fs.statSync(file).isDirectory()) { + _walkDirRecursive(file, result, trimPos); + } else { + result.push(file.substr(trimPos)); + } + } + } + function write(absoluteFilePath: string, contents: string | Buffer): void { + if (/(\.ts$)|(\.js$)/.test(absoluteFilePath)) { + contents = toggleComments(contents.toString()); + } + writeFile(absoluteFilePath, contents); + + function toggleComments(fileContents: string): string { + let lines = fileContents.split(/\r\n|\r|\n/); + let mode = 0; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + if (mode === 0) { + if (/\/\/ ESM-comment-begin/.test(line)) { + mode = 1; + continue; + } + if (/\/\/ ESM-uncomment-begin/.test(line)) { + mode = 2; + continue; + } + continue; + } + + if (mode === 1) { + if (/\/\/ ESM-comment-end/.test(line)) { + mode = 0; + continue; + } + lines[i] = '// ' + line; + continue; + } + + if (mode === 2) { + if (/\/\/ ESM-uncomment-end/.test(line)) { + mode = 0; + continue; + } + lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) { + return indent; + }); + } + } + + return lines.join('\n'); + } + } } function transportCSS(module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { @@ -363,33 +347,3 @@ function transportCSS(module: string, enqueue: (module: string) => void, write: return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; } } - -function transportResource(options: IOptions, module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { - - if (!/\.svg/.test(module)) { - return false; - } - - write(module, fs.readFileSync(path.join(SRC_DIR, module))); - return true; -} - -function transportDTS(options: IOptions, module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { - - if (options.redirects[module] && fs.existsSync(path.join(SRC_DIR, options.redirects[module] + '.ts'))) { - return false; - } - - if (!fs.existsSync(path.join(SRC_DIR, module + '.d.ts'))) { - return false; - } - - write(module + '.d.ts', fs.readFileSync(path.join(SRC_DIR, module + '.d.ts'))); - let filename: string; - if (options.redirects[module]) { - write(module + '.js', fs.readFileSync(path.join(SRC_DIR, options.redirects[module] + '.js'))); - } else { - write(module + '.js', fs.readFileSync(path.join(SRC_DIR, module + '.js'))); - } - return true; -} diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js index e1138b07f90..94f73cdd4a0 100644 --- a/build/lib/watch/index.js +++ b/build/lib/watch/index.js @@ -19,16 +19,6 @@ function handleDeletions() { let watch = void 0; -// Disabled due to https://github.com/Microsoft/vscode/issues/36214 -// if (!process.env['VSCODE_USE_LEGACY_WATCH']) { -// try { -// watch = require('./watch-nsfw'); -// } catch (err) { -// console.warn('Could not load our cross platform file watcher: ' + err.toString()); -// console.warn('Falling back to our platform specific watcher...'); -// } -// } - if (!watch) { watch = process.platform === 'win32' ? require('./watch-win32') : require('gulp-watch'); } diff --git a/build/lib/watch/package.json b/build/lib/watch/package.json index b10e8ed2727..0d031340153 100644 --- a/build/lib/watch/package.json +++ b/build/lib/watch/package.json @@ -5,7 +5,6 @@ "author": "Microsoft ", "private": true, "devDependencies": { - "gulp-watch": "^4.3.9", - "nsfw": "^1.0.15" + "gulp-watch": "^4.3.9" } } diff --git a/build/lib/watch/watch-nsfw.js b/build/lib/watch/watch-nsfw.js deleted file mode 100644 index fb2b2758d02..00000000000 --- a/build/lib/watch/watch-nsfw.js +++ /dev/null @@ -1,94 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -var nsfw = require('nsfw'); -var path = require('path'); -var fs = require('fs'); -var File = require('vinyl'); -var es = require('event-stream'); -var filter = require('gulp-filter'); - -function toChangeType(type) { - switch (type) { - case 0: return 'add'; - case 1: return 'unlink'; - case 2: return 'change'; - } -} - -function watch(root) { - var result = es.through(); - - function handleEvent(path, type) { - if (/[/\\].git[/\\]/.test(path) || /[/\\]out[/\\]/.test(path)) { - return; // filter as early as possible - } - - var file = new File({ - path: path, - base: root - }); - //@ts-ignore - file.event = type; - result.emit('data', file); - } - - nsfw(root, function (events) { - for (var i = 0; i < events.length; i++) { - var e = events[i]; - var changeType = e.action; - - if (changeType === 3 /* RENAMED */) { - handleEvent(path.join(e.directory, e.oldFile), 'unlink'); - handleEvent(path.join(e.directory, e.newFile), 'add'); - } else { - handleEvent(path.join(e.directory, e.file), toChangeType(changeType)); - } - } - }).then(function (watcher) { - watcher.start(); - }); - - return result; -} - -var cache = Object.create(null); - -module.exports = function (pattern, options) { - options = options || {}; - - var cwd = path.normalize(options.cwd || process.cwd()); - var watcher = cache[cwd]; - - if (!watcher) { - watcher = cache[cwd] = watch(cwd); - } - - var rebase = !options.base ? es.through() : es.mapSync(function (f) { - f.base = options.base; - return f; - }); - - return watcher - .pipe(filter(['**', '!.git{,/**}'])) // ignore all things git - .pipe(filter(pattern)) - .pipe(es.map(function (file, cb) { - fs.stat(file.path, function (err, stat) { - if (err && err.code === 'ENOENT') { return cb(null, file); } - if (err) { return cb(); } - if (!stat.isFile()) { return cb(); } - - fs.readFile(file.path, function (err, contents) { - if (err && err.code === 'ENOENT') { return cb(null, file); } - if (err) { return cb(); } - - file.contents = contents; - file.stat = stat; - cb(null, file); - }); - }); - })) - .pipe(rebase); -}; \ No newline at end of file diff --git a/build/lib/watch/yarn.lock b/build/lib/watch/yarn.lock index 0b4d3f70bb7..4fb4b56c0f6 100644 --- a/build/lib/watch/yarn.lock +++ b/build/lib/watch/yarn.lock @@ -61,10 +61,6 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -330,16 +326,6 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -fs-extra@^0.26.5: - version "0.26.7" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - path-is-absolute "^1.0.0" - rimraf "^2.2.8" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -424,7 +410,7 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -649,12 +635,6 @@ 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" -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - optionalDependencies: - graceful-fs "^4.1.6" - jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -680,12 +660,6 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - optionalDependencies: - graceful-fs "^4.1.9" - lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -736,14 +710,6 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - -lodash.isundefined@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" - lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -835,7 +801,7 @@ multipipe@^0.1.2: dependencies: duplexer2 "0.0.2" -nan@^2.0.0, nan@^2.3.0: +nan@^2.3.0: version "2.7.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" @@ -855,12 +821,6 @@ node-pre-gyp@^0.6.39: tar "^2.2.1" tar-pack "^3.4.0" -nodegit-promise@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/nodegit-promise/-/nodegit-promise-4.0.0.tgz#5722b184f2df7327161064a791d2e842c9167b34" - dependencies: - asap "~2.0.3" - nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -883,16 +843,6 @@ npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" -nsfw@^1.0.15: - version "1.0.16" - resolved "https://registry.yarnpkg.com/nsfw/-/nsfw-1.0.16.tgz#78ba3e7f513b53d160c221b9018e0baf108614cc" - dependencies: - fs-extra "^0.26.5" - lodash.isinteger "^4.0.4" - lodash.isundefined "^3.0.1" - nan "^2.0.0" - promisify-node "^0.3.0" - number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -980,12 +930,6 @@ process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" -promisify-node@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/promisify-node/-/promisify-node-0.3.0.tgz#b4b55acf90faa7d2b8b90ca396899086c03060cf" - dependencies: - nodegit-promise "~4.0.0" - punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -1089,7 +1033,7 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: +rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 218d4cdf8f5..bebfe723484 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -25,13 +25,6 @@ declare namespace monaco { dispose(): void; } - export enum Severity { - Ignore = 0, - Info = 1, - Warning = 2, - Error = 3, - } - export enum MarkerTag { Unnecessary = 1, } @@ -44,7 +37,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 index 05377a19ba0..beaad500aad 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -71,7 +71,6 @@ import * as editorAPI from 'vs/editor/editor.api'; a = editorAPI.Range; a = editorAPI.Selection; a = editorAPI.SelectionDirection; - a = editorAPI.Severity; a = editorAPI.MarkerSeverity; a = editorAPI.MarkerTag; a = editorAPI.Promise; diff --git a/build/monaco/package.json b/build/monaco/package.json index 256ca1ff534..efd919085b2 100644 --- a/build/monaco/package.json +++ b/build/monaco/package.json @@ -1,7 +1,7 @@ { "name": "monaco-editor-core", "private": true, - "version": "0.12.0", + "version": "0.14.3", "description": "A browser based code editor", "author": "Microsoft Corporation", "license": "MIT", diff --git a/build/tfs/darwin/continuous-build-darwin.yml b/build/tfs/darwin/continuous-build-darwin.yml index 20070e08745..9d3f6254cd6 100644 --- a/build/tfs/darwin/continuous-build-darwin.yml +++ b/build/tfs/darwin/continuous-build-darwin.yml @@ -4,7 +4,7 @@ steps: versionSpec: "8.9.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: - versionSpec: "1.3.2" + versionSpec: "1.9.4" - script: | yarn displayName: Install Dependencies @@ -15,7 +15,7 @@ steps: yarn gulp hygiene displayName: Run Hygiene Checks - script: | - yarn check-monaco-editor-compilation + yarn monaco-compile-check displayName: Run Monaco Editor Checks - script: | yarn compile diff --git a/build/tfs/darwin/product-build-darwin.yml b/build/tfs/darwin/product-build-darwin.yml index 0dd31503586..c823012c1df 100644 --- a/build/tfs/darwin/product-build-darwin.yml +++ b/build/tfs/darwin/product-build-darwin.yml @@ -5,7 +5,7 @@ steps: - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: - versionSpec: "1.3.2" + versionSpec: "1.9.4" - script: | set -e diff --git a/build/tfs/linux/continuous-build-linux.yml b/build/tfs/linux/continuous-build-linux.yml index 7ec3aec74b9..9c2e125447b 100644 --- a/build/tfs/linux/continuous-build-linux.yml +++ b/build/tfs/linux/continuous-build-linux.yml @@ -14,7 +14,7 @@ steps: versionSpec: "8.9.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: - versionSpec: "1.3.2" + versionSpec: "1.9.4" - script: | yarn displayName: Install Dependencies @@ -25,7 +25,7 @@ steps: yarn gulp hygiene displayName: Run Hygiene Checks - script: | - yarn check-monaco-editor-compilation + yarn monaco-compile-check displayName: Run Monaco Editor Checks - script: | yarn compile diff --git a/build/tfs/linux/product-build-linux.yml b/build/tfs/linux/product-build-linux.yml index f6e067ebfe3..f38980542e8 100644 --- a/build/tfs/linux/product-build-linux.yml +++ b/build/tfs/linux/product-build-linux.yml @@ -5,7 +5,7 @@ steps: - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: - versionSpec: "1.3.2" + versionSpec: "1.9.4" - script: | set -e @@ -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/tfs/win32/continuous-build-win32.yml b/build/tfs/win32/continuous-build-win32.yml index 6490c637b78..780c9a0e197 100644 --- a/build/tfs/win32/continuous-build-win32.yml +++ b/build/tfs/win32/continuous-build-win32.yml @@ -4,51 +4,33 @@ steps: versionSpec: "8.9.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: - versionSpec: "1.3.2" + versionSpec: "1.9.4" - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn } + yarn displayName: Install Dependencies - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn gulp electron } + yarn gulp electron displayName: Download Electron - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn gulp hygiene } + yarn gulp hygiene displayName: Run Hygiene Checks - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn check-monaco-editor-compilation } + yarn monaco-compile-check displayName: Run Monaco Editor Checks - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn compile } + yarn compile displayName: Compile Sources - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn download-builtin-extensions } + yarn download-builtin-extensions displayName: Download Built-in Extensions - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { .\scripts\test.bat --tfs "Unit Tests" } + .\scripts\test.bat --tfs "Unit Tests" displayName: Run Unit Tests - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { .\scripts\test-integration.bat --tfs "Integration Tests" } + .\scripts\test-integration.bat --tfs "Integration Tests" displayName: Run Integration Tests - powershell: | - . build/tfs/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" } + yarn smoketest --screenshots "$(Build.ArtifactStagingDirectory)\artifacts" --log "$(Build.ArtifactStagingDirectory)\artifacts\smoketest.log" displayName: Run Smoke Tests continueOnError: true - task: PublishBuildArtifacts@1 diff --git a/build/tfs/win32/product-build-win32.yml b/build/tfs/win32/product-build-win32.yml index c887ade31fd..31288bdbdda 100644 --- a/build/tfs/win32/product-build-win32.yml +++ b/build/tfs/win32/product-build-win32.yml @@ -5,7 +5,7 @@ steps: - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: - versionSpec: "1.3.2" + versionSpec: "1.9.4" - powershell: | . build/tfs/win32/exec.ps1 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/configuration-editing/package.json b/extensions/configuration-editing/package.json index f63d8c84087..125f2456f54 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -96,6 +96,6 @@ ] }, "devDependencies": { - "@types/node": "7.0.4" + "@types/node": "^8.10.25" } -} \ No newline at end of file +} diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts index 68dbbf165b4..f6ec0b625e7 100644 --- a/extensions/configuration-editing/src/extension.ts +++ b/extensions/configuration-editing/src/extension.ts @@ -11,8 +11,8 @@ import { getLocation, visit, parse, ParseErrorCode } from 'jsonc-parser'; import * as path from 'path'; import { SettingsDocument } from './settingsDocumentHelper'; -const decoration = vscode.window.createTextEditorDecorationType({ - color: '#9e9e9e' +const fadedDecoration = vscode.window.createTextEditorDecorationType({ + color: '#777' }); let pendingLaunchJsonDecoration: NodeJS.Timer; @@ -241,7 +241,7 @@ function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): voi } }); - editor.setDecorations(decoration, ranges); + editor.setDecorations(fadedDecoration, ranges); } vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, { diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index bcf4ddb12d8..c3f9631aeb0 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.4.tgz#9aabc135979ded383325749f508894c662948c8b" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" jsonc-parser@^1.0.0: version "1.0.0" 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/.vscodeignore b/extensions/css-language-features/.vscodeignore index b85541d78ee..debeb96e691 100644 --- a/extensions/css-language-features/.vscodeignore +++ b/extensions/css-language-features/.vscodeignore @@ -1,5 +1,11 @@ +test/** +.vscode/** client/src/** client/tsconfig.json server/src/** +server/test/** +server/out/test/** +server/.vscode/** server/tsconfig.json -server/node_modules/@types/** \ No newline at end of file +**/node_modules/@types/** +**/node_modules/*/lib/esm/** \ No newline at end of file diff --git a/extensions/css-language-features/CONTRIBUTING.md b/extensions/css-language-features/CONTRIBUTING.md index be3c614d9b7..38843f2fbaa 100644 --- a/extensions/css-language-features/CONTRIBUTING.md +++ b/extensions/css-language-features/CONTRIBUTING.md @@ -27,12 +27,13 @@ However, within this extension, you can run a development version of `vscode-css - Clone [Microsoft/vscode-css-languageservice](https://github.com/Microsoft/vscode-css-languageservice) - Run `yarn` in `vscode-css-languageservice` - Run `yarn link` in `vscode-css-languageservice`. This will compile and link `vscode-css-languageservice` -- In `css-language-features/server/`, run `npm link vscode-css-languageservice` +- In `css-language-features/server/`, run `yarn link vscode-css-languageservice` #### Testing the development version of `vscode-css-languageservice` - Open both `vscode-css-languageservice` and this extension in a single workspace with [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) feature -- Run `yarn watch` at `css-languagefeatures/server/` to recompile this extension with the linked version of `vscode-css-languageservice` +- Run `yarn watch` in `vscode-css-languageservice` to recompile the extension whenever it changes +- Run `yarn watch` at `css-language-features/server/` to recompile this extension with the linked version of `vscode-css-languageservice` - Make some changes in `vscode-css-languageservice` - Now when you run `Launch Extension` debug target, the launched instance will use your development version of `vscode-css-languageservice`. You can interactively test the language features. -- You can also run the `Debug Extension and Language Server` debug target, which will launch the extension and attach the debugger to the language server. After successful attach, you should be able to hit breakpoints in both `vscode-css-languageservice` and `css-language-features/server/` \ No newline at end of file +- You can also run the `Debug Extension and Language Server` debug target, which will launch the extension and attach the debugger to the language server. After successful attach, you should be able to hit breakpoints in both `vscode-css-languageservice` and `css-language-features/server/` 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..cf0903f0a81 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", + "@types/node": "^8.10.25", "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..6e85706a2f1 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -8,13 +8,12 @@ "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.3", + "vscode-languageserver": "^4.4.0" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "7.0.43", + "@types/node": "^8.10.25", "glob": "^7.1.2", "mocha": "^5.2.0", "mocha-junit-reporter": "^1.17.0", diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index 8778faf2b79..853f6690473 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -7,15 +7,14 @@ import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder } from 'vscode-languageserver'; - +import URI from 'vscode-uri'; import { TextDocument, CompletionList } from 'vscode-languageserver-types'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; 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'; +import { formatError, runSafe } from './utils/runner'; +import { getDocumentContext } from './utils/documentContext'; 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, @@ -87,6 +86,9 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { referencesProvider: true, definitionProvider: true, documentHighlightProvider: true, + documentLinkProvider: { + resolveProvider: false + }, codeActionProvider: true, renameProvider: true, colorProvider: {}, @@ -228,29 +230,43 @@ connection.onDocumentSymbol((documentSymbolParams, token) => { }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); }); -connection.onDefinition((documentSymbolParams, token) => { +connection.onDefinition((documentDefinitionParams, token) => { return runSafe(() => { - const document = documents.get(documentSymbolParams.textDocument.uri); + const document = documents.get(documentDefinitionParams.textDocument.uri); if (document) { const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDefinition(document, documentSymbolParams.position, stylesheet); + return getLanguageService(document).findDefinition(document, documentDefinitionParams.position, stylesheet); } return null; - }, null, `Error while computing definitions for ${documentSymbolParams.textDocument.uri}`, token); + }, null, `Error while computing definitions for ${documentDefinitionParams.textDocument.uri}`, token); }); -connection.onDocumentHighlight((documentSymbolParams, token) => { +connection.onDocumentHighlight((documentHighlightParams, token) => { return runSafe(() => { - const document = documents.get(documentSymbolParams.textDocument.uri); + const document = documents.get(documentHighlightParams.textDocument.uri); if (document) { const stylesheet = stylesheets.get(document); - return getLanguageService(document).findDocumentHighlights(document, documentSymbolParams.position, stylesheet); + return getLanguageService(document).findDocumentHighlights(document, documentHighlightParams.position, stylesheet); } return []; - }, [], `Error while computing document highlights for ${documentSymbolParams.textDocument.uri}`, token); + }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); }); + +connection.onDocumentLinks((documentLinkParams, token) => { + return runSafe(() => { + const document = documents.get(documentLinkParams.textDocument.uri); + if (document) { + const documentContext = getDocumentContext(document.uri, workspaceFolders); + const stylesheet = stylesheets.get(document); + return getLanguageService(document).findDocumentLinks(document, stylesheet, documentContext); + } + return []; + }, [], `Error while computing document links for ${documentLinkParams.textDocument.uri}`, token); +}); + + connection.onReferences((referenceParams, token) => { return runSafe(() => { const document = documents.get(referenceParams.textDocument.uri); @@ -306,7 +322,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/documentContext.ts b/extensions/css-language-features/server/src/utils/documentContext.ts new file mode 100644 index 00000000000..b37993b7655 --- /dev/null +++ b/extensions/css-language-features/server/src/utils/documentContext.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { DocumentContext } from 'vscode-css-languageservice'; +import { endsWith, startsWith } from '../utils/strings'; +import * as url from 'url'; +import { WorkspaceFolder } from 'vscode-languageserver'; + +export function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext { + function getRootFolder(): string | undefined { + for (let folder of workspaceFolders) { + let folderURI = folder.uri; + if (!endsWith(folderURI, '/')) { + folderURI = folderURI + '/'; + } + if (startsWith(documentUri, folderURI)) { + return folderURI; + } + } + return void 0; + } + + return { + resolveReference: (ref, base = documentUri) => { + if (ref[0] === '/') { // resolve absolute path against the current workspace folder + if (startsWith(base, 'file://')) { + let folderUri = getRootFolder(); + if (folderUri) { + return folderUri + ref.substr(1); + } + } + } + return url.resolve(base, ref); + }, + }; +} + 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..c2ae2ced51a 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -6,9 +6,9 @@ version "2.2.33" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" ansi-regex@^3.0.0: version "3.0.0" @@ -194,49 +194,46 @@ 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.3: + version "3.0.10-next.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.10-next.3.tgz#d663f03b8a9bf11222bd2cd39e9aa05e1a58c58d" dependencies: - vscode-languageserver-types "^3.7.2" - vscode-nls "^3.2.2" + vscode-languageserver-types "^3.10.1" + 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-types@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.1.tgz#d5d5f44f688a3b2aa9857dc53cb9cacca73fe35a" + +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..a51de5f43ac 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" balanced-match@^1.0.0: version "1.0.0" @@ -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/extension.ts b/extensions/debug-auto-launch/src/extension.ts index e10846e111e..3f101d071bf 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -7,77 +7,78 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import { basename } from 'path'; -import { pollProcesses, attachToProcess } from './nodeProcessTree'; const localize = nls.loadMessageBundle(); const ON_TEXT = localize('status.text.auto.attach.on', "Auto Attach: On"); const OFF_TEXT = localize('status.text.auto.attach.off', "Auto Attach: Off"); const TOGGLE_COMMAND = 'extension.node-debug.toggleAutoAttach'; +const DEBUG_SETTINGS = 'debug.node'; +const AUTO_ATTACH_SETTING = 'autoAttach'; -let currentState: string; -let autoAttacher: vscode.Disposable | undefined; -let statusItem: vscode.StatusBarItem | undefined = undefined; +type AUTO_ATTACH_VALUES = 'disabled' | 'on' | 'off'; +let currentState: AUTO_ATTACH_VALUES = 'disabled'; // on activation this feature is always disabled and +let statusItem: vscode.StatusBarItem | undefined; // there is no status bar item +let autoAttachStarted = false; export function activate(context: vscode.ExtensionContext): void { - context.subscriptions.push(vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttach)); + context.subscriptions.push(vscode.commands.registerCommand(TOGGLE_COMMAND, toggleAutoAttachSetting)); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('debug.node.autoAttach')) { - updateAutoAttachInStatus(context); + if (e.affectsConfiguration(DEBUG_SETTINGS + '.' + AUTO_ATTACH_SETTING)) { + updateAutoAttach(context); } })); - updateAutoAttachInStatus(context); + updateAutoAttach(context); } export function deactivate(): void { } -function toggleAutoAttach(context: vscode.ExtensionContext) { +function toggleAutoAttachSetting(context: vscode.ExtensionContext) { - const conf = vscode.workspace.getConfiguration('debug.node'); + const conf = vscode.workspace.getConfiguration(DEBUG_SETTINGS); + if (conf) { + let value = conf.get(AUTO_ATTACH_SETTING); + if (value === 'on') { + value = 'off'; + } else { + value = 'on'; + } - let value = conf.get('autoAttach'); - if (value === 'on') { - value = 'off'; - } else { - value = 'on'; - } - - const info = conf.inspect('autoAttach'); - let target: vscode.ConfigurationTarget = vscode.ConfigurationTarget.Global; - if (info) { - if (info.workspaceFolderValue) { - target = vscode.ConfigurationTarget.WorkspaceFolder; - } else if (info.workspaceValue) { - target = vscode.ConfigurationTarget.Workspace; - } else if (info.globalValue) { - target = vscode.ConfigurationTarget.Global; - } else if (info.defaultValue) { - // setting not yet used: store setting in workspace - if (vscode.workspace.workspaceFolders) { + const info = conf.inspect(AUTO_ATTACH_SETTING); + let target: vscode.ConfigurationTarget = vscode.ConfigurationTarget.Global; + if (info) { + if (info.workspaceFolderValue) { + target = vscode.ConfigurationTarget.WorkspaceFolder; + } else if (info.workspaceValue) { target = vscode.ConfigurationTarget.Workspace; + } else if (info.globalValue) { + target = vscode.ConfigurationTarget.Global; + } else if (info.defaultValue) { + // setting not yet used: store setting in workspace + if (vscode.workspace.workspaceFolders) { + target = vscode.ConfigurationTarget.Workspace; + } } } + conf.update(AUTO_ATTACH_SETTING, value, target); } - conf.update('autoAttach', value, target); - - updateAutoAttachInStatus(context); } -function updateAutoAttachInStatus(context: vscode.ExtensionContext) { +/** + * Updates the auto attach feature based on the user or workspace setting + */ +function updateAutoAttach(context: vscode.ExtensionContext) { - const newState = vscode.workspace.getConfiguration('debug.node').get('autoAttach'); + const newState = vscode.workspace.getConfiguration(DEBUG_SETTINGS).get(AUTO_ATTACH_SETTING); if (newState !== currentState) { - currentState = newState; - if (newState === 'disabled') { // turn everything off @@ -85,9 +86,11 @@ function updateAutoAttachInStatus(context: vscode.ExtensionContext) { statusItem.hide(); statusItem.text = OFF_TEXT; } - if (autoAttacher) { - autoAttacher.dispose(); - autoAttacher = undefined; + if (autoAttachStarted) { + vscode.commands.executeCommand('extension.node-debug.stopAutoAttach').then(_ => { + currentState = newState; + autoAttachStarted = false; + }); } } else { // 'on' or 'off' @@ -96,7 +99,6 @@ function updateAutoAttachInStatus(context: vscode.ExtensionContext) { if (!statusItem) { statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); statusItem.command = TOGGLE_COMMAND; - statusItem.text = OFF_TEXT; statusItem.tooltip = localize('status.tooltip.auto.attach', "Automatically attach to node.js processes in debug mode"); statusItem.show(); context.subscriptions.push(statusItem); @@ -105,28 +107,28 @@ function updateAutoAttachInStatus(context: vscode.ExtensionContext) { } if (newState === 'off') { - statusItem.text = OFF_TEXT; - if (autoAttacher) { - autoAttacher.dispose(); - autoAttacher = undefined; + if (autoAttachStarted) { + vscode.commands.executeCommand('extension.node-debug.stopAutoAttach').then(_ => { + currentState = newState; + if (statusItem) { + statusItem.text = OFF_TEXT; + } + autoAttachStarted = false; + }); } + } else if (newState === 'on') { - statusItem.text = ON_TEXT; + const vscode_pid = process.env['VSCODE_PID']; const rootPid = vscode_pid ? parseInt(vscode_pid) : 0; - autoAttacher = startAutoAttach(rootPid); + vscode.commands.executeCommand('extension.node-debug.startAutoAttach', rootPid).then(_ => { + if (statusItem) { + statusItem.text = ON_TEXT; + } + currentState = newState; + autoAttachStarted = true; + }); } } } } - -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 deleted file mode 100644 index 16bc55476e1..00000000000 --- a/extensions/debug-auto-launch/src/nodeProcessTree.ts +++ /dev/null @@ -1,139 +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 { getProcessTree, ProcessTreeNode } from './processTree'; -import { analyseArguments } from './protocolDetection'; - -const pids = new Set(); - -const POLL_INTERVAL = 1000; - -/** - * Poll for all subprocesses of given root process. - */ -export function pollProcesses(rootPid: number, inTerminal: boolean, cb: (pid: number, cmd: string, args: string) => void): vscode.Disposable { - - let stopped = false; - - function poll() { - //const start = Date.now(); - findChildProcesses(rootPid, inTerminal, cb).then(_ => { - //console.log(`duration: ${Date.now() - start}`); - setTimeout(_ => { - if (!stopped) { - poll(); - } - }, POLL_INTERVAL); - }); - } - - poll(); - - return new vscode.Disposable(() => stopped = true); -} - -export function attachToProcess(folder: vscode.WorkspaceFolder | undefined, name: string, pid: number, args: string, baseConfig?: vscode.DebugConfiguration) { - - if (pids.has(pid)) { - return; - } - pids.add(pid); - - const config: vscode.DebugConfiguration = { - type: 'node', - request: 'attach', - name: name, - stopOnEntry: false - }; - - if (baseConfig) { - // selectively copy attributes - if (baseConfig.timeout) { - config.timeout = baseConfig.timeout; - } - if (baseConfig.sourceMaps) { - config.sourceMaps = baseConfig.sourceMaps; - } - if (baseConfig.outFiles) { - config.outFiles = baseConfig.outFiles; - } - if (baseConfig.sourceMapPathOverrides) { - config.sourceMapPathOverrides = baseConfig.sourceMapPathOverrides; - } - if (baseConfig.smartStep) { - config.smartStep = baseConfig.smartStep; - } - if (baseConfig.skipFiles) { - config.skipFiles = baseConfig.skipFiles; - } - if (baseConfig.showAsyncStacks) { - config.sourceMaps = baseConfig.showAsyncStacks; - } - if (baseConfig.trace) { - config.trace = baseConfig.trace; - } - } - - let { usePort, protocol, port } = analyseArguments(args); - if (usePort) { - config.processId = `${protocol}${port}`; - } else { - if (protocol && port > 0) { - config.processId = `${pid}${protocol}${port}`; - } else { - config.processId = pid.toString(); - } - } - - vscode.debug.startDebugging(folder, config); -} - -function findChildProcesses(rootPid: number, inTerminal: boolean, cb: (pid: number, cmd: string, args: string) => void): Promise { - - function walker(node: ProcessTreeNode, terminal: boolean, renderer: number) { - - if (node.args.indexOf('--type=terminal') >= 0 && (renderer === 0 || node.ppid === renderer)) { - terminal = true; - } - - let { protocol } = analyseArguments(node.args); - if (terminal && protocol) { - cb(node.pid, node.command, node.args); - } - - for (const child of node.children || []) { - walker(child, terminal, renderer); - } - } - - 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); - } - } - }); -} diff --git a/extensions/debug-auto-launch/src/processTree.ts b/extensions/debug-auto-launch/src/processTree.ts deleted file mode 100644 index f072d9cf58c..00000000000 --- a/extensions/debug-auto-launch/src/processTree.ts +++ /dev/null @@ -1,186 +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 { spawn, ChildProcess } from 'child_process'; -import { join } from 'path'; - -export class ProcessTreeNode { - children?: ProcessTreeNode[]; - - constructor(public pid: number, public ppid: number, public command: string, public args: string) { - } -} - -export async function getProcessTree(rootPid: number): Promise { - - const map = new Map(); - - map.set(0, new ProcessTreeNode(0, 0, '???', '')); - - try { - await getProcesses((pid: number, ppid: number, command: string, args: string) => { - if (pid !== ppid) { - map.set(pid, new ProcessTreeNode(pid, ppid, command, args)); - } - }); - } catch (err) { - return undefined; - } - - const values = map.values(); - for (const p of values) { - const parent = map.get(p.ppid); - if (parent && parent !== p) { - if (!parent.children) { - parent.children = []; - } - parent.children.push(p); - } - } - - if (!isNaN(rootPid) && rootPid > 0) { - return map.get(rootPid); - } - return map.get(0); -} - -export function getProcesses(one: (pid: number, ppid: number, command: string, args: string, date?: number) => void): Promise { - - // returns a function that aggregates chunks of data until one or more complete lines are received and passes them to a callback. - function lines(callback: (a: string) => void) { - let unfinished = ''; // unfinished last line of chunk - return (data: string | Buffer) => { - const lines = data.toString().split(/\r?\n/); - const finishedLines = lines.slice(0, lines.length - 1); - finishedLines[0] = unfinished + finishedLines[0]; // complete previous unfinished line - unfinished = lines[lines.length - 1]; // remember unfinished last line of this chunk for next round - for (const s of finishedLines) { - callback(s); - } - }; - } - - return new Promise((resolve, reject) => { - - let proc: ChildProcess; - - if (process.platform === 'win32') { - - // attributes columns are in alphabetic order! - const CMD_PAT = /^(.*)\s+([0-9]+)\.[0-9]+[+-][0-9]+\s+([0-9]+)\s+([0-9]+)$/; - - const wmic = join(process.env['WINDIR'] || 'C:\\Windows', 'System32', 'wbem', 'WMIC.exe'); - proc = spawn(wmic, ['process', 'get', 'CommandLine,CreationDate,ParentProcessId,ProcessId']); - proc.stdout.setEncoding('utf8'); - proc.stdout.on('data', lines(line => { - let matches = CMD_PAT.exec(line.trim()); - if (matches && matches.length === 5) { - const pid = Number(matches[4]); - const ppid = Number(matches[3]); - const date = Number(matches[2]); - let args = matches[1].trim(); - if (!isNaN(pid) && !isNaN(ppid) && args) { - let command = args; - if (args[0] === '"') { - const end = args.indexOf('"', 1); - if (end > 0) { - command = args.substr(1, end - 1); - args = args.substr(end + 2); - } - } else { - const end = args.indexOf(' '); - if (end > 0) { - command = args.substr(0, end); - args = args.substr(end + 1); - } else { - args = ''; - } - } - one(pid, ppid, command, args, date); - } - } - })); - - } else if (process.platform === 'darwin') { // OS X - - proc = spawn('/bin/ps', ['-x', '-o', `pid,ppid,comm=${'a'.repeat(256)},command`]); - proc.stdout.setEncoding('utf8'); - proc.stdout.on('data', lines(line => { - - const pid = Number(line.substr(0, 5)); - const ppid = Number(line.substr(6, 5)); - const command = line.substr(12, 256).trim(); - const args = line.substr(269 + command.length); - - if (!isNaN(pid) && !isNaN(ppid)) { - one(pid, ppid, command, args); - } - })); - - } else { // linux - - proc = spawn('/bin/ps', ['-ax', '-o', 'pid,ppid,comm:20,command']); - proc.stdout.setEncoding('utf8'); - proc.stdout.on('data', lines(line => { - - const pid = Number(line.substr(0, 5)); - const ppid = Number(line.substr(6, 5)); - let command = line.substr(12, 20).trim(); - let args = line.substr(33); - - let pos = args.indexOf(command); - if (pos >= 0) { - pos = pos + command.length; - while (pos < args.length) { - if (args[pos] === ' ') { - break; - } - pos++; - } - command = args.substr(0, pos); - args = args.substr(pos + 1); - } - - if (!isNaN(pid) && !isNaN(ppid)) { - one(pid, ppid, command, args); - } - })); - } - - proc.on('error', err => { - reject(err); - }); - - proc.stderr.setEncoding('utf8'); - proc.stderr.on('data', data => { - reject(new Error(data.toString())); - }); - - proc.on('close', (code, signal) => { - if (code === 0) { - resolve(); - } else if (code > 0) { - reject(new Error(`process terminated with exit code: ${code}`)); - } - if (signal) { - reject(new Error(`process terminated with signal: ${signal}`)); - } - }); - - proc.on('exit', (code, signal) => { - if (code === 0) { - //resolve(); - } else if (code > 0) { - reject(new Error(`process terminated with exit code: ${code}`)); - } - if (signal) { - reject(new Error(`process terminated with signal: ${signal}`)); - } - }); - }); -} - diff --git a/extensions/debug-auto-launch/src/protocolDetection.ts b/extensions/debug-auto-launch/src/protocolDetection.ts deleted file mode 100644 index 9ccca40e849..00000000000 --- a/extensions/debug-auto-launch/src/protocolDetection.ts +++ /dev/null @@ -1,58 +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'; - -export const INSPECTOR_PORT_DEFAULT = 9229; -export const LEGACY_PORT_DEFAULT = 5858; - -export interface DebugArguments { - usePort: boolean; // if true debug by using the debug port - protocol?: 'legacy' | 'inspector'; - address?: string; - port: number; -} - -/* - * analyse the given command line arguments and extract debug port and protocol from it. - */ -export function analyseArguments(args: string): DebugArguments { - - const DEBUG_FLAGS_PATTERN = /--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/; - const DEBUG_PORT_PATTERN = /--(inspect|debug)-port=(\d+)/; - - const result: DebugArguments = { - usePort: false, - port: -1 - }; - - // match --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, --inspect=1234, --inspect-brk, --inspect-brk=1234 - let matches = DEBUG_FLAGS_PATTERN.exec(args); - if (matches && matches.length >= 2) { - // attach via port - result.usePort = true; - if (matches.length >= 6 && matches[5]) { - result.address = matches[5]; - } - if (matches.length >= 7 && matches[6]) { - result.port = parseInt(matches[6]); - } - result.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; - } - - // a debug-port=1234 or --inspect-port=1234 overrides the port - matches = DEBUG_PORT_PATTERN.exec(args); - if (matches && matches.length === 3) { - // override port - result.port = parseInt(matches[2]); - result.protocol = matches[1] === 'debug' ? 'legacy' : 'inspector'; - } - - if (result.port < 0) { - result.port = result.protocol === 'inspector' ? INSPECTOR_PORT_DEFAULT : LEGACY_PORT_DEFAULT; - } - - return result; -} 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/.vscodeignore b/extensions/emmet/.vscodeignore index ebab1d50b9b..61ece8e14f8 100644 --- a/extensions/emmet/.vscodeignore +++ b/extensions/emmet/.vscodeignore @@ -1,3 +1,8 @@ test/** src/** -tsconfig.json \ No newline at end of file +out/** +tsconfig.json +node_modules/@emmetio/css-parser/** +node_modules/@emmetio/html-matcher/** +node_modules/@emmetio/math-expression/** +node_modules/image-size/** diff --git a/extensions/emmet/extension.webpack.config.js b/extensions/emmet/extension.webpack.config.js new file mode 100644 index 00000000000..d5064ef4517 --- /dev/null +++ b/extensions/emmet/extension.webpack.config.js @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + extension: './src/extension.ts', + }, + externals: { + 'vscode-emmet-helper': 'commonjs vscode-emmet-helper', + }, +}); 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/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/tsconfig.json b/extensions/emmet/tsconfig.json index 0a6f9c5eaf7..b035483b959 100644 --- a/extensions/emmet/tsconfig.json +++ b/extensions/emmet/tsconfig.json @@ -16,4 +16,4 @@ "include": [ "src/**/*" ] -} \ No newline at end of file +} 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.json b/extensions/extension-editing/package.json index 88b339023b2..b386dc565f3 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -45,6 +45,6 @@ }, "devDependencies": { "@types/markdown-it": "0.0.2", - "@types/node": "6.0.78" + "@types/node": "^8.10.25" } -} \ No newline at end of file +} 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 04b0521c591..54af327ae07 100644 --- a/extensions/extension-editing/src/extensionLinter.ts +++ b/extensions/extension-editing/src/extensionLinter.ts @@ -249,9 +249,10 @@ export class ExtensionLinter { private readPackageJsonInfo(folder: Uri, tree: JsonNode) { const engine = tree && findNodeAtLocation(tree, ['engines', 'vscode']); const repo = tree && findNodeAtLocation(tree, ['repository', 'url']); + const uri = repo && parseUri(repo.value); const info: PackageJsonInfo = { isExtension: !!(engine && engine.type === 'string'), - hasHttpsRepository: !!(repo && repo.type === 'string' && repo.value && parseUri(repo.value).scheme.toLowerCase() === 'https') + hasHttpsRepository: !!(repo && repo.type === 'string' && repo.value && uri && uri.scheme.toLowerCase() === 'https') }; const str = folder.toString(); const oldInfo = this.folderToPackageJsonInfo[str]; @@ -263,6 +264,9 @@ 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') }); try { const document = await workspace.openTextDocument(file); @@ -285,6 +289,9 @@ export class ExtensionLinter { private addDiagnostics(diagnostics: Diagnostic[], document: TextDocument, begin: number, end: number, src: string, context: Context, info: PackageJsonInfo) { const uri = parseUri(src); + if (!uri) { + return; + } const scheme = uri.scheme.toLowerCase(); if (scheme && scheme !== 'https' && scheme !== 'data') { @@ -344,7 +351,7 @@ function parseUri(src: string) { try { return Uri.parse(encodeURI(src)); } catch (err) { - return Uri.parse(''); + return null; } } } \ No newline at end of file diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock index cdc02d1e035..b28c81dbb27 100644 --- a/extensions/extension-editing/yarn.lock +++ b/extensions/extension-editing/yarn.lock @@ -6,10 +6,14 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660" -"@types/node@6.0.78", "@types/node@^6.0.46": +"@types/node@^6.0.46": version "6.0.78" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.78.tgz#5d4a3f579c1524e01ee21bf474e6fba09198f470" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" + argparse@^1.0.7: version "1.0.9" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" diff --git a/extensions/fsharp/test/colorize-results/test_fs.json b/extensions/fsharp/test/colorize-results/test_fs.json index ecc06e7991f..328557e57b3 100644 --- a/extensions/fsharp/test/colorize-results/test_fs.json +++ b/extensions/fsharp/test/colorize-results/test_fs.json @@ -3,9 +3,9 @@ "c": "// from https://msdn.microsoft.com/en-us/library/dd233160.aspx", "t": "source.fsharp comment.line.double-slash.fsharp", "r": { - "dark_plus": "comment: #608B4E", + "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": "// The declaration creates a constructor that takes two values, name and age.", "t": "source.fsharp comment.line.double-slash.fsharp", "r": { - "dark_plus": "comment: #608B4E", + "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": "// A read/write property.", "t": "source.fsharp comment.line.double-slash.fsharp", "r": { - "dark_plus": "comment: #608B4E", + "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/.vscodeignore b/extensions/git/.vscodeignore index 436567b7d69..5c006f2a394 100644 --- a/extensions/git/.vscodeignore +++ b/extensions/git/.vscodeignore @@ -1,5 +1,10 @@ src/** test/** -out/test/** +out/** tsconfig.json -build/** \ No newline at end of file +build/** +node_modules/byline/** +node_modules/file-type/** +node_modules/iconv-lite/** +node_modules/jschardet/** +node_modules/which/** diff --git a/extensions/git/extension.webpack.config.js b/extensions/git/extension.webpack.config.js new file mode 100644 index 00000000000..ac1ed5928a9 --- /dev/null +++ b/extensions/git/extension.webpack.config.js @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + node: { + __dirname: false // leave the __dirname-behaviour intact + }, + entry: { + main: './src/main.ts', + ['askpass-main']: './src/askpass-main.ts' + } +}); diff --git a/extensions/git/package.json b/extensions/git/package.json index e455ec699ad..712f071eb3a 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" }, @@ -1185,8 +1205,8 @@ "@types/byline": "4.2.31", "@types/file-type": "^5.2.1", "@types/mocha": "2.2.43", - "@types/node": "7.0.43", + "@types/node": "^8.10.25", "@types/which": "^1.0.28", "mocha": "^3.2.0" } -} \ No newline at end of file +} diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 7b56fd9e95a..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/api.ts b/extensions/git/src/api.ts deleted file mode 100644 index cfce4c9ed8b..00000000000 --- a/extensions/git/src/api.ts +++ /dev/null @@ -1,65 +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 { Model } from './model'; -import { Repository as ModelRepository } from './repository'; -import { Uri, SourceControlInputBox } from 'vscode'; - -export interface InputBox { - value: string; -} - -export class InputBoxImpl implements InputBox { - set value(value: string) { this.inputBox.value = value; } - get value(): string { return this.inputBox.value; } - constructor(private inputBox: SourceControlInputBox) { } -} - -export interface Repository { - readonly rootUri: Uri; - readonly inputBox: InputBox; -} - -export class RepositoryImpl implements Repository { - - readonly rootUri: Uri; - readonly inputBox: InputBox; - - constructor(repository: ModelRepository) { - this.rootUri = Uri.file(repository.root); - this.inputBox = new InputBoxImpl(repository.inputBox); - } -} - -export interface API { - getRepositories(): Promise; - getGitPath(): Promise; -} - -export class APIImpl implements API { - - constructor(private model: Model) { } - - async getGitPath(): Promise { - return this.model.git.path; - } - - async getRepositories(): Promise { - return this.model.repositories.map(repository => new RepositoryImpl(repository)); - } -} - -export class NoopAPIImpl implements API { - - async getGitPath(): Promise { - throw new Error('Git model not found'); - } - - async getRepositories(): Promise { - throw new Error('Git model not found'); - } -} diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts new file mode 100644 index 00000000000..fdf0142fb45 --- /dev/null +++ b/extensions/git/src/api/api1.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Model } from '../model'; +import { Repository as BaseRepository, Resource } from '../repository'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change } from './git'; +import { Event, SourceControlInputBox, Uri } from 'vscode'; +import { mapEvent } from '../util'; + +class ApiInputBox implements InputBox { + set value(value: string) { this._inputBox.value = value; } + get value(): string { return this._inputBox.value; } + constructor(private _inputBox: SourceControlInputBox) { } +} + +export class ApiChange implements Change { + + constructor(_resource: Resource) { } +} + +export class ApiRepositoryState implements RepositoryState { + + get HEAD(): Branch | undefined { return this._repository.HEAD; } + get refs(): Ref[] { return [...this._repository.refs]; } + get remotes(): Remote[] { return [...this._repository.remotes]; } + get submodules(): Submodule[] { return [...this._repository.submodules]; } + get rebaseCommit(): Commit | undefined { return this._repository.rebaseCommit; } + + get mergeChanges(): Change[] { return this._repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); } + get indexChanges(): Change[] { return this._repository.indexGroup.resourceStates.map(r => new ApiChange(r)); } + get workingTreeChanges(): Change[] { return this._repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); } + + readonly onDidChange: Event = this._repository.onDidRunGitStatus; + + constructor(private _repository: BaseRepository) { } +} + +export class ApiRepository implements Repository { + + readonly rootUri: Uri = Uri.file(this._repository.root); + readonly inputBox: InputBox = new ApiInputBox(this._repository.inputBox); + readonly state: RepositoryState = new ApiRepositoryState(this._repository); + + constructor(private _repository: BaseRepository) { } + + getConfigs(): Promise<{ key: string; value: string; }[]> { + return this._repository.getConfigs(); + } + + getConfig(key: string): Promise { + return this._repository.getConfig(key); + } + + setConfig(key: string, value: string): Promise { + return this._repository.setConfig(key, value); + } + + show(ref: string, path: string): Promise { + return this._repository.show(ref, path); + } + + getCommit(ref: string): Promise { + return this._repository.getCommit(ref); + } + + getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> { + return this._repository.getObjectDetails(treeish, path); + } + + diffWithHEAD(path: string): Promise { + return this._repository.diffWithHEAD(path); + } + + diffWith(ref: string, path: string): Promise { + return this._repository.diffWith(ref, path); + } + + diffIndexWithHEAD(path: string): Promise { + return this._repository.diffIndexWithHEAD(path); + } + + diffIndexWith(ref: string, path: string): Promise { + return this._repository.diffIndexWith(ref, path); + } + + diffBlobs(object1: string, object2: string): Promise { + return this._repository.diffBlobs(object1, object2); + } + + diffBetween(ref1: string, ref2: string, path: string): Promise { + return this._repository.diffBetween(ref1, ref2, path); + } + + hashObject(data: string): Promise { + return this._repository.hashObject(data); + } + + createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise { + return this._repository.branch(name, checkout, ref); + } + + deleteBranch(name: string): Promise { + return this._repository.deleteBranch(name); + } + + getBranch(name: string): Promise { + return this._repository.getBranch(name); + } + + setBranchUpstream(name: string, upstream: string): Promise { + return this._repository.setBranchUpstream(name, upstream); + } + + getMergeBase(ref1: string, ref2: string): Promise { + throw new Error('Method not implemented.'); + } + + status(): Promise { + return this._repository.status(); + } + + checkout(treeish: string): Promise { + return this._repository.checkout(treeish); + } + + addRemote(name: string, url: string): Promise { + return this._repository.addRemote(name, url); + } + + removeRemote(name: string): Promise { + return this._repository.removeRemote(name); + } + + fetch(remote?: string | undefined, ref?: string | undefined): Promise { + return this._repository.fetch(remote, ref); + } + + pull(): Promise { + return this._repository.pull(); + } +} + +export class ApiGit implements Git { + + get path(): string { return this._model.git.path; } + + constructor(private _model: Model) { } +} + +export class ApiImpl implements API { + + readonly git = new ApiGit(this._model); + + get onDidOpenRepository(): Event { + return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r)); + } + + get onDidCloseRepository(): Event { + return mapEvent(this._model.onDidCloseRepository, r => new ApiRepository(r)); + } + + get repositories(): Repository[] { + return this._model.repositories.map(r => new ApiRepository(r)); + } + + constructor(private _model: Model) { } +} diff --git a/extensions/git/src/api/extension.ts b/extensions/git/src/api/extension.ts new file mode 100644 index 00000000000..622d3110724 --- /dev/null +++ b/extensions/git/src/api/extension.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Model } from '../model'; +import { GitExtension, Repository, API } from './git'; +import { ApiRepository, ApiImpl } from './api1'; + +export function deprecated(target: any, key: string, descriptor: any): void { + if (typeof descriptor.value !== 'function') { + throw new Error('not supported'); + } + + const fn = descriptor.value; + descriptor.value = function () { + console.warn(`Git extension API method '${key}' is deprecated.`); + return fn.apply(this, arguments); + }; +} + +class NoModelGitExtension implements GitExtension { + + @deprecated + async getGitPath(): Promise { + throw new Error('Git model not found'); + } + + @deprecated + async getRepositories(): Promise { + throw new Error('Git model not found'); + } + + getAPI(): API { + throw new Error('Git model not found'); + } +} + +class GitExtensionImpl implements GitExtension { + + constructor(private _model: Model) { } + + @deprecated + async getGitPath(): Promise { + return this._model.git.path; + } + + @deprecated + async getRepositories(): Promise { + return this._model.repositories.map(repository => new ApiRepository(repository)); + } + + getAPI(version: number): API { + if (version !== 1) { + throw new Error(`No API version ${version} found.`); + } + + return new ApiImpl(this._model); + } +} + +export function createGitExtension(model?: Model): GitExtension { + if (!model) { + return new NoModelGitExtension(); + } + + return new GitExtensionImpl(model); +} diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts new file mode 100644 index 00000000000..b86b263b940 --- /dev/null +++ b/extensions/git/src/api/git.d.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Uri, SourceControlInputBox, Event, CancellationToken } from 'vscode'; + +export interface Git { + readonly path: string; +} + +export interface InputBox { + value: string; +} + +export const enum RefType { + Head, + RemoteHead, + Tag +} + +export interface Ref { + readonly type: RefType; + readonly name?: string; + readonly commit?: string; + readonly remote?: string; +} + +export interface UpstreamRef { + readonly remote: string; + readonly name: string; +} + +export interface Branch extends Ref { + readonly upstream?: UpstreamRef; + readonly ahead?: number; + readonly behind?: number; +} + +export interface Commit { + readonly hash: string; + readonly message: string; + readonly parents: string[]; +} + +export interface Submodule { + readonly name: string; + readonly path: string; + readonly url: string; +} + +export interface Remote { + readonly name: string; + readonly fetchUrl?: string; + readonly pushUrl?: string; + readonly isReadOnly: boolean; +} + +export interface Change { + // TODO +} + +export interface RepositoryState { + readonly HEAD: Branch | undefined; + readonly refs: Ref[]; + readonly remotes: Remote[]; + readonly submodules: Submodule[]; + readonly rebaseCommit: Commit | undefined; + + readonly mergeChanges: Change[]; + readonly indexChanges: Change[]; + readonly workingTreeChanges: Change[]; + + readonly onDidChange: Event; +} + +export interface Repository { + + readonly rootUri: Uri; + readonly inputBox: InputBox; + readonly state: RepositoryState; + + getConfigs(): Promise<{ key: string; value: string; }[]>; + getConfig(key: string): Promise; + setConfig(key: string, value: string): Promise; + + show(ref: string, path: string): Promise; + getCommit(ref: string): Promise; + getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; + + diffWithHEAD(path: string): Promise; + diffWith(ref: string, path: string): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffBlobs(object1: string, object2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + + hashObject(data: string): Promise; + + createBranch(name: string, checkout: boolean, ref?: string): Promise; + deleteBranch(name: string): Promise; + getBranch(name: string): Promise; + setBranchUpstream(name: string, upstream: string): Promise; + + getMergeBase(ref1: string, ref2: string): Promise; + + status(): Promise; + checkout(treeish: string): Promise; + + addRemote(name: string, url: string): Promise; + removeRemote(name: string): Promise; + + fetch(remote?: string, ref?: string): Promise; + pull(): Promise; +} + +export interface API { + readonly git: Git; + readonly repositories: Repository[]; + readonly onDidOpenRepository: Event; + readonly onDidCloseRepository: Event; +} + +export interface GitExtension { + + /** + * Returns a specific API version. + * + * @param version Version number. + * @returns API instance + */ + getAPI(version: 1): API; +} + +export const enum GitErrorCodes { + BadConfigFile = 'BadConfigFile', + AuthenticationFailed = 'AuthenticationFailed', + NoUserNameConfigured = 'NoUserNameConfigured', + NoUserEmailConfigured = 'NoUserEmailConfigured', + NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified', + NotAGitRepository = 'NotAGitRepository', + NotAtRepositoryRoot = 'NotAtRepositoryRoot', + Conflict = 'Conflict', + UnmergedChanges = 'UnmergedChanges', + PushRejected = 'PushRejected', + RemoteConnectionError = 'RemoteConnectionError', + DirtyWorkTree = 'DirtyWorkTree', + CantOpenResource = 'CantOpenResource', + GitNotFound = 'GitNotFound', + CantCreatePipe = 'CantCreatePipe', + CantAccessRemote = 'CantAccessRemote', + RepositoryNotFound = 'RepositoryNotFound', + RepositoryIsLocked = 'RepositoryIsLocked', + BranchNotFullyMerged = 'BranchNotFullyMerged', + NoRemoteReference = 'NoRemoteReference', + InvalidBranchName = 'InvalidBranchName', + BranchAlreadyExists = 'BranchAlreadyExists', + NoLocalChanges = 'NoLocalChanges', + NoStashFound = 'NoStashFound', + LocalChangesOverwritten = 'LocalChangesOverwritten', + NoUpstreamBranch = 'NoUpstreamBranch', + IsInSubmodule = 'IsInSubmodule', + WrongCase = 'WrongCase', +} \ No newline at end of file diff --git a/extensions/git/src/autofetch.ts b/extensions/git/src/autofetch.ts index 758dc82182c..33439a8d315 100644 --- a/extensions/git/src/autofetch.ts +++ b/extensions/git/src/autofetch.ts @@ -6,10 +6,10 @@ 'use strict'; import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget } from 'vscode'; -import { GitErrorCodes } from './git'; import { Repository, Operation } from './repository'; import { eventToPromise, filterEvent, onceEvent } from './util'; import * as nls from 'vscode-nls'; +import { GitErrorCodes } from './api/git'; const localize = nls.loadMessageBundle(); @@ -102,7 +102,7 @@ export class AutoFetcher { } try { - await this.repository.fetch(); + await this.repository.fetchDefault(); } catch (err) { if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) { this.disable(); diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 19c3cbdbe59..11e02c23814 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ 'use strict'; import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation, TextEditor, MessageOptions } from 'vscode'; -import { Ref, RefType, Git, GitErrorCodes, Branch } from './git'; +import { Git } from './git'; import { Repository, Resource, Status, CommitOptions, ResourceGroupType } from './repository'; import { Model } from './model'; import { toGitUri, fromGitUri } from './uri'; @@ -17,6 +17,7 @@ import { lstat, Stats } from 'fs'; import * as os from 'os'; import TelemetryReporter from 'vscode-extension-telemetry'; import * as nls from 'vscode-nls'; +import { Ref, RefType, Branch, GitErrorCodes } from './api/git'; const localize = nls.loadMessageBundle(); @@ -1021,8 +1022,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); @@ -1234,7 +1235,7 @@ export class CommandCenter { } const name = result.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$/g, '-'); - await repository.branch(name); + await repository.branch(name, true); } @command('git.deleteBranch', { repository: true }) @@ -1354,7 +1355,7 @@ export class CommandCenter { return; } - await repository.fetch(); + await repository.fetchDefault(); } @command('git.pullFrom', { repository: true }) diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts index 443dd774f2a..e03a97bd29b 100644 --- a/extensions/git/src/contentProvider.ts +++ b/extensions/git/src/contentProvider.ts @@ -92,7 +92,11 @@ export class GitContentProvider { return ''; } - return await repository.diff(path, { cached: ref === 'index' }); + if (ref === 'index') { + return await repository.diffIndexWithHEAD(path); + } else { + return await repository.diffWithHEAD(path); + } } const repository = this.model.getRepository(uri); diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 48594918f5d..05eff573e72 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -11,7 +11,7 @@ import { Repository, GitResourceGroup, Status } from './repository'; import { Model } from './model'; import { debounce } from './decorators'; import { filterEvent, dispose, anyEvent, fireEvent } from './util'; -import { GitErrorCodes } from './git'; +import { GitErrorCodes } from './api/git'; type Callback = { resolve: (status: boolean) => void, reject: (err: any) => void }; @@ -93,7 +93,7 @@ class GitDecorationProvider implements DecorationProvider { private static SubmoduleDecorationData: DecorationData = { title: 'Submodule', - abbreviation: 'S', + letter: 'S', color: new ThemeColor('gitDecoration.submoduleResourceForeground') }; diff --git a/extensions/git/src/encoding.ts b/extensions/git/src/encoding.ts index 4396105567f..d2bb831f123 100644 --- a/extensions/git/src/encoding.ts +++ b/extensions/git/src/encoding.ts @@ -9,7 +9,7 @@ import * as jschardet from 'jschardet'; jschardet.Constants.MINIMUM_THRESHOLD = 0.2; -function detectEncodingByBOM(buffer: NodeBuffer): string | null { +function detectEncodingByBOM(buffer: Buffer): string | null { if (!buffer || buffer.length < 2) { return null; } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index a360264a394..3069b8fd77b 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -16,6 +16,7 @@ import * as filetype from 'file-type'; import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; import { CancellationToken } from 'vscode'; import { detectEncoding } from './encoding'; +import { Ref, RefType, Branch, Remote, GitErrorCodes } from './api/git'; const readfile = denodeify(fs.readFile); @@ -31,40 +32,15 @@ export interface IFileStatus { rename?: string; } -export interface Remote { - name: string; - fetchUrl?: string; - pushUrl?: string; - isReadOnly: boolean; -} - export interface Stash { index: number; description: string; } -export enum RefType { - Head, - RemoteHead, - Tag -} - -export interface Ref { - type: RefType; - name?: string; - commit?: string; - remote?: string; -} - -export interface UpstreamRef { - remote: string; - name: string; -} - -export interface Branch extends Ref { - upstream?: UpstreamRef; - ahead?: number; - behind?: number; +interface MutableRemote extends Remote { + fetchUrl?: string; + pushUrl?: string; + isReadOnly: boolean; } function parseVersion(raw: string): string { @@ -309,37 +285,6 @@ export interface IGitOptions { env?: any; } -export const GitErrorCodes = { - BadConfigFile: 'BadConfigFile', - AuthenticationFailed: 'AuthenticationFailed', - NoUserNameConfigured: 'NoUserNameConfigured', - NoUserEmailConfigured: 'NoUserEmailConfigured', - NoRemoteRepositorySpecified: 'NoRemoteRepositorySpecified', - NotAGitRepository: 'NotAGitRepository', - NotAtRepositoryRoot: 'NotAtRepositoryRoot', - Conflict: 'Conflict', - UnmergedChanges: 'UnmergedChanges', - PushRejected: 'PushRejected', - RemoteConnectionError: 'RemoteConnectionError', - DirtyWorkTree: 'DirtyWorkTree', - CantOpenResource: 'CantOpenResource', - GitNotFound: 'GitNotFound', - CantCreatePipe: 'CantCreatePipe', - CantAccessRemote: 'CantAccessRemote', - RepositoryNotFound: 'RepositoryNotFound', - RepositoryIsLocked: 'RepositoryIsLocked', - BranchNotFullyMerged: 'BranchNotFullyMerged', - NoRemoteReference: 'NoRemoteReference', - InvalidBranchName: 'InvalidBranchName', - BranchAlreadyExists: 'BranchAlreadyExists', - NoLocalChanges: 'NoLocalChanges', - NoStashFound: 'NoStashFound', - LocalChangesOverwritten: 'LocalChangesOverwritten', - NoUpstreamBranch: 'NoUpstreamBranch', - IsInSubmodule: 'IsInSubmodule', - WrongCase: 'WrongCase', -}; - function getGitErrorCode(stderr: string): string | undefined { if (/Another git process seems to be running in this repository|If no other git process is currently running/.test(stderr)) { return GitErrorCodes.RepositoryIsLocked; @@ -427,6 +372,10 @@ export class Git { return await this._exec(args, options); } + async exec2(args: string[], options: SpawnOptions = {}): Promise> { + return await this._exec(args, options); + } + stream(cwd: string, args: string[], options: SpawnOptions = {}): cp.ChildProcess { options = assign({ cwd }, options || {}); return this.spawn(args, options); @@ -674,10 +623,6 @@ export function parseLsFiles(raw: string): LsFilesElement[] { .map(([, mode, object, stage, file]) => ({ mode, object, stage, file })); } -export interface DiffOptions { - cached?: boolean; -} - export class Repository { constructor( @@ -706,7 +651,7 @@ export class Repository { return this.git.spawn(args, options); } - async config(scope: string, key: string, value: any, options: SpawnOptions): Promise { + async config(scope: string, key: string, value: any = null, options: SpawnOptions = {}): Promise { const args = ['config']; if (scope) { @@ -723,6 +668,24 @@ export class Repository { return result.stdout; } + async getConfigs(scope: string): Promise<{ key: string; value: string; }[]> { + const args = ['config']; + + if (scope) { + args.push('--' + scope); + } + + args.push('-l'); + + const result = await this.run(args); + const lines = result.stdout.trim().split(/\r|\r\n|\n/); + + return lines.map(entry => { + const equalsIndex = entry.indexOf('='); + return { key: entry.substr(0, equalsIndex), value: entry.substr(equalsIndex + 1) }; + }); + } + async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false): Promise { const stdout = await this.buffer(object); @@ -848,10 +811,10 @@ export class Repository { } } - async diff(path: string, options: DiffOptions = {}): Promise { + async diff(path: string, cached = false): Promise { const args = ['diff']; - if (options.cached) { + if (cached) { args.push('--cached'); } @@ -861,6 +824,57 @@ export class Repository { return result.stdout; } + async diffWithHEAD(path: string): Promise { + const args = ['diff', '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffWith(ref: string, path: string): Promise { + const args = ['diff', ref, '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffIndexWithHEAD(path: string): Promise { + const args = ['diff', '--cached', '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffIndexWith(ref: string, path: string): Promise { + const args = ['diff', '--cached', ref, '--', path]; + const result = await this.run(args); + return result.stdout; + } + + async diffBlobs(object1: string, object2: string): Promise { + const args = ['diff', object1, object2]; + const result = await this.run(args); + return result.stdout; + } + + async diffBetween(ref1: string, ref2: string, path: string): Promise { + const args = ['diff', `${ref1}...${ref2}`, '--', path]; + const result = await this.run(args); + + return result.stdout.trim(); + } + + async getMergeBase(ref1: string, ref2: string): Promise { + const args = ['merge-base', ref1, ref2]; + const result = await this.run(args); + + return result.stdout.trim(); + } + + async hashObject(data: string): Promise { + const args = ['hash-object', '-w', '--stdin']; + const result = await this.run(args, { input: data }); + + return result.stdout.trim(); + } + async add(paths: string[]): Promise { const args = ['add', '-A', '--']; @@ -981,8 +995,13 @@ export class Repository { throw commitErr; } - async branch(name: string, checkout: boolean): Promise { + async branch(name: string, checkout: boolean, ref?: string): Promise { const args = checkout ? ['checkout', '-q', '-b', name] : ['branch', '-q', name]; + + if (ref) { + args.push(ref); + } + await this.run(args); } @@ -996,6 +1015,11 @@ export class Repository { await this.run(args); } + async setBranchUpstream(name: string, upstream: string): Promise { + const args = ['branch', '--set-upstream-to', upstream, name]; + await this.run(args); + } + async deleteRef(ref: string): Promise { const args = ['update-ref', '-d', ref]; await this.run(args); @@ -1093,7 +1117,27 @@ export class Repository { } } - async fetch(): Promise { + async addRemote(name: string, url: string): Promise { + const args = ['remote', 'add', name, url]; + await this.run(args); + } + + async removeRemote(name: string): Promise { + const args = ['remote', 'rm', name]; + await this.run(args); + } + + async fetch(remote?: string, ref?: string): Promise { + const args = ['fetch']; + + if (remote) { + args.push(remote); + + if (ref) { + args.push(ref); + } + } + try { await this.run(['fetch']); } catch (err) { @@ -1316,7 +1360,7 @@ export class Repository { async getRemotes(): Promise { const result = await this.run(['remote', '--verbose']); const lines = result.stdout.trim().split('\n').filter(l => !!l); - const remotes: Remote[] = []; + const remotes: MutableRemote[] = []; for (const line of lines) { const parts = line.split(/\s/); diff --git a/extensions/git/src/iterators.ts b/extensions/git/src/iterators.ts deleted file mode 100644 index 3b0f741f310..00000000000 --- a/extensions/git/src/iterators.ts +++ /dev/null @@ -1,57 +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'; - -function* filter(it: IterableIterator, condition: (t: T, i: number) => boolean): IterableIterator { - let i = 0; - for (let t of it) { - if (condition(t, i++)) { - yield t; - } - } -} - -function* map(it: IterableIterator, fn: (t: T, i: number) => R): IterableIterator { - let i = 0; - for (let t of it) { - yield fn(t, i++); - } -} - -export interface FunctionalIterator extends Iterable { - filter(condition: (t: T, i: number) => boolean): FunctionalIterator; - map(fn: (t: T, i: number) => R): FunctionalIterator; - toArray(): T[]; -} - -class FunctionalIteratorImpl implements FunctionalIterator { - - constructor(private iterator: IterableIterator) { } - - filter(condition: (t: T, i: number) => boolean): FunctionalIterator { - return new FunctionalIteratorImpl(filter(this.iterator, condition)); - } - - map(fn: (t: T, i: number) => R): FunctionalIterator { - return new FunctionalIteratorImpl(map(this.iterator, fn)); - } - - toArray(): T[] { - return Array.from(this.iterator); - } - - [Symbol.iterator](): IterableIterator { - return this.iterator; - } -} - -export function iterate(obj: T[] | IterableIterator): FunctionalIterator { - if (Array.isArray(obj)) { - return new FunctionalIteratorImpl(obj[Symbol.iterator]()); - } - - return new FunctionalIteratorImpl(obj); -} \ No newline at end of file diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 6eae1da2c9d..0d3ebc4d560 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -7,6 +7,7 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); + import { ExtensionContext, workspace, window, Disposable, commands, Uri, OutputChannel } from 'vscode'; import { findGit, Git, IGit } from './git'; import { Model } from './model'; @@ -16,8 +17,9 @@ import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; -import { API, NoopAPIImpl, APIImpl } from './api'; +import { GitExtension } from './api/git'; import { GitProtocolHandler } from './protocolHandler'; +import { createGitExtension } from './api/extension'; const deactivateTasks: { (): Promise; }[] = []; @@ -69,7 +71,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann return model; } -export async function activate(context: ExtensionContext): Promise { +export async function activate(context: ExtensionContext): Promise { const disposables: Disposable[] = []; context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose())); @@ -77,7 +79,7 @@ export async function activate(context: ExtensionContext): Promise { commands.registerCommand('git.showOutput', () => outputChannel.show()); disposables.push(outputChannel); - const { name, version, aiKey } = require(context.asAbsolutePath('./package.json')) as { name: string, version: string, aiKey: string }; + const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string }; const telemetryReporter = new TelemetryReporter(name, version, aiKey); deactivateTasks.push(() => telemetryReporter.dispose()); @@ -92,7 +94,7 @@ export async function activate(context: ExtensionContext): Promise { try { const model = await createModel(context, outputChannel, telemetryReporter, disposables); - return new APIImpl(model); + return createGitExtension(model); } catch (err) { if (!/Git installation not found/.test(err.message || '')) { throw err; @@ -121,7 +123,7 @@ export async function activate(context: ExtensionContext): Promise { } } - return new NoopAPIImpl(); + return createGitExtension(); } } diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index f28f71324ca..42011b06c65 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -9,11 +9,12 @@ import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitte import { Repository, RepositoryState } from './repository'; import { memoize, sequentialize, debounce } from './decorators'; import { dispose, anyEvent, filterEvent, isDescendant, firstIndex } from './util'; -import { Git, GitErrorCodes } from './git'; +import { Git } from './git'; import * as path from 'path'; import * as fs from 'fs'; import * as nls from 'vscode-nls'; import { fromGitUri } from './uri'; +import { GitErrorCodes } from './api/git'; const localize = nls.loadMessageBundle(); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index b3af0698312..b63466635d4 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -6,7 +6,7 @@ 'use strict'; import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode'; -import { Repository as BaseRepository, Ref, Branch, Remote, Commit, GitErrorCodes, Stash, RefType, GitError, Submodule, DiffOptions } from './git'; +import { Repository as BaseRepository, Commit, Stash, GitError, Submodule } from './git'; import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent } from './util'; import { memoize, throttle, debounce } from './decorators'; import { toGitUri } from './uri'; @@ -15,6 +15,7 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; import * as fs from 'fs'; import { StatusBarCommands } from './statusbar'; +import { Branch, Ref, Remote, RefType, GitErrorCodes } from './api/git'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -259,10 +260,10 @@ export class Resource implements SourceControlResourceState { get resourceDecoration(): DecorationData { const title = this.tooltip; - const abbreviation = this.letter; + const letter = this.letter; const color = this.color; const priority = this.priority; - return { bubble: true, source: 'git.resource', title, abbreviation, color, priority }; + return { bubble: true, source: 'git.resource', title, letter, color, priority }; } constructor( @@ -276,14 +277,20 @@ export class Resource implements SourceControlResourceState { export enum Operation { Status = 'Status', + Config = 'Config', Diff = 'Diff', + MergeBase = 'MergeBase', Add = 'Add', RevertFiles = 'RevertFiles', Commit = 'Commit', Clean = 'Clean', Branch = 'Branch', + GetBranch = 'GetBranch', + SetBranchUpstream = 'SetBranchUpstream', + HashObject = 'HashObject', Checkout = 'Checkout', Reset = 'Reset', + Remote = 'Remote', Fetch = 'Fetch', Pull = 'Pull', Push = 'Push', @@ -649,13 +656,53 @@ export class Repository implements Disposable { } } + getConfigs(): Promise<{ key: string; value: string; }[]> { + return this.run(Operation.Config, () => this.repository.getConfigs('local')); + } + + getConfig(key: string): Promise { + return this.run(Operation.Config, () => this.repository.config('local', key)); + } + + setConfig(key: string, value: string): Promise { + return this.run(Operation.Config, () => this.repository.config('local', key, value)); + } + @throttle async status(): Promise { await this.run(Operation.Status); } - diff(path: string, options: DiffOptions = {}): Promise { - return this.run(Operation.Diff, () => this.repository.diff(path, options)); + diffWithHEAD(path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffWithHEAD(path)); + } + + diffWith(ref: string, path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffWith(ref, path)); + } + + diffIndexWithHEAD(path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffIndexWithHEAD(path)); + } + + diffIndexWith(ref: string, path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffIndexWith(ref, path)); + } + + diffBlobs(object1: string, object2: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffBlobs(object1, object2)); + } + + diffBetween(ref1: string, ref2: string, path: string): Promise { + return this.run(Operation.Diff, () => this.repository.diffBetween(ref1, ref2, path)); + } + + getMergeBase(ref1: string, ref2: string): Promise { + return this.run(Operation.MergeBase, () => this.repository.getMergeBase(ref1, ref2)); + } + + async hashObject(data: string): Promise { + return this.run(Operation.HashObject, () => this.repository.hashObject(data)); } async add(resources: Uri[]): Promise { @@ -745,7 +792,7 @@ export class Repository implements Disposable { }); } - async branch(name: string): Promise { + async branch(name: string, checkout: boolean, ref?: string): Promise { await this.run(Operation.Branch, () => this.repository.branch(name, true)); } @@ -757,6 +804,14 @@ export class Repository implements Disposable { await this.run(Operation.RenameBranch, () => this.repository.renameBranch(name)); } + async getBranch(name: string): Promise { + return await this.run(Operation.GetBranch, () => this.repository.getBranch(name)); + } + + async setBranchUpstream(name: string, upstream: string): Promise { + await this.run(Operation.SetBranchUpstream, () => this.repository.setBranchUpstream(name, upstream)); + } + async merge(ref: string): Promise { await this.run(Operation.Merge, () => this.repository.merge(ref)); } @@ -781,8 +836,20 @@ export class Repository implements Disposable { await this.run(Operation.DeleteRef, () => this.repository.deleteRef(ref)); } + async addRemote(name: string, url: string): Promise { + await this.run(Operation.Remote, () => this.repository.addRemote(name, url)); + } + + async removeRemote(name: string): Promise { + await this.run(Operation.Remote, () => this.repository.removeRemote(name)); + } + @throttle - async fetch(): Promise { + async fetchDefault(): Promise { + await this.run(Operation.Fetch, () => this.repository.fetch()); + } + + async fetch(remote?: string, ref?: string): Promise { await this.run(Operation.Fetch, () => this.repository.fetch()); } @@ -800,7 +867,7 @@ export class Repository implements Disposable { } @throttle - async pull(head: Branch | undefined): Promise { + async pull(head?: Branch): Promise { let remote: string | undefined; let branch: string | undefined; diff --git a/extensions/git/src/statusbar.ts b/extensions/git/src/statusbar.ts index 979c961f64d..8cabe51c016 100644 --- a/extensions/git/src/statusbar.ts +++ b/extensions/git/src/statusbar.ts @@ -6,10 +6,10 @@ 'use strict'; import { Disposable, Command, EventEmitter, Event } from 'vscode'; -import { Branch } from './git'; import { Repository, Operation } from './repository'; import { anyEvent, dispose } from './util'; import * as nls from 'vscode-nls'; +import { Branch } from './api/git'; const localize = nls.loadMessageBundle(); diff --git a/extensions/git/src/typings/jschardet.d.ts b/extensions/git/src/typings/jschardet.d.ts index 9c553b129eb..f252a47fd09 100644 --- a/extensions/git/src/typings/jschardet.d.ts +++ b/extensions/git/src/typings/jschardet.d.ts @@ -3,7 +3,7 @@ declare module 'jschardet' { encoding: string, confidence: number } - export function detect(buffer: NodeBuffer): IDetectedMap; + export function detect(buffer: Buffer): IDetectedMap; export const Constants: { MINIMUM_THRESHOLD: number, 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/tsconfig.json b/extensions/git/tsconfig.json index a7eca805f97..7477ea05a83 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -16,4 +16,4 @@ "include": [ "src/**/*" ] -} \ No newline at end of file +} diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index d8a4d64e743..cf3f64be853 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -22,9 +22,9 @@ version "8.0.51" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" "@types/which@^1.0.28": version "1.0.28" @@ -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/grunt/package.json b/extensions/grunt/package.json index 18c5c21e0cb..c1839be2cc9 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -19,7 +19,7 @@ "vscode-nls": "^3.2.4" }, "devDependencies": { - "@types/node": "7.0.43" + "@types/node": "^8.10.25" }, "main": "./out/main", "activationEvents": [ @@ -46,7 +46,9 @@ "taskDefinitions": [ { "type": "grunt", - "required": ["task"], + "required": [ + "task" + ], "properties": { "task": { "type": "string", @@ -60,4 +62,4 @@ } ] } -} \ No newline at end of file +} diff --git a/extensions/grunt/yarn.lock b/extensions/grunt/yarn.lock index 573098d7d26..fe244123db7 100644 --- a/extensions/grunt/yarn.lock +++ b/extensions/grunt/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" vscode-nls@^3.2.4: version "3.2.4" diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 65d9197a0d9..5c5656a126d 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -19,7 +19,7 @@ "vscode-nls": "^3.2.4" }, "devDependencies": { - "@types/node": "7.0.43" + "@types/node": "^8.10.25" }, "main": "./out/main", "activationEvents": [ @@ -46,7 +46,9 @@ "taskDefinitions": [ { "type": "gulp", - "required": ["task"], + "required": [ + "task" + ], "properties": { "task": { "type": "string", @@ -60,4 +62,4 @@ } ] } -} \ No newline at end of file +} diff --git a/extensions/gulp/yarn.lock b/extensions/gulp/yarn.lock index 573098d7d26..fe244123db7 100644 --- a/extensions/gulp/yarn.lock +++ b/extensions/gulp/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" vscode-nls@^3.2.4: version "3.2.4" 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/.vscodeignore b/extensions/html-language-features/.vscodeignore index da621e23a7a..fe4f2396293 100644 --- a/extensions/html-language-features/.vscodeignore +++ b/extensions/html-language-features/.vscodeignore @@ -1,8 +1,10 @@ test/** +.vscode/** server/tsconfig.json -server/node_modules/@types/** server/src/** server/test/** server/out/test/** client/tsconfig.json -client/src/** \ No newline at end of file +client/src/** +**/node_modules/*/lib/esm/** +**/node_modules/@types/** \ No newline at end of file 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..e55530df063 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,12 +173,11 @@ } }, "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": { - "@types/node": "7.0.43" + "@types/node": "^8.10.25" } } diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index e9586c7ad34..9d4b15d815b 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -8,17 +8,16 @@ "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", - "@types/node": "7.0.43", + "@types/node": "^8.10.25", "glob": "^7.1.2", "mocha": "^5.2.0", "mocha-junit-reporter": "^1.17.0", diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 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..d642e6bb321 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -6,9 +6,9 @@ version "2.2.33" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" ansi-regex@^3.0.0: version "3.0.0" @@ -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..f6e965d98ea 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" applicationinsights@1.0.1: version "1.0.1" @@ -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/jake/package.json b/extensions/jake/package.json index 6befa9fbd9e..34a149cf355 100644 --- a/extensions/jake/package.json +++ b/extensions/jake/package.json @@ -19,7 +19,7 @@ "vscode-nls": "^3.2.4" }, "devDependencies": { - "@types/node": "7.0.43" + "@types/node": "^8.10.25" }, "main": "./out/main", "activationEvents": [ @@ -46,7 +46,9 @@ "taskDefinitions": [ { "type": "jake", - "required": ["task"], + "required": [ + "task" + ], "properties": { "task": { "type": "string", @@ -60,4 +62,4 @@ } ] } -} \ No newline at end of file +} diff --git a/extensions/jake/yarn.lock b/extensions/jake/yarn.lock index 573098d7d26..fe244123db7 100644 --- a/extensions/jake/yarn.lock +++ b/extensions/jake/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" vscode-nls@^3.2.4: version "3.2.4" diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index b19cc7b4754..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/2f20bc5a5b07686ec0139e2969431210d81b6991", + "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,6 +1302,161 @@ } ] }, + "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": "(?=\\=|;)", @@ -1442,24 +1477,6 @@ "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 665268f9e6a..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" } @@ -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" } @@ -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" } diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index 1e8f440a420..b41053843cf 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -25,6 +25,7 @@ ["\"", "\""], ["`", "`"] ], + "autoCloseBefore": ";:.,=}])>` \n\t", "folding": { "markers": { "start": "^\\s*//\\s*#?region\\b", diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 50bb5f619c5..58c279763a6 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/27425437b2144f43607047ae7ee9b826e36856a5", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/4407e8d57037cdb02e5fb670bcf318e0daebb40f", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -93,6 +93,10 @@ }, { "include": "#export-declaration" + }, + { + "name": "storage.modifier.js", + "match": "(?)\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 +557,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.js", + "begin": "(?)\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" }, - { - "include": "#string" - }, - { - "include": "#array-literal" - }, - { - "include": "#numeric-literal" - }, - { - "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*([\\{\\[]\\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" - }, - "2": { - "name": "keyword.operator.optional.js" - } - } - }, - { - "name": "meta.definition.property.js variable.object.property.js", - "match": "[_$[:alpha:]][_$[:alnum:]]*" - }, - { - "name": "keyword.operator.optional.js", - "match": "\\?" + "2": { + "name": "keyword.operator.optional.js" } - ] + } + }, + { + "name": "meta.definition.property.js variable.object.property.js", + "match": "[_$[:alpha:]][_$[:alnum:]]*" + }, + { + "name": "keyword.operator.optional.js", + "match": "\\?" } ] }, @@ -869,21 +1138,24 @@ }, "function-declaration": { "name": "meta.function.js", - "begin": "(?\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2568,7 +2870,7 @@ "name": "keyword.control.as.js" } }, - "end": "(?=$|^|[;,:})\\]]|((?)" } ] }, @@ -3625,13 +3931,10 @@ "include": "#typeof-operator" }, { - "begin": "(?:([&|])|(=(?!>)))(?=\\s*\\{)", + "begin": "([&|\\*])(?=\\s*\\{)", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.js" - }, - "2": { - "name": "keyword.operator.assignment.js" } }, "end": "(?<=\\})", @@ -3642,13 +3945,10 @@ ] }, { - "begin": "([&|])|(=(?!>))", + "begin": "[&|\\*]", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.js" - }, - "2": { - "name": "keyword.operator.assignment.js" } }, "end": "(?=\\S)" @@ -3780,7 +4080,7 @@ "patterns": [ { "name": "string.template.js", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js" @@ -3846,7 +4146,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 +4169,7 @@ }, { "name": "string.regexp.js", - "begin": "(?\\s*$)", @@ -4125,7 +4447,7 @@ "name": "punctuation.definition.comment.js" } }, - "end": "(?=^)", + "end": "(?=$)", "patterns": [ { "name": "meta.tag.js", @@ -4608,8 +4930,8 @@ ] }, "jsx-tag-without-attributes-in-expression": { - "begin": "(?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\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 +4940,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 +4990,8 @@ ] }, "jsx-tag-in-expression": { - "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*(?:([_$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 +5000,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 +5027,7 @@ }, "patterns": [ { - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.js" @@ -4838,7 +5160,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 8015ed5e8ed..230fbaa7a0a 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/27425437b2144f43607047ae7ee9b826e36856a5", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/4407e8d57037cdb02e5fb670bcf318e0daebb40f", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -93,6 +93,10 @@ }, { "include": "#export-declaration" + }, + { + "name": "storage.modifier.js.jsx", + "match": "(?)\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 +557,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.js.jsx", + "begin": "(?)\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" }, - { - "include": "#string" - }, - { - "include": "#array-literal" - }, - { - "include": "#numeric-literal" - }, - { - "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*([\\{\\[]\\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" - }, - "2": { - "name": "keyword.operator.optional.js.jsx" - } - } - }, - { - "name": "meta.definition.property.js.jsx variable.object.property.js.jsx", - "match": "[_$[:alpha:]][_$[:alnum:]]*" - }, - { - "name": "keyword.operator.optional.js.jsx", - "match": "\\?" + "2": { + "name": "keyword.operator.optional.js.jsx" } - ] + } + }, + { + "name": "meta.definition.property.js.jsx variable.object.property.js.jsx", + "match": "[_$[:alpha:]][_$[:alnum:]]*" + }, + { + "name": "keyword.operator.optional.js.jsx", + "match": "\\?" } ] }, @@ -869,21 +1138,24 @@ }, "function-declaration": { "name": "meta.function.js.jsx", - "begin": "(?\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js.jsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2568,7 +2870,7 @@ "name": "keyword.control.as.js.jsx" } }, - "end": "(?=$|^|[;,:})\\]]|((?)" } ] }, @@ -3625,13 +3931,10 @@ "include": "#typeof-operator" }, { - "begin": "(?:([&|])|(=(?!>)))(?=\\s*\\{)", + "begin": "([&|\\*])(?=\\s*\\{)", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.js.jsx" - }, - "2": { - "name": "keyword.operator.assignment.js.jsx" } }, "end": "(?<=\\})", @@ -3642,13 +3945,10 @@ ] }, { - "begin": "([&|])|(=(?!>))", + "begin": "[&|\\*]", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.js.jsx" - }, - "2": { - "name": "keyword.operator.assignment.js.jsx" } }, "end": "(?=\\S)" @@ -3780,7 +4080,7 @@ "patterns": [ { "name": "string.template.js.jsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js.jsx" @@ -3846,7 +4146,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 +4169,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "(?\\s*$)", @@ -4125,7 +4447,7 @@ "name": "punctuation.definition.comment.js.jsx" } }, - "end": "(?=^)", + "end": "(?=$)", "patterns": [ { "name": "meta.tag.js.jsx", @@ -4608,8 +4930,8 @@ ] }, "jsx-tag-without-attributes-in-expression": { - "begin": "(?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\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 +4940,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 +4990,8 @@ ] }, "jsx-tag-in-expression": { - "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*(?:([_$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 +5000,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 +5027,7 @@ }, "patterns": [ { - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.js.jsx" @@ -4838,7 +5160,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/.vscodeignore b/extensions/json-language-features/.vscodeignore index d30ea9070d5..eea89a1f649 100644 --- a/extensions/json-language-features/.vscodeignore +++ b/extensions/json-language-features/.vscodeignore @@ -1,7 +1,11 @@ test/** +.vscode/** client/tsconfig.json client/src/** server/bin server/tsconfig.json server/src/** -server/node_modules/@types/** \ No newline at end of file +server/test/** +server/node_modules/@types/** +**/node_modules/*/lib/esm/** +**/node_modules/@types/** \ No newline at end of file diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 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..a1910651a22 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -100,12 +100,11 @@ } }, "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.2", "vscode-nls": "^3.2.4" }, "devDependencies": { - "@types/node": "7.0.43" + "@types/node": "^8.10.25" } } 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..76fc712381c 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -11,17 +11,16 @@ "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.5", + "vscode-languageserver": "^4.4.2", "vscode-nls": "^3.2.4", - "vscode-uri": "^1.0.3" + "vscode-uri": "^1.0.5" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "7.0.43" + "@types/node": "^8.10.25" }, "scripts": { "prepublishOnly": "npm run clean && npm run test", diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index 2fa7619d1ff..c152a7ac137 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[]; } @@ -61,6 +59,7 @@ documents.listen(connection); let clientSnippetSupport = false; let clientDynamicRegisterSupport = false; let foldingRangeLimit = Number.MAX_VALUE; +let hierarchicalDocumentSymbolSupport = false; // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. @@ -81,7 +80,8 @@ 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 = { + hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); + 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, @@ -344,7 +344,11 @@ connection.onDocumentSymbol((documentSymbolParams, token) => { const document = documents.get(documentSymbolParams.textDocument.uri); if (document) { const jsonDocument = getJSONDocument(document); - return languageService.findDocumentSymbols(document, jsonDocument); + if (hierarchicalDocumentSymbolSupport) { + return languageService.findDocumentSymbols2(document, jsonDocument); + } else { + return languageService.findDocumentSymbols(document, jsonDocument); + } } return []; }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); @@ -382,7 +386,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..b8eca3bc52b 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -6,9 +6,9 @@ version "2.2.33" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" agent-base@4, agent-base@^4.1.0: version "4.1.2" @@ -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,49 @@ 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.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.5.tgz#4ebac4cadcaedd55ea2d0716259b50a89955e00e" 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.1" + 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.3: + version "3.10.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.3.tgz#59841c9602a6a6baab68613c2a47760994657196" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.1" -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.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.1.tgz#d5d5f44f688a3b2aa9857dc53cb9cacca73fe35a" -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.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.4.2.tgz#600ae9cc7a6ff1e84d93c7807840c2cb5b22821b" dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-uri "^1.0.1" + vscode-languageserver-protocol "^3.10.3" + vscode-uri "^1.0.5" -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..cb126a6c120 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" applicationinsights@1.0.1: version "1.0.1" @@ -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.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.4.2.tgz#a341a7b4ac403e481f011ed4572854646e8968c4" dependencies: - vscode-languageserver-protocol "^3.7.2" + vscode-languageserver-protocol "^3.10.3" -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.3: + version "3.10.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.3.tgz#59841c9602a6a6baab68613c2a47760994657196" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.1" -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.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.1.tgz#d5d5f44f688a3b2aa9857dc53cb9cacca73fe35a" 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/test/colorize-results/test_lua.json b/extensions/lua/test/colorize-results/test_lua.json index c1495f2253e..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" } @@ -608,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" } @@ -619,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/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/snippets/markdown.json b/extensions/markdown-basics/snippets/markdown.json index 6bba2591d10..4a5152c18c8 100644 --- a/extensions/markdown-basics/snippets/markdown.json +++ b/extensions/markdown-basics/snippets/markdown.json @@ -60,12 +60,12 @@ }, "Insert link": { "prefix": "link", - "body": "[${1:text}](http://${2:link})$0", + "body": "[${1:text}](https://${2:link})$0", "description": "Insert link" }, "Insert image": { "prefix": "image", - "body": "![${1:alt}](http://${2:link})$0", + "body": "![${1:alt}](https://${2:link})$0", "description": "Insert image" } } diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index d49d0688467..a81c3615bb5 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4504240cdb13a4640f64fc98a0adb858226a879e", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/5f5b3d7eaced763432867f8eb6d3d768f6cd2acc", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -165,6 +165,9 @@ { "include": "#fenced_code_block_dart" }, + { + "include": "#fenced_code_block_handlebars" + }, { "include": "#fenced_code_block_unknown" }, @@ -186,7 +189,7 @@ "begin": "(^|\\G)[ ]{0,3}(>) ?", "captures": { "2": { - "name": "beginning.punctuation.definition.quote.markdown" + "name": "punctuation.definition.quote.begin.markdown" } }, "name": "markup.quote.markdown", @@ -1685,6 +1688,39 @@ } ] }, + "fenced_code_block_handlebars": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(handlebars|hbs)(\\s+[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "5": { + "name": "fenced_code.block.language" + }, + "6": { + "name": "fenced_code.block.language.attributes" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.handlebars", + "patterns": [ + { + "include": "text.html.handlebars" + } + ] + } + ] + }, "fenced_code_block_unknown": { "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?=([^`~]*)?$)", "beginCaptures": { @@ -1912,7 +1948,7 @@ "name": "punctuation.definition.string.end.markdown" } }, - "match": "(?x)\n \\s* # Leading whitespace\n (\\[)(.+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotes…\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", + "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([\\w ]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotes…\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", "name": "meta.link.reference.def.markdown" }, "list_paragraph": { @@ -1934,10 +1970,10 @@ "lists": { "patterns": [ { - "begin": "(^|\\G)([ ]{0,3})([*+-])([ ]{1,3}|\\t)", + "begin": "(^|\\G)([ ]{0,3})([*+-])([ \\t])", "beginCaptures": { "3": { - "name": "beginning.punctuation.definition.list.markdown" + "name": "punctuation.definition.list.begin.markdown" } }, "comment": "Currently does not support un-indented second lines.", @@ -1950,13 +1986,13 @@ "include": "#list_paragraph" } ], - "while": "((^|\\G)([ ]{4}|\\t))|(^[ \\t]*$)" + "while": "((^|\\G)([ ]{2,4}|\\t))|(^[ \\t]*$)" }, { - "begin": "(^|\\G)([ ]{0,3})([0-9]+\\.)([ ]{1,3}|\\t)", + "begin": "(^|\\G)([ ]{0,3})([0-9]+\\.)([ \\t])", "beginCaptures": { "3": { - "name": "beginning.punctuation.definition.list.markdown" + "name": "punctuation.definition.list.begin.markdown" } }, "name": "markup.list.numbered.markdown", @@ -1968,7 +2004,7 @@ "include": "#list_paragraph" } ], - "while": "((^|\\G)([ ]{4}|\\t))|(^[ \\t]*$)" + "while": "((^|\\G)([ ]{2,4}|\\t))|(^[ \\t]*$)" } ] }, @@ -1994,7 +2030,7 @@ "while": "(^|\\G)([ ]{4}|\\t)" }, "separator": { - "match": "(^|\\G)[ ]{0,3}([\\*\\-\\_])([ ]{0,2}\\2){2,}[ \\t]*$\\n?", + "match": "(^|\\G)[ ]{0,3}([*-_])([ ]{0,2}\\2){2,}[ \\t]*$\\n?", "name": "meta.separator.markdown" } } @@ -2049,6 +2085,9 @@ }, { "include": "#link-ref-literal" + }, + { + "include": "#link-ref-shortcut" } ], "repository": { @@ -2058,7 +2097,7 @@ "name": "meta.other.valid-ampersand.markdown" }, "bold": { - "begin": "(?x)\n ((?]*+> # HTML tags\n | (?`+)([^`]|(?!(?(?!`))`)*+\\k\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (? # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whitespace\n ? # URL\n [ \\t]*+ # Optional whitespace\n ( # Optional Title\n (?['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | (?!(?<=\\S)\\1). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=__\\b|\\*\\*)\\1 # Close\n )\n", + "begin": "(?x)\n (\\*\\*(?=\\w)|(?<!\\w)\\*\\*|(?<!\\w)\\b__)(?=\\S) # Open\n (?=\n (\n <[^>]*+> # HTML tags\n | (?<raw>`+)([^`]|(?!(?<!`)\\k<raw>(?!`))`)*+\\k<raw>\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (?<square> # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g<square>*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whitespace\n <?(.*?)>? # URL\n [ \\t]*+ # Optional whitespace\n ( # Optional Title\n (?<title>['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | (?!(?<=\\S)\\1). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=__\\b|\\*\\*)\\1 # Close\n )\n", "captures": { "1": { "name": "punctuation.definition.bold.markdown" @@ -2115,6 +2154,9 @@ }, { "include": "#link-ref" + }, + { + "include": "#link-ref-shortcut" } ] }, @@ -2200,7 +2242,7 @@ "name": "meta.image.reference.markdown" }, "italic": { - "begin": "(?x) (\\*\\b|\\b_)(?=\\S) # Open\n (?=\n (\n <[^>]*+> # HTML tags\n | (?<raw>`+)([^`]|(?!(?<!`)\\k<raw>(?!`))`)*+\\k<raw>\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (?<square> # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g<square>*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whtiespace\n <?(.*?)>? # URL\n [ \\t]*+ # Optional whtiespace\n ( # Optional Title\n (?<title>['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | \\1\\1 # Must be bold closer\n | (?!(?<=\\S)\\1). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=_\\b|\\*)\\1 # Close\n )\n", + "begin": "(?x) (\\*(?=\\w)|(?<!\\w)\\*|(?<!\\w)\\b_)(?=\\S) # Open\n (?=\n (\n <[^>]*+> # HTML tags\n | (?<raw>`+)([^`]|(?!(?<!`)\\k<raw>(?!`))`)*+\\k<raw>\n # Raw\n | \\\\[\\\\`*_{}\\[\\]()#.!+\\->]?+ # Escapes\n | \\[\n (\n (?<square> # Named group\n [^\\[\\]\\\\] # Match most chars\n | \\\\. # Escaped chars\n | \\[ \\g<square>*+ \\] # Nested brackets\n )*+\n \\]\n (\n ( # Reference Link\n [ ]? # Optional space\n \\[[^\\]]*+\\] # Ref name\n )\n | ( # Inline Link\n \\( # Opening paren\n [ \\t]*+ # Optional whtiespace\n <?(.*?)>? # URL\n [ \\t]*+ # Optional whtiespace\n ( # Optional Title\n (?<title>['\"])\n (.*?)\n \\k<title>\n )?\n \\)\n )\n )\n )\n | \\1\\1 # Must be bold closer\n | (?!(?<=\\S)\\1). # Everything besides\n # style closer\n )++\n (?<=\\S)(?=_\\b|\\*)\\1 # Close\n )\n", "captures": { "1": { "name": "punctuation.definition.italic.markdown" @@ -2254,6 +2296,9 @@ }, { "include": "#link-ref" + }, + { + "include": "#link-ref-shortcut" } ] }, @@ -2307,32 +2352,32 @@ "7": { "name": "markup.underline.link.markdown" }, - "8": { + "9": { "name": "punctuation.definition.link.markdown" }, - "9": { - "name": "string.other.link.description.title.markdown" - }, "10": { - "name": "punctuation.definition.string.begin.markdown" + "name": "string.other.link.description.title.markdown" }, "11": { - "name": "punctuation.definition.string.end.markdown" - }, - "12": { - "name": "string.other.link.description.title.markdown" - }, - "13": { "name": "punctuation.definition.string.begin.markdown" }, - "14": { + "12": { "name": "punctuation.definition.string.end.markdown" }, + "13": { + "name": "string.other.link.description.title.markdown" + }, + "14": { + "name": "punctuation.definition.string.begin.markdown" + }, "15": { + "name": "punctuation.definition.string.end.markdown" + }, + "16": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (<?)(.*?)(>?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (<?)((?<url>[^\\s()]+|\\(\\g<url>*\\))*)(>?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.link.inline.markdown" }, "link-ref": { @@ -2380,6 +2425,21 @@ "match": "(\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])[ ]?(\\[)(\\])", "name": "meta.link.reference.literal.markdown" }, + "link-ref-shortcut": { + "captures": { + "1": { + "name": "punctuation.definition.string.begin.markdown" + }, + "2": { + "name": "string.other.link.title.markdown" + }, + "4": { + "name": "punctuation.definition.string.end.markdown" + } + }, + "match": "(\\[)(\\S+?)(\\])", + "name": "meta.link.reference.markdown" + }, "raw": { "captures": { "1": { 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.begin.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.code.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -122,7 +122,7 @@ }, { "c": "code", - "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.code.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -133,7 +133,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.begin.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -155,7 +155,7 @@ }, { "c": "pre", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.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.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.begin.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -276,7 +276,7 @@ }, { "c": "pre", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -287,7 +287,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..faa18bc663b 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" } @@ -311,9 +311,9 @@ "c": " html madness ", "t": "text.html.markdown comment.block.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" } @@ -322,16 +322,16 @@ "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.begin.html", + "t": "text.html.markdown meta.tag.structure.div.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -573,7 +573,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -584,7 +584,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.begin.html", + "t": "text.html.markdown 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", @@ -738,7 +738,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -749,7 +749,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.begin.html", + "t": "text.html.markdown meta.tag.inline.b.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -925,7 +903,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -936,7 +914,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.begin.html", + "t": "text.html.markdown meta.tag.structure.div.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1211,7 +1189,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1222,7 +1200,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", @@ -1233,12 +1211,12 @@ }, { "c": "*", - "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1266,12 +1244,12 @@ }, { "c": "-", - "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1299,12 +1277,12 @@ }, { "c": "+", - "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1343,12 +1321,12 @@ }, { "c": "+", - "t": "text.html.markdown markup.list.unnumbered.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1618,12 +1596,12 @@ }, { "c": ">", - "t": "text.html.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", + "t": "text.html.markdown markup.quote.markdown punctuation.definition.quote.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", + "dark_plus": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_plus": "punctuation.definition.quote.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_vs": "punctuation.definition.quote.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1651,23 +1629,23 @@ }, { "c": ">", - "t": "text.html.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", + "t": "text.html.markdown markup.quote.markdown punctuation.definition.quote.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", + "dark_plus": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_plus": "punctuation.definition.quote.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_vs": "punctuation.definition.quote.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, { "c": ">", - "t": "text.html.markdown markup.quote.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", + "t": "text.html.markdown markup.quote.markdown markup.quote.markdown punctuation.definition.quote.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", + "dark_plus": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_plus": "punctuation.definition.quote.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_vs": "punctuation.definition.quote.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1695,12 +1673,12 @@ }, { "c": "1.", - "t": "text.html.markdown markup.list.numbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.numbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1739,12 +1717,12 @@ }, { "c": ">", - "t": "text.html.markdown markup.list.numbered.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", + "t": "text.html.markdown markup.list.numbered.markdown markup.quote.markdown punctuation.definition.quote.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", - "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", + "dark_plus": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_plus": "punctuation.definition.quote.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.quote.begin.markdown: #6A9955", + "light_vs": "punctuation.definition.quote.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1772,12 +1750,12 @@ }, { "c": "2.", - "t": "text.html.markdown markup.list.numbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.numbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -1805,12 +1783,12 @@ }, { "c": "3.", - "t": "text.html.markdown markup.list.numbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.numbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -2267,12 +2245,12 @@ }, { "c": "*", - "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -2300,12 +2278,12 @@ }, { "c": "*", - "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -2443,12 +2421,12 @@ }, { "c": "*", - "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -2476,12 +2454,12 @@ }, { "c": "*", - "t": "text.html.markdown markup.list.unnumbered.markdown beginning.punctuation.definition.list.markdown", + "t": "text.html.markdown markup.list.unnumbered.markdown punctuation.definition.list.begin.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_plus": "beginning.punctuation.definition.list.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.list.markdown: #6796E6", - "light_vs": "beginning.punctuation.definition.list.markdown: #0451A5", + "dark_plus": "punctuation.definition.list.begin.markdown: #6796E6", + "light_plus": "punctuation.definition.list.begin.markdown: #0451A5", + "dark_vs": "punctuation.definition.list.begin.markdown: #6796E6", + "light_vs": "punctuation.definition.list.begin.markdown: #0451A5", "hc_black": "default: #FFFFFF" } }, @@ -2508,7 +2486,51 @@ } }, { - "c": "*[ABBR]: Markdown plus abbreviations (produces an ", + "c": "*", + "t": "text.html.markdown meta.paragraph.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 meta.paragraph.markdown meta.link.reference.markdown punctuation.definition.string.begin.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ABBR", + "t": "text.html.markdown meta.paragraph.markdown meta.link.reference.markdown string.other.link.title.markdown", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "]", + "t": "text.html.markdown meta.paragraph.markdown meta.link.reference.markdown", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ": Markdown plus abbreviations (produces an ", "t": "text.html.markdown meta.paragraph.markdown", "r": { "dark_plus": "default: #D4D4D4", @@ -2520,7 +2542,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 +2553,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 +2564,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..31171b5aef9 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -295,14 +295,14 @@ "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": { "@types/highlight.js": "9.1.10", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "0.0.2", - "@types/node": "7.0.43", + "@types/node": "^8.10.25", "lodash.throttle": "^4.1.1", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", diff --git a/extensions/markdown-language-features/src/commands/showPreview.ts b/extensions/markdown-language-features/src/commands/showPreview.ts index 3c4873ba424..bd59d575bbb 100644 --- a/extensions/markdown-language-features/src/commands/showPreview.ts +++ b/extensions/markdown-language-features/src/commands/showPreview.ts @@ -38,9 +38,10 @@ async function showPreview( return; } + const resourceColumn = (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One; webviewManager.preview(resource, { - resourceColumn: (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One, - previewColumn: previewSettings.sideBySide ? vscode.ViewColumn.Beside : vscode.ViewColumn.Active, + resourceColumn: resourceColumn, + previewColumn: previewSettings.sideBySide ? resourceColumn + 1 : resourceColumn, locked: !!previewSettings.locked }); 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..3874cc32f87 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Token } from 'markdown-it'; import * as vscode from 'vscode'; - import { MarkdownEngine } from '../markdownEngine'; import { TableOfContentsProvider } from '../tableOfContentsProvider'; @@ -16,35 +16,52 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi private readonly engine: MarkdownEngine ) { } + private async getRegions(document: vscode.TextDocument): Promise<vscode.FoldingRange[]> { + + const isStartRegion = (t: string) => /^\s*<!--\s*#?region\b.*-->/.test(t); + const isEndRegion = (t: string) => /^\s*<!--\s*#?endregion\b.*-->/.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<vscode.FoldingRange[]> { - const tocProvider = new TableOfContentsProvider(this.engine, document); - let toc = await tocProvider.getToc(); - if (toc.length > rangeLimit) { - toc = toc.slice(0, rangeLimit); - } - - const foldingRanges = toc.map((entry, startIndex) => { - const start = entry.line; - let end: number | undefined = undefined; - for (let i = startIndex + 1; i < toc.length; ++i) { - if (toc[i].level <= entry.level) { - end = toc[i].line - 1; - if (document.lineAt(end).isEmptyOrWhitespace && end >= start + 1) { - end = end - 1; - } - break; - } - } - return new vscode.FoldingRange( - start, - typeof end === 'number' ? end : document.lineCount - 1); - }); - - - return foldingRanges; + const [regions, sections] = await Promise.all([this.getRegions(document), this.getHeaderFoldingRanges(document)]); + return [...regions, ...sections].slice(0, rangeLimit); } -} \ No newline at end of file + + private async getHeaderFoldingRanges(document: vscode.TextDocument) { + const tocProvider = new TableOfContentsProvider(this.engine, document); + const toc = await tocProvider.getToc(); + return toc.map(entry => { + let endLine = entry.location.range.end.line; + if (document.lineAt(endLine).isEmptyOrWhitespace && endLine >= entry.line + 1) { + endLine = endLine - 1; + } + return new vscode.FoldingRange(entry.line, endLine); + }); + } +} 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<any>, 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/tableOfContentsProvider.ts b/extensions/markdown-language-features/src/tableOfContentsProvider.ts index ff2cd01ad24..944fc750734 100644 --- a/extensions/markdown-language-features/src/tableOfContentsProvider.ts +++ b/extensions/markdown-language-features/src/tableOfContentsProvider.ts @@ -17,6 +17,7 @@ export interface TocEntry { export interface SkinnyTextDocument { readonly uri: vscode.Uri; + readonly lineCount: number; getText(): string; lineAt(line: number): vscode.TextLine; } @@ -61,7 +62,25 @@ export class TableOfContentsProvider { location: new vscode.Location(document.uri, line.range) }); } - return toc; + + // Get full range of section + return toc.map((entry, startIndex): TocEntry => { + let end: number | undefined = undefined; + for (let i = startIndex + 1; i < toc.length; ++i) { + if (toc[i].level <= entry.level) { + end = toc[i].line - 1; + break; + } + } + const endLine = typeof end === 'number' ? end : document.lineCount - 1; + return { + ...entry, + location: new vscode.Location(document.uri, + new vscode.Range( + entry.location.range.start, + new vscode.Position(endLine, document.lineAt(endLine).range.end.character))) + }; + }); } private static getHeaderLevel(markup: string): number { diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 910ed3c9bdf..5a1d0c87880 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -10,7 +10,7 @@ import LinkProvider from '../features/documentLinkProvider'; import { InMemoryDocument } from './inMemoryDocument'; -const testFileName = vscode.Uri.parse('test.md'); +const testFileName = vscode.Uri.file('test.md'); const noopToken = new class implements vscode.CancellationToken { private _onCancellationRequestedEmitter = new vscode.EventEmitter<void>(); diff --git a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts index b87b5cf2ea4..9533f807cf9 100644 --- a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts @@ -11,7 +11,7 @@ import { InMemoryDocument } from './inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; -const testFileName = vscode.Uri.parse('test.md'); +const testFileName = vscode.Uri.file('test.md'); function getSymbolsForFile(fileContents: string) { 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..6489694db46 100644 --- a/extensions/markdown-language-features/src/test/foldingProvider.test.ts +++ b/extensions/markdown-language-features/src/test/foldingProvider.test.ts @@ -11,7 +11,7 @@ import MarkdownFoldingProvider from '../features/foldingProvider'; import { InMemoryDocument } from './inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; -const testFileName = vscode.Uri.parse('test.md'); +const testFileName = vscode.Uri.file('test.md'); suite('markdown.FoldingProvider', () => { test('Should not return anything for empty document', async () => { @@ -78,6 +78,31 @@ y`); assert.strictEqual(firstFold.end, 2); }); + test('Should fold nested <!-- #region --> markers', async () => { + const folds = await getFoldsForDocument(`a +<!-- #region --> +b +<!-- #region hello!--> +b.a +<!-- #endregion --> +b +<!-- #region: foo! --> +b.b +<!-- #endregion: foo --> +b +<!-- #endregion --> +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/src/test/tableOfContentsProvider.test.ts b/extensions/markdown-language-features/src/test/tableOfContentsProvider.test.ts index 6d6f382ae7e..ee103b294ca 100644 --- a/extensions/markdown-language-features/src/test/tableOfContentsProvider.test.ts +++ b/extensions/markdown-language-features/src/test/tableOfContentsProvider.test.ts @@ -11,7 +11,7 @@ import { TableOfContentsProvider } from '../tableOfContentsProvider'; import { InMemoryDocument } from './inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; -const testFileName = vscode.Uri.parse('test.md'); +const testFileName = vscode.Uri.file('test.md'); suite('markdown.TableOfContentsProvider', () => { test('Lookup should not return anything for empty document', async () => { diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts index 2ee12a7979c..f5b3eb0ea4a 100644 --- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts @@ -22,7 +22,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { }); test('Should return symbols from workspace with one markdown file', async () => { - const testFileName = vscode.Uri.parse('test.md'); + const testFileName = vscode.Uri.file('test.md'); const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocumentProvider([ new InMemoryDocument(testFileName, `# header1\nabc\n## header2`) @@ -49,7 +49,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { }); test('Should update results when markdown file changes symbols', async () => { - const testFileName = vscode.Uri.parse('test.md'); + const testFileName = vscode.Uri.file('test.md'); const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([ new InMemoryDocument(testFileName, `# header1`) @@ -68,7 +68,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { }); test('Should remove results when file is deleted', async () => { - const testFileName = vscode.Uri.parse('test.md'); + const testFileName = vscode.Uri.file('test.md'); const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([ new InMemoryDocument(testFileName, `# header1`) @@ -84,7 +84,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { }); test('Should update results when markdown file is created', async () => { - const testFileName = vscode.Uri.parse('test.md'); + const testFileName = vscode.Uri.file('test.md'); const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([ new InMemoryDocument(testFileName, `# header1`) @@ -94,7 +94,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); // Creat file - workspaceFileProvider.createDocument(new InMemoryDocument(vscode.Uri.parse('test2.md'), `# new header\nabc\n## header2`)); + workspaceFileProvider.createDocument(new InMemoryDocument(vscode.Uri.file('test2.md'), `# new header\nabc\n## header2`)); const newSymbols = await provider.provideWorkspaceSymbols(''); assert.strictEqual(newSymbols.length, 3); }); @@ -105,7 +105,7 @@ class InMemoryWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocu private readonly _documents = new Map<string, vscode.TextDocument>(); constructor(documents: vscode.TextDocument[]) { - for( const doc of documents) { + for (const doc of documents) { this._documents.set(doc.fileName, doc); } } diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index c44b9715579..f75c44c634f 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -24,9 +24,9 @@ version "0.0.2" resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660" -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" abbrev@1: version "1.1.1" @@ -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/README.md b/extensions/npm/README.md index 6c8fa19625d..a24a7d69d6e 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -15,11 +15,16 @@ For more information about auto detection of Tasks, see the [documentation](http ### 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. +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 provides code lense actions to run or debug a script 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 diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 434f7ea20b1..2e478c09e5e 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -24,7 +24,7 @@ }, "devDependencies": { "@types/minimatch": "^3.0.3", - "@types/node": "7.0.43" + "@types/node": "^8.10.25" }, "main": "./out/main", "activationEvents": [ @@ -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,13 +191,7 @@ "type": "boolean", "default": false, "scope": "resource", - "description": "%config.npm.runSilent%" - }, - "npm.scriptCodeLens.enable": { - "type": "boolean", - "default": false, - "scope": "resource", - "description": "%config.scriptCodeLens.enable%" + "markdownDescription": "%config.npm.runSilent%" }, "npm.packageManager": { "scope": "resource", @@ -217,9 +226,18 @@ "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" + ] } } }, @@ -252,4 +270,4 @@ } ] } -} \ No newline at end of file +} diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index be9381e3377..4a33bcc0bc3 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -1,13 +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.scriptCodeLens.enable": "Enable the code lens to 'Run' or 'Debug' an npm script.", + "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.", @@ -16,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..300d7dad1f0 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,13 +25,20 @@ 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(xhr: XHRRequest) { + this.xhr = xhr; } public getDocumentSelector(): DocumentSelector { return [{ language: 'json', scheme: '*', pattern: '**/bower.json' }, { language: 'json', scheme: '*', pattern: '**/.bower.json' }]; } + private onlineEnabled() { + return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); + } + public collectDefaultSuggestions(_resource: string, collector: ISuggestionsCollector): Thenable<any> { const defaultValue = { 'name': '${1:name}', @@ -50,7 +57,7 @@ export class BowerJSONContribution implements IJSONContribution { public collectPropertySuggestions(_resource: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, collector: ISuggestionsCollector): Thenable<any> | null { if ((location.matches(['dependencies']) || location.matches(['devDependencies']))) { - if (currentWord.length > 0) { + if (currentWord.length > 0 && this.onlineEnabled()) { const queryUrl = 'https://registry.bower.io/packages/search/' + encodeURIComponent(currentWord); return this.xhr({ @@ -144,6 +151,10 @@ export class BowerJSONContribution implements IJSONContribution { } private getInfo(pack: string): Thenable<string | undefined> { + if (!this.onlineEnabled()) { + return Promise.resolve(undefined); + } + const queryUrl = 'https://registry.bower.io/packages/' + encodeURIComponent(pack); return this.xhr({ 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..38e4ce05310 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,14 @@ 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(xhr: XHRRequest) { + this.xhr = xhr; } public collectDefaultSuggestions(_fileName: string, result: ISuggestionsCollector): Thenable<any> { @@ -52,6 +54,10 @@ export class PackageJSONContribution implements IJSONContribution { return Promise.resolve(null); } + private onlineEnabled() { + return !!workspace.getConfiguration('npm').get('fetchOnlinePackageInfo'); + } + public collectPropertySuggestions( _resource: string, location: Location, @@ -60,6 +66,10 @@ export class PackageJSONContribution implements IJSONContribution { isLast: boolean, collector: ISuggestionsCollector ): Thenable<any> | null { + if (!this.onlineEnabled()) { + return null; + } + if ((location.matches(['dependencies']) || location.matches(['devDependencies']) || location.matches(['optionalDependencies']) || location.matches(['peerDependencies']))) { let queryUrl: string; if (currentWord.length > 0) { @@ -209,6 +219,10 @@ export class PackageJSONContribution implements IJSONContribution { location: Location, result: ISuggestionsCollector ): Thenable<any> | null { + if (!this.onlineEnabled()) { + return null; + } + if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const currentKey = location.path[location.path.length - 1]; if (typeof currentKey === 'string') { diff --git a/extensions/npm/src/lenses.ts b/extensions/npm/src/lenses.ts deleted file mode 100644 index 6394fbad5b3..00000000000 --- a/extensions/npm/src/lenses.ts +++ /dev/null @@ -1,86 +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 { - ExtensionContext, CodeLensProvider, TextDocument, commands, ProviderResult, CodeLens, CancellationToken, - workspace, tasks, Range, Command, Event, EventEmitter -} from 'vscode'; -import { - createTask, startDebugging, findAllScriptRanges, extractDebugArgFromScript -} from './tasks'; -import * as nls from 'vscode-nls'; - -const localize = nls.loadMessageBundle(); - -export class NpmLensProvider implements CodeLensProvider { - private extensionContext: ExtensionContext; - private _onDidChangeCodeLenses: EventEmitter<void> = new EventEmitter<void>(); - readonly onDidChangeCodeLenses: Event<void> = this._onDidChangeCodeLenses.event; - - constructor(context: ExtensionContext) { - this.extensionContext = context; - context.subscriptions.push(commands.registerCommand('npm.runScriptFromLens', this.runScriptFromLens, this)); - context.subscriptions.push(commands.registerCommand('npm.debugScriptFromLens', this.debugScriptFromLens, this)); - } - - public provideCodeLenses(document: TextDocument, _token: CancellationToken): ProviderResult<CodeLens[]> { - let result = findAllScriptRanges(document.getText()); - let folder = workspace.getWorkspaceFolder(document.uri); - let lenses: CodeLens[] = []; - - - if (folder && !workspace.getConfiguration('npm', folder.uri).get<string>('scriptCodeLens.enable', 'true')) { - return lenses; - } - - result.forEach((value, key) => { - let start = document.positionAt(value[0]); - let end = document.positionAt(value[0] + value[1]); - let range = new Range(start, end); - - let command: Command = { - command: 'npm.runScriptFromLens', - title: localize('run', "Run"), - arguments: [document, key] - }; - let lens: CodeLens = new CodeLens(range, command); - lenses.push(lens); - - let debugArgs = extractDebugArgFromScript(value[2]); - if (debugArgs) { - command = { - command: 'npm.debugScriptFromLens', - title: localize('debug', "Debug"), - arguments: [document, key, debugArgs[0], debugArgs[1]] - }; - lens = new CodeLens(range, command); - lenses.push(lens); - } - }); - return lenses; - } - - public refresh() { - this._onDidChangeCodeLenses.fire(); - } - - public runScriptFromLens(document: TextDocument, script: string) { - let uri = document.uri; - let folder = workspace.getWorkspaceFolder(uri); - if (folder) { - let task = createTask(script, `run ${script}`, folder, uri); - tasks.executeTask(task); - } - } - - public debugScriptFromLens(document: TextDocument, script: string, protocol: string, port: number) { - let uri = document.uri; - let folder = workspace.getWorkspaceFolder(uri); - if (folder) { - startDebugging(script, protocol, port, folder); - } - } -} diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index f2c80f5f476..02e22490fd7 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -6,22 +6,22 @@ import * as httpRequest from 'request-light'; import * as vscode from 'vscode'; - import { addJSONProviders } from './features/jsonContributions'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { invalidateScriptsCache, NpmTaskProvider } from './tasks'; -import { NpmLensProvider } from './lenses'; +import { invalidateTasksCache, NpmTaskProvider } from './tasks'; +import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; +import { runSelectedScript } from './commands'; export async function activate(context: vscode.ExtensionContext): Promise<void> { const taskProvider = registerTaskProvider(context); const treeDataProvider = registerExplorer(context); - const lensProvider = registerLensProvider(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,21 +31,29 @@ export async function activate(context: vscode.ExtensionContext): Promise<void> treeDataProvider.refresh(); } } - if (e.affectsConfiguration('npm.scriptCodeLens.enable')) { - if (lensProvider) { - lensProvider.refresh(); - } - } }); + 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 = new NpmTaskProvider(context); @@ -66,15 +74,15 @@ function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataP return undefined; } -function registerLensProvider(context: vscode.ExtensionContext): NpmLensProvider | 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 NpmLensProvider(context); - context.subscriptions.push(vscode.languages.registerCodeLensProvider(npmSelector, provider)); + let provider = new NpmScriptHoverProvider(context); + context.subscriptions.push(vscode.languages.registerHoverProvider(npmSelector, provider)); return provider; } return undefined; 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<string, [number, number, string]> | 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<Hover> { + 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 5d19a4c4c00..189fd98baec 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -6,7 +6,7 @@ import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, - DebugConfiguration, debug, TaskProvider, ExtensionContext + DebugConfiguration, debug, TaskProvider, ExtensionContext, TextDocument, tasks } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; @@ -41,7 +41,7 @@ export class NpmTaskProvider implements TaskProvider { } } -export function invalidateScriptsCache() { +export function invalidateTasksCache() { cachedTasks = undefined; } @@ -288,9 +288,18 @@ async function readFile(file: string): Promise<string> { }); } +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, + // 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+))?/); @@ -321,7 +330,7 @@ export function startDebugging(scriptName: string, protocol: string, port: numbe name: `Debug ${scriptName}`, runtimeExecutable: packageManager, runtimeArgs: [ - 'run-script', + 'run', scriptName, ], port: port, @@ -371,6 +380,9 @@ async function findAllScripts(buffer: string): Promise<StringMap> { export function findAllScriptRanges(buffer: string): Map<string, [number, number, string]> { var scripts: Map<string, [number, number, string]> = new Map(); let script: string | undefined = undefined; + let offset: number; + let length: number; + let inScripts = false; let visitor: JSONVisitor = { @@ -381,18 +393,20 @@ export function findAllScriptRanges(buffer: string): Map<string, [number, number inScripts = false; } }, - onLiteralValue(value: any, offset: number, length: number) { + onLiteralValue(value: any, _offset: number, _length: number) { if (script) { scripts.set(script, [offset, length, value]); script = undefined; } }, - onObjectProperty(property: string, offset: number, length: number) { + onObjectProperty(property: string, off: number, len: number) { if (property === 'scripts') { inScripts = true; } else if (inScripts) { script = property; + offset = off; + length = len; } } }; @@ -400,6 +414,42 @@ export function findAllScriptRanges(buffer: string): Map<string, [number, number 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<StringMap | undefined> { diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index 9822a92a784..395adb4e496 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -6,9 +6,9 @@ version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" agent-base@4, agent-base@^4.1.0: version "4.2.0" 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 1c3a19a4f02..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.20180723" + "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.json b/extensions/php-language-features/package.json index e39073271f3..29ebe03b9b1 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -80,6 +80,6 @@ "vscode-nls": "^3.2.4" }, "devDependencies": { - "@types/node": "7.0.43" + "@types/node": "^8.10.25" } } 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-language-features/src/features/validationProvider.ts b/extensions/php-language-features/src/features/validationProvider.ts index d139fde1121..c3c513d3220 100644 --- a/extensions/php-language-features/src/features/validationProvider.ts +++ b/extensions/php-language-features/src/features/validationProvider.ts @@ -23,7 +23,7 @@ export class LineDecoder { this.remaining = null; } - public write(buffer: NodeBuffer): string[] { + public write(buffer: Buffer): string[] { var result: string[] = []; var value = this.remaining ? this.remaining + this.stringDecoder.write(buffer) diff --git a/extensions/php-language-features/yarn.lock b/extensions/php-language-features/yarn.lock index 573098d7d26..fe244123db7 100644 --- a/extensions/php-language-features/yarn.lock +++ b/extensions/php-language-features/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" vscode-nls@^3.2.4: version "3.2.4" 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.begin.html", + "t": "text.html.php meta.tag.metadata.title.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -133,7 +133,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -144,7 +144,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.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -166,7 +166,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -177,7 +177,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.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3576,7 +3576,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3587,7 +3587,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.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3609,7 +3609,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3620,7 +3620,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..60dee137b19 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/6f5438611c54922ea94c81532a2dcfee72190039", "name": "PowerShell", "scopeName": "source.powershell", "patterns": [ @@ -71,17 +71,7 @@ }, { "begin": "(?<!')'", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.powershell" - } - }, "end": "'(?!')", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.powershell" - } - }, "name": "string.quoted.single.powershell", "patterns": [ { @@ -147,7 +137,7 @@ "begin": "(\\$)(\\()", "beginCaptures": { "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "punctuation.section.group.begin.powershell" @@ -180,20 +170,8 @@ "name": "keyword.control.powershell" }, { - "begin": "(?<!\\w)(--%)(?!\\w)", - "beginCaptures": { - "1": { - "name": "keyword.control.powershell" - } - }, - "end": "$", - "patterns": [ - { - "match": ".+", - "name": "string.unquoted.powershell" - } - ], - "comment": "This should be moved to the repository at some point." + "match": "(?<!\\w)(--%)(?!\\w)", + "name": "keyword.control.powershell" }, { "comment": "This should only be relevant inside a class but will require a rework of how classes are matched. This is a temp fix.", @@ -242,7 +220,7 @@ }, { "match": "\\|{2}|&{2}|;", - "name": "punctuation.terminator.statement.powershell" + "name": "keyword.other.statement-separator.powershell" }, { "match": "&|(?<!\\w)\\.(?= )|`|,|\\|", @@ -348,17 +326,7 @@ }, { "begin": "(?<!')'", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.powershell" - } - }, "end": "'(?!')", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.powershell" - } - }, "name": "string.quoted.single.powershell", "patterns": [ { @@ -411,7 +379,7 @@ "name": "keyword.operator.documentation.powershell" } }, - "match": "^(?i:(?:\\s*|#)+(\\.)(COMPONENT|DESCRIPTION|EXAMPLE|EXTERNALHELP|FORWARDHELPCATEGORY|FORWARDHELPTARGETNAME|FUNCTIONALITY|INPUTS|LINK|NOTES|OUTPUTS|REMOTEHELPRUNSPACE|ROLE|SYNOPSIS))", + "match": "(?i:\\s*(\\.)(COMPONENT|DESCRIPTION|EXAMPLE|EXTERNALHELP|FORWARDHELPCATEGORY|FORWARDHELPTARGETNAME|FUNCTIONALITY|INPUTS|LINK|NOTES|OUTPUTS|REMOTEHELPRUNSPACE|ROLE|SYNOPSIS))", "name": "comment.documentation.embedded.powershell" }, { @@ -496,7 +464,7 @@ "begin": "(\\$)(\\()", "beginCaptures": { "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "punctuation.section.group.begin.powershell" @@ -649,11 +617,11 @@ "patterns": [ { "captures": { - "0": { - "name": "constant.language.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" + }, + "2": { + "name": "constant.language.powershell" } }, "comment": "These are special constants.", @@ -661,14 +629,14 @@ }, { "captures": { - "0": { + "1": { + "name": "keyword.other.variable.definition.powershell" + }, + "2": { "name": "support.constant.variable.powershell" }, - "1": { - "name": "punctuation.definition.variable.powershell" - }, "3": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "comment": "These are the other built-in constants.", @@ -676,14 +644,14 @@ }, { "captures": { - "0": { + "1": { + "name": "keyword.other.variable.definition.powershell" + }, + "2": { "name": "support.constant.automatic.powershell" }, - "1": { - "name": "punctuation.definition.variable.powershell" - }, "3": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "comment": "Automatic variables are not constants, but they are read-only. In monokai (default) color schema support.variable doesn't have color, so we use constant.", @@ -691,14 +659,14 @@ }, { "captures": { - "0": { + "1": { + "name": "keyword.other.variable.definition.powershell" + }, + "2": { "name": "variable.language.powershell" }, - "1": { - "name": "punctuation.definition.variable.powershell" - }, "3": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "comment": "Style preference variables as language variables so that they stand out.", @@ -706,28 +674,25 @@ }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "storage.modifier.scope.powershell" }, + "3": { + "name": "variable.other.readwrite.powershell" + }, "4": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "match": "(?i:(\\$|@)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?" }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "punctuation.section.braces.begin.powershell" @@ -735,39 +700,39 @@ "3": { "name": "storage.modifier.scope.powershell" }, + "4": { + "name": "variable.other.readwrite.powershell" + }, "5": { "name": "punctuation.section.braces.end.powershell" }, "6": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "match": "(?i:(\\$)(\\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\\}))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?" }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "support.variable.drive.powershell" }, + "3": { + "name": "variable.other.readwrite.powershell" + }, "4": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "match": "(?i:(\\$|@)((?:\\p{L}|\\d|_)+:)?((?:\\p{L}|\\d|_)+))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?" }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "punctuation.section.braces.begin.powershell" @@ -775,11 +740,14 @@ "3": { "name": "support.variable.drive.powershell" }, + "4": { + "name": "variable.other.readwrite.powershell" + }, "5": { "name": "punctuation.section.braces.end.powershell" }, "6": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "match": "(?i:(\\$)(\\{)((?:\\p{L}|\\d|_)+:)?([^}]*[^}`])(\\}))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?" @@ -827,11 +795,11 @@ "patterns": [ { "captures": { - "0": { - "name": "constant.language.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" + }, + "2": { + "name": "constant.language.powershell" } }, "comment": "These are special constants.", @@ -839,14 +807,14 @@ }, { "captures": { - "0": { + "1": { + "name": "keyword.other.variable.definition.powershell" + }, + "2": { "name": "support.constant.variable.powershell" }, - "1": { - "name": "punctuation.definition.variable.powershell" - }, "3": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "comment": "These are the other built-in constants.", @@ -854,14 +822,14 @@ }, { "captures": { - "0": { + "1": { + "name": "keyword.other.variable.definition.powershell" + }, + "2": { "name": "support.variable.automatic.powershell" }, - "1": { - "name": "punctuation.definition.variable.powershell" - }, "3": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "comment": "Automatic variables are not constants, but they are read-only...", @@ -869,14 +837,14 @@ }, { "captures": { - "0": { + "1": { + "name": "keyword.other.variable.definition.powershell" + }, + "2": { "name": "variable.language.powershell" }, - "1": { - "name": "punctuation.definition.variable.powershell" - }, "3": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "comment": "Style preference variables as language variables so that they stand out.", @@ -884,65 +852,62 @@ }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "storage.modifier.scope.powershell" }, + "3": { + "name": "variable.other.readwrite.powershell" + }, "4": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "match": "(?i:(\\$|@)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))" }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "storage.modifier.scope.powershell" }, + "3": { + "name": "variable.other.readwrite.powershell" + }, "4": { "name": "keyword.other.powershell" }, "5": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "match": "(?i:(\\$)(\\{)(global|local|private|script|using|workflow):([^}]*[^}`])(\\}))" }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "support.variable.drive.powershell" }, + "3": { + "name": "variable.other.readwrite.powershell" + }, "4": { - "name": "variable.other.member.powershell" + "name": "entity.name.function.invocation.powershell" } }, "match": "(?i:(\\$)((?:\\p{L}|\\d|_)+:)?((?:\\p{L}|\\d|_)+))" }, { "captures": { - "0": { - "name": "variable.other.readwrite.powershell" - }, "1": { - "name": "punctuation.definition.variable.powershell" + "name": "keyword.other.variable.definition.powershell" }, "2": { "name": "punctuation.section.braces.begin" @@ -950,6 +915,9 @@ "3": { "name": "support.variable.drive.powershell" }, + "4": { + "name": "variable.other.readwrite.powershell" + }, "5": { "name": "punctuation.section.braces.end" } @@ -1004,17 +972,7 @@ }, "doubleQuotedString": { "begin": "(?<!(?<!`)\")\"", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.powershell" - } - }, "end": "\"(?!\")", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.powershell" - } - }, "name": "string.quoted.double.powershell", "patterns": [ { diff --git a/extensions/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json index 15d677e1b95..b09a52ebc66 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" } @@ -155,13 +155,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -287,13 +287,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -386,13 +386,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -441,13 +441,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -463,13 +463,13 @@ }, { "c": ".IsInRole", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell variable.other.readwrite.powershell variable.other.member.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell entity.name.function.invocation.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "hc_black": "entity.name.function: #DCDCAA" } }, { @@ -649,18 +649,7 @@ } }, { - "c": "\"", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell string.quoted.double.powershell punctuation.definition.string.begin.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "Failed to determine if the current user has elevated privileges. The error was: '{0}'.", + "c": "\"Failed to determine if the current user has elevated privileges. The error was: '{0}'.\"", "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell string.quoted.double.powershell", "r": { "dark_plus": "string: #CE9178", @@ -670,17 +659,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "\"", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell string.quoted.double.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": " ", "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell", @@ -716,13 +694,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -1013,13 +991,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -1090,13 +1068,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -1167,7 +1145,7 @@ }, { "c": "\"", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.double.powershell punctuation.definition.string.begin.powershell", + "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.double.powershell", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1178,13 +1156,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.double.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.double.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -1199,7 +1177,7 @@ } }, { - "c": " 2>&1 & set", + "c": " 2>&1 & set\"", "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.double.powershell", "r": { "dark_plus": "string: #CE9178", @@ -1209,17 +1187,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "\"", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.double.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": ")", "t": "source.powershell meta.scriptblock.powershell punctuation.section.group.end.powershell", @@ -1299,13 +1266,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -1353,18 +1320,7 @@ } }, { - "c": "'", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.single.powershell punctuation.definition.string.begin.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "^([^=]+)=(.*)", + "c": "'^([^=]+)=(.*)'", "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.single.powershell", "r": { "dark_plus": "string: #CE9178", @@ -1374,17 +1330,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "'", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell string.quoted.single.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": ")", "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell punctuation.section.group.end.powershell", @@ -1486,13 +1431,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -1563,13 +1508,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -1727,18 +1672,7 @@ } }, { - "c": "'", - "t": "source.powershell string.quoted.single.powershell punctuation.definition.string.begin.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "Initializing Azure PowerShell environment...", + "c": "'Initializing Azure PowerShell environment...'", "t": "source.powershell string.quoted.single.powershell", "r": { "dark_plus": "string: #CE9178", @@ -1748,35 +1682,24 @@ "hc_black": "string: #CE9178" } }, - { - "c": "'", - "t": "source.powershell string.quoted.single.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": ";", - "t": "source.powershell punctuation.terminator.statement.powershell", + "t": "source.powershell keyword.other.statement-separator.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { "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 +1708,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" } @@ -1947,18 +1870,7 @@ } }, { - "c": "'", - "t": "source.powershell meta.scriptblock.powershell string.quoted.single.powershell punctuation.definition.string.begin.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "Please launch command under administrator account. It is needed for environment setting up and unit test.", + "c": "'Please launch command under administrator account. It is needed for environment setting up and unit test.'", "t": "source.powershell meta.scriptblock.powershell string.quoted.single.powershell", "r": { "dark_plus": "string: #CE9178", @@ -1968,17 +1880,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "'", - "t": "source.powershell meta.scriptblock.powershell string.quoted.single.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": " ", "t": "source.powershell meta.scriptblock.powershell", @@ -2014,13 +1915,13 @@ }, { "c": ";", - "t": "source.powershell meta.scriptblock.powershell punctuation.terminator.statement.powershell", + "t": "source.powershell meta.scriptblock.powershell keyword.other.statement-separator.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2036,18 +1937,18 @@ }, { "c": "$", - "t": "source.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { "c": "env:", - "t": "source.powershell variable.other.readwrite.powershell support.variable.drive.powershell", + "t": "source.powershell support.variable.drive.powershell", "r": { "dark_plus": "support.variable: #9CDCFE", "light_plus": "support.variable: #001080", @@ -2168,18 +2069,18 @@ }, { "c": "$", - "t": "source.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { "c": "env:", - "t": "source.powershell variable.other.readwrite.powershell support.variable.drive.powershell", + "t": "source.powershell support.variable.drive.powershell", "r": { "dark_plus": "support.variable: #9CDCFE", "light_plus": "support.variable: #001080", @@ -2201,13 +2102,13 @@ }, { "c": ";", - "t": "source.powershell punctuation.terminator.statement.powershell", + "t": "source.powershell keyword.other.statement-separator.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2289,7 +2190,7 @@ }, { "c": "\"", - "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell punctuation.definition.string.begin.powershell", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2300,18 +2201,18 @@ }, { "c": "$", - "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { "c": "env:", - "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell variable.other.readwrite.powershell support.variable.drive.powershell", + "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell support.variable.drive.powershell", "r": { "dark_plus": "support.variable: #9CDCFE", "light_plus": "support.variable: #001080", @@ -2332,7 +2233,7 @@ } }, { - "c": "\\Microsoft Visual Studio 12.0", + "c": "\\Microsoft Visual Studio 12.0\"", "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell", "r": { "dark_plus": "string: #CE9178", @@ -2342,17 +2243,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "\"", - "t": "source.powershell interpolated.simple.source.powershell string.quoted.double.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": ")", "t": "source.powershell punctuation.section.group.end.powershell", @@ -2399,13 +2289,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2431,18 +2321,7 @@ } }, { - "c": "\"", - "t": "source.powershell meta.scriptblock.powershell string.quoted.double.powershell punctuation.definition.string.begin.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "12.0", + "c": "\"12.0\"", "t": "source.powershell meta.scriptblock.powershell string.quoted.double.powershell", "r": { "dark_plus": "string: #CE9178", @@ -2452,17 +2331,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "\"", - "t": "source.powershell meta.scriptblock.powershell string.quoted.double.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": "}", "t": "source.powershell meta.scriptblock.powershell punctuation.section.braces.end.powershell", @@ -2531,13 +2399,13 @@ }, { "c": "$", - "t": "source.powershell meta.scriptblock.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell meta.scriptblock.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2563,18 +2431,7 @@ } }, { - "c": "\"", - "t": "source.powershell meta.scriptblock.powershell string.quoted.double.powershell punctuation.definition.string.begin.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "11.0", + "c": "\"11.0\"", "t": "source.powershell meta.scriptblock.powershell string.quoted.double.powershell", "r": { "dark_plus": "string: #CE9178", @@ -2584,17 +2441,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "\"", - "t": "source.powershell meta.scriptblock.powershell string.quoted.double.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": "}", "t": "source.powershell meta.scriptblock.powershell punctuation.section.braces.end.powershell", @@ -2608,13 +2454,13 @@ }, { "c": "$", - "t": "source.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2662,18 +2508,7 @@ } }, { - "c": "'", - "t": "source.powershell string.quoted.single.powershell punctuation.definition.string.begin.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\"{0}\\Microsoft Visual Studio {1}\\VC\\vcvarsall.bat\" x64", + "c": "'\"{0}\\Microsoft Visual Studio {1}\\VC\\vcvarsall.bat\" x64'", "t": "source.powershell string.quoted.single.powershell", "r": { "dark_plus": "string: #CE9178", @@ -2683,17 +2518,6 @@ "hc_black": "string: #CE9178" } }, - { - "c": "'", - "t": "source.powershell string.quoted.single.powershell punctuation.definition.string.end.powershell", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, { "c": " ", "t": "source.powershell", @@ -2729,18 +2553,18 @@ }, { "c": "$", - "t": "source.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { "c": "env:", - "t": "source.powershell variable.other.readwrite.powershell support.variable.drive.powershell", + "t": "source.powershell support.variable.drive.powershell", "r": { "dark_plus": "support.variable: #9CDCFE", "light_plus": "support.variable: #001080", @@ -2784,13 +2608,13 @@ }, { "c": "$", - "t": "source.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2806,13 +2630,13 @@ }, { "c": ";", - "t": "source.powershell punctuation.terminator.statement.powershell", + "t": "source.powershell keyword.other.statement-separator.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2861,13 +2685,13 @@ }, { "c": "$", - "t": "source.powershell variable.other.readwrite.powershell punctuation.definition.variable.powershell", + "t": "source.powershell keyword.other.variable.definition.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } }, { @@ -2883,13 +2707,13 @@ }, { "c": ";", - "t": "source.powershell punctuation.terminator.statement.powershell", + "t": "source.powershell keyword.other.statement-separator.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" } } ] \ No newline at end of file diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index fc32d74cec5..44a995bc046 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/fb56c6a98d684e30bed1b0f9647e85741a48f914", + "version": "https://github.com/MagicStack/MagicPython/commit/b453f26ed856c9b16a053517c41207e3a72cc7d5", "name": "MagicPython", "scopeName": "source.python", "patterns": [ @@ -97,8 +97,7 @@ }, "docstring-statement": { "begin": "^(?=\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))", - "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]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))", + "end": "(?<=\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\")", "patterns": [ { "include": "#docstring" @@ -165,7 +164,7 @@ { "name": "string.quoted.docstring.single.python", "begin": "(\\'|\\\")", - "end": "(\\1)|(\\n)", + "end": "(\\1)|((?<!\\\\)\\n)", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.python" @@ -191,7 +190,7 @@ { "name": "string.quoted.docstring.raw.single.python", "begin": "([rR])(\\'|\\\")", - "end": "(\\2)|(\\n)", + "end": "(\\2)|((?<!\\\\)\\n)", "beginCaptures": { "1": { "name": "storage.type.string.python" @@ -848,45 +847,40 @@ "match": "\\\\$" }, "string-formatting": { - "name": "meta.format.percent.python", - "match": "(?x)\n (\n % (\\([\\w\\s]*\\))?\n [-+#0 ]*\n (\\d+|\\*)? (\\.(\\d+|\\*))?\n ([hlL])?\n [diouxXeEfFgGcrsa%]\n )\n", - "captures": { - "1": { - "name": "constant.character.format.placeholder.other.python" - } - } + "name": "constant.character.format.placeholder.other.python", + "match": "(?x)\n % (\\([\\w\\s]*\\))?\n [-+#0 ]*\n (\\d+|\\*)? (\\.(\\d+|\\*))?\n ([hlL])?\n [diouxXeEfFgGcrsa%]\n" }, "string-brace-formatting": { "patterns": [ { - "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", + "name": "constant.character.format.placeholder.other.python", + "match": "(?x)\n (?:\n {{ | }}\n | (?:\n {\n \\w*? (\\.[[:alpha:]_]\\w*? | \\[[^\\]'\"]+\\])*?\n (![rsa])?\n ( : \\w? [<>=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )?\n })\n )\n", "captures": { - "1": { - "name": "constant.character.format.placeholder.other.python" - }, - "3": { + "2": { "name": "storage.type.format.python" }, - "4": { + "3": { "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" + "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" }, "3": { "name": "storage.type.format.python" - }, - "4": { - "name": "storage.type.format.python" } - } + }, + "patterns": [ + { + "match": "(?x) \\{ [^'\"}\\n]*? \\} (?=.*?\\})\n" + } + ] } ] }, @@ -4543,7 +4537,7 @@ }, "string-quoted-single-line": { "name": "string.quoted.single.python", - "begin": "(?:\\b([rR])(?=[uU]))?([uU])?((['\"]))", + "begin": "(\\b[rR](?=[uU]))?([uU])?((['\"]))", "end": "(\\3)|((?<!\\\\)\\n)", "beginCaptures": { "1": { @@ -4717,7 +4711,7 @@ }, "string-quoted-multi-line": { "name": "string.quoted.multi.python", - "begin": "(?:\\b([rR])(?=[uU]))?([uU])?('''|\"\"\")", + "begin": "(\\b[rR](?=[uU]))?([uU])?('''|\"\"\")", "end": "(\\3)", "beginCaptures": { "1": { diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json index 235958f078f..21d185717d4 100644 --- a/extensions/python/test/colorize-results/test_py.json +++ b/extensions/python/test/colorize-results/test_py.json @@ -113,9 +113,9 @@ "c": "#", "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "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": " Bananas the monkey can eat.", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1906,9 +1906,9 @@ "c": "#", "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1917,9 +1917,9 @@ "c": " Looks like a valid date", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3017,9 +3017,9 @@ "c": "#", "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3028,9 +3028,9 @@ "c": "comment", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3149,9 +3149,9 @@ "c": "#", "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3160,9 +3160,9 @@ "c": "sqadsad", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "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.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6119,9 +6119,9 @@ "c": " Comments in dictionary items should be colorized accordingly", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "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.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "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": " this should be colorized as comment", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6383,9 +6383,9 @@ "c": "#", "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6394,9 +6394,9 @@ "c": "this should be colorized as comment", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6416,9 +6416,9 @@ "c": "#", "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6427,9 +6427,9 @@ "c": " test raw strings", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6581,9 +6581,9 @@ "c": "#", "t": "source.python comment.line.number-sign.python punctuation.definition.comment.python", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6592,9 +6592,9 @@ "c": " highlight doctests", "t": "source.python comment.line.number-sign.python", "r": { - "dark_plus": "comment: #608B4E", + "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/r/test/colorize-results/test_r.json b/extensions/r/test/colorize-results/test_r.json index d9dfb68a0e5..2cba70d079b 100644 --- a/extensions/r/test/colorize-results/test_r.json +++ b/extensions/r/test/colorize-results/test_r.json @@ -3,9 +3,9 @@ "c": "#", "t": "source.r comment.line.number-sign.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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": " © Microsoft. All rights reserved.", "t": "source.r comment.line.number-sign.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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": " Add together two numbers.", "t": "source.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -104,7 +104,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" } @@ -113,9 +113,9 @@ "c": " A number.", "t": "source.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -170,7 +170,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" } @@ -179,9 +179,9 @@ "c": " A number.", "t": "source.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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": " The sum of \\code{x} and \\code{y}.", "t": "source.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "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": " add(1, 1)", "t": "source.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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": "#'", "t": "source.r comment.line.roxygen.r punctuation.definition.comment.r", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -300,9 +300,9 @@ "c": " add(10, 1)", "t": "source.r comment.line.roxygen.r", "r": { - "dark_plus": "comment: #608B4E", + "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/razor/test/colorize-results/test_cshtml.json b/extensions/razor/test/colorize-results/test_cshtml.json index a0988149bbf..37e1a3d66d1 100644 --- a/extensions/razor/test/colorize-results/test_cshtml.json +++ b/extensions/razor/test/colorize-results/test_cshtml.json @@ -531,9 +531,9 @@ "c": "//", "t": "text.html.cshtml 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" } @@ -542,9 +542,9 @@ "c": " Retrieve the numbers that the user entered.", "t": "text.html.cshtml 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" } @@ -872,9 +872,9 @@ "c": "//", "t": "text.html.cshtml 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" } @@ -883,9 +883,9 @@ "c": " Convert the entered strings into integers numbers and add.", "t": "text.html.cshtml 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" } @@ -1409,7 +1409,7 @@ }, { "c": "<!", - "t": "text.html.cshtml meta.tag.sgml.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.metadata.doctype.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1419,41 +1419,8 @@ } }, { - "c": "DOCTYPE html", - "t": "text.html.cshtml meta.tag.sgml.html meta.tag.sgml.doctype.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.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.begin.html", + "t": "text.html.cshtml meta.tag.metadata.title.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1651,7 +1673,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1662,7 +1684,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.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1805,7 +1838,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1816,7 +1849,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.begin.html", + "t": "text.html.cshtml meta.tag.inline.strong.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1970,7 +2003,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1981,7 +2014,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2014,7 +2047,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2025,7 +2058,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.label.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2366,7 +2399,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2377,7 +2410,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2586,7 +2630,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2597,7 +2641,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.label.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2773,7 +2817,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2784,7 +2828,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2993,7 +3048,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3004,7 +3059,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3235,7 +3301,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3246,7 +3312,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.form.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3279,7 +3345,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3290,7 +3356,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.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3521,7 +3587,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3532,7 +3598,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.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3576,7 +3642,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.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3587,7 +3653,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.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3609,7 +3675,7 @@ }, { "c": "html", - "t": "text.html.cshtml meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.cshtml meta.tag.structure.html.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3620,7 +3686,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/rust/language-configuration.json b/extensions/rust/language-configuration.json index 34396c3121e..689f90d77ef 100644 --- a/extensions/rust/language-configuration.json +++ b/extensions/rust/language-configuration.json @@ -20,5 +20,11 @@ ["(", ")"], ["\"", "\""], ["'", "'"] - ] + ], + "folding": { + "markers": { + "start": "^\\s*// region", + "end": "^\\s*// endregion" + } + } } 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 d798d7b2842..a967105f855 100644 --- a/extensions/search-rg/package.json +++ b/extensions/search-rg/package.json @@ -13,9 +13,9 @@ }, "categories": [], "dependencies": { - "vscode-extension-telemetry": "0.0.15", + "vscode-extension-telemetry": "0.0.18", "vscode-nls": "^3.2.4", - "vscode-ripgrep": "^1.0.1" + "vscode-ripgrep": "1.1.0" }, "devDependencies": { "@types/node": "8.0.33", 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<vscode.Uri>; - token: vscode.CancellationToken; -} - -export interface IInternalFileSearchProvider { - provideFileSearchResults(options: vscode.FileSearchOptions, progress: vscode.Progress<string>, token: vscode.CancellationToken): Thenable<void>; -} - -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<vscode.Uri>, token: vscode.CancellationToken): Thenable<void> { - 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<IInternalFileMatch[]> { - const allResultsPromise = new Promise<IInternalFileMatch[]>((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<IInternalFileMatch[]> { - 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<IInternalFileMatch[]> { - // 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<IInternalFileMatch[]> { - // Find cache entries by prefix of search value - const hasPathSep = searchValue.indexOf(path.sep) >= 0; - let cached: CacheEntry<IInternalFileMatch>; - 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<void> { - return new Promise<void>((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<void> { - delete this.caches[cacheKey]; - return Promise.resolve(undefined); - } -} - -interface IInternalFileMatch { - relativePath?: string; // Not present for extraFiles or absolute path matches - basename: string; -} - -interface CacheEntry<T> { - finished: Promise<T[]>; -} - -class Cache { - public resultsToSearchCache: { [searchValue: string]: CacheEntry<IInternalFileMatch> } = Object.create(null); - public scorerCache: ScorerCache = Object.create(null); -} - -const FileMatchItemAccessor = new class implements IItemAccessor<IInternalFileMatch> { - - 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<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number): Promise<T[]> { - // 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<T>(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<T>(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<T> { - - /** - * 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<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, 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<T>(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, 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<T>(item: T, score: IItemScore, accessor: IItemAccessor<T>): 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<T>(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor<T>): 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..9c725266796 100644 --- a/extensions/search-rg/src/extension.ts +++ b/extensions/search-rg/src/extension.ts @@ -4,26 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { RipgrepTextSearchEngine } from './ripgrepTextSearch'; import { RipgrepFileSearchEngine } from './ripgrepFileSearch'; -import { CachedSearchProvider } from './cachedSearchProvider'; +import { RipgrepTextSearchEngine } from './ripgrepTextSearch'; 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<SearchEngine> = new Set(); constructor(private outputChannel: vscode.OutputChannel) { - this.cachedProvider = new CachedSearchProvider(); process.once('exit', () => this.dispose()); } @@ -32,13 +31,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<vscode.Uri>, token: vscode.CancellationToken): Thenable<void> { + provideFileIndex(options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable<vscode.Uri[]> { 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(vscode.Uri.file(options.folder.fsPath + '/' + relativePathMatch)); + }; + + return this.withEngine(engine, () => engine.provideFileSearchResults(options, { report: onResult }, token)) + .then(() => results); } private withEngine(engine: SearchEngine, fn: () => Thenable<void>): Thenable<void> { 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/ripgrepTextSearch.ts b/extensions/search-rg/src/ripgrepTextSearch.ts index 728365bff17..6853cec9deb 100644 --- a/extensions/search-rg/src/ripgrepTextSearch.ts +++ b/extensions/search-rg/src/ripgrepTextSearch.ts @@ -11,7 +11,7 @@ import * as path from 'path'; import { NodeStringDecoder, StringDecoder } from 'string_decoder'; import * as vscode from 'vscode'; import { rgPath } from './ripgrep'; -import { anchorGlob } from './utils'; +import { anchorGlob, createTextSearchResult } from './utils'; // 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'); @@ -67,7 +67,7 @@ export class RipgrepTextSearchEngine { }); let gotResult = false; - this.ripgrepParser = new RipgrepParser(MAX_TEXT_RESULTS, cwd); + this.ripgrepParser = new RipgrepParser(MAX_TEXT_RESULTS, cwd, options.previewOptions); this.ripgrepParser.on('result', (match: vscode.TextSearchResult) => { gotResult = true; progress.report(match); @@ -160,7 +160,7 @@ export class RipgrepParser extends EventEmitter { private numResults = 0; - constructor(private maxResults: number, private rootFolder: string) { + constructor(private maxResults: number, private rootFolder: string, private previewOptions?: vscode.TextSearchPreviewOptions) { super(); this.stringDecoder = new StringDecoder(); } @@ -289,19 +289,11 @@ export class RipgrepParser extends EventEmitter { realTextParts.push(chunk); // Get full real text line without color codes - const preview = realTextParts.join(''); + const previewText = realTextParts.join(''); + const uri = vscode.Uri.file(path.join(this.rootFolder, this.currentFile)); lineMatches - .map(range => { - return <vscode.TextSearchResult>{ - uri: vscode.Uri.file(path.join(this.rootFolder, this.currentFile)), - range, - preview: { - text: preview, - match: new vscode.Range(0, range.start.character, 0, range.end.character) - } - }; - }) + .map(range => createTextSearchResult(uri, previewText, range, this.previewOptions)) .forEach(match => this.onResult(match)); if (hitLimit) { @@ -366,6 +358,7 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti } args.push('--no-config'); + args.push('--no-ignore-global'); // Folder to search args.push('--'); diff --git a/extensions/search-rg/src/test/searchrg.test.ts b/extensions/search-rg/src/test/searchrg.test.ts new file mode 100644 index 00000000000..3407245bb1a --- /dev/null +++ b/extensions/search-rg/src/test/searchrg.test.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'mocha'; +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import * as path from 'path'; +import { createTextSearchResult } from '../utils'; + +function createOneLineRange(lineNumber: number, startCol: number, endCol: number): vscode.Range { + return new vscode.Range(lineNumber, startCol, lineNumber, endCol); +} + +const uri = vscode.Uri.file('/foo/bar'); + +suite('search-rg', () => { + const previewOptions1: vscode.TextSearchPreviewOptions = { + leadingChars: 10, + maxLines: 1, + totalChars: 100 + }; + + test('empty', () => { + assert.deepEqual( + createTextSearchResult(uri, '', createOneLineRange(5, 0, 0)), + <vscode.TextSearchResult>{ + preview: { + text: '', + match: createOneLineRange(0, 0, 0) + }, + range: createOneLineRange(5, 0, 0), + uri + }); + + assert.deepEqual( + createTextSearchResult(uri, '', createOneLineRange(5, 0, 0), previewOptions1), + <vscode.TextSearchResult>{ + preview: { + text: '', + match: createOneLineRange(0, 0, 0) + }, + range: createOneLineRange(5, 0, 0), + uri + }); + }); + + test('short', () => { + assert.deepEqual( + createTextSearchResult(uri, 'foo bar', createOneLineRange(5, 4, 7)), + <vscode.TextSearchResult>{ + preview: { + text: 'foo bar', + match: createOneLineRange(0, 4, 7) + }, + range: createOneLineRange(5, 4, 7), + uri + }); + + assert.deepEqual( + createTextSearchResult(uri, 'foo bar', createOneLineRange(5, 4, 7), previewOptions1), + <vscode.TextSearchResult>{ + preview: { + text: 'foo bar', + match: createOneLineRange(0, 4, 7) + }, + range: createOneLineRange(5, 4, 7), + uri + }); + }); + + test('leading', () => { + assert.deepEqual( + createTextSearchResult(uri, 'long text very long text foo', createOneLineRange(5, 25, 28), previewOptions1), + <vscode.TextSearchResult>{ + preview: { + text: 'long text foo', + match: createOneLineRange(0, 10, 13) + }, + range: createOneLineRange(5, 25, 28), + uri + }); + }); + + test('trailing', () => { + assert.deepEqual( + createTextSearchResult(uri, 'foo long text very long text long text very long text long text very long text long text very long text long text very long text', createOneLineRange(5, 0, 3), previewOptions1), + <vscode.TextSearchResult>{ + preview: { + text: 'foo long text very long text long text very long text long text very long text long text very long t', + match: createOneLineRange(0, 0, 3) + }, + range: createOneLineRange(5, 0, 3), + uri + }); + }); + + test('middle', () => { + assert.deepEqual( + createTextSearchResult(uri, 'long text very long text long foo text very long text long text very long text long text very long text long text very long text', createOneLineRange(5, 30, 33), previewOptions1), + <vscode.TextSearchResult>{ + preview: { + text: 'text long foo text very long text long text very long text long text very long text long text very l', + match: createOneLineRange(0, 10, 13) + }, + range: createOneLineRange(5, 30, 33), + uri + }); + }); +}); \ No newline at end of file diff --git a/extensions/search-rg/src/utils.ts b/extensions/search-rg/src/utils.ts index fd083fe075f..86303ed79ee 100644 --- a/extensions/search-rg/src/utils.ts +++ b/extensions/search-rg/src/utils.ts @@ -19,9 +19,26 @@ export function anchorGlob(glob: string): string { return glob.startsWith('**') || glob.startsWith('/') ? glob : `/${glob}`; } -export function joinPath(resource: vscode.Uri, pathFragment: string): vscode.Uri { - const joinedPath = path.join(resource.path || '/', pathFragment); - return resource.with({ - path: joinedPath - }); +export function createTextSearchResult(uri: vscode.Uri, fullText: string, range: vscode.Range, previewOptions?: vscode.TextSearchPreviewOptions): vscode.TextSearchResult { + let preview: vscode.TextSearchResultPreview; + if (previewOptions) { + const previewStart = Math.max(range.start.character - previewOptions.leadingChars, 0); + const previewEnd = Math.max(previewOptions.totalChars + previewStart, range.end.character); + + preview = { + text: fullText.substring(previewStart, previewEnd), + match: new vscode.Range(0, range.start.character - previewStart, 0, range.end.character - previewStart) + }; + } else { + preview = { + text: fullText, + match: new vscode.Range(0, range.start.character, 0, range.end.character) + }; + } + + return <vscode.TextSearchResult>{ + uri, + range, + preview + }; } diff --git a/extensions/search-rg/yarn.lock b/extensions/search-rg/yarn.lock index 99b491f01ff..33381ca1f0d 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" @@ -1549,9 +1549,9 @@ vscode-nls@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398" -vscode-ripgrep@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.0.1.tgz#eff2f2b2a49921ac0acd3ff8dfecaaeebf0184cf" +vscode-ripgrep@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.1.0.tgz#93c1e39d88342ee1b15530a12898ce930d511948" vscode@^1.1.17: version "1.1.17" diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js new file mode 100644 index 00000000000..716610dc733 --- /dev/null +++ b/extensions/shared.webpack.config.js @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +/** @typedef {import('webpack').Configuration} WebpackConfig **/ + +'use strict'; + +const path = require('path'); +const merge = require('merge-options'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); + + +module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { + + /** @type WebpackConfig */ + let defaultConfig = { + mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production') + target: 'node', // extensions run in a node context + resolve: { + mainFields: ['main'], // prefer the main-entry of package.json files + extensions: ['.ts', '.js'] // support ts-files and js-files + }, + module: { + rules: [{ + test: /\.ts$/, + exclude: /node_modules/, + use: [{ + // vscode-nls-dev loader: + // * rewrite nls-calls + loader: 'vscode-nls-dev/lib/webpack-loader', + options: { + base: path.join(extConfig.context, 'src') + } + }, { + // configure TypeScript loader: + // * enable sources maps for end-to-end source maps + loader: 'ts-loader', + options: { + compilerOptions: { + "sourceMap": true, + } + } + }] + }] + }, + externals: { + 'vscode': 'commonjs vscode', // ignored because it doesn't exist + + "vscode-extension-telemetry": 'commonjs vscode-extension-telemetry', // commonly used + "vscode-nls": 'commonjs vscode-nls', + }, + output: { + // all output goes into `dist`. + // packaging depends on that and this must always be like it + filename: '[name].js', + path: path.join(extConfig.context, 'dist'), + libraryTarget: "commonjs", + }, + // yes, really source maps + devtool: 'source-map', + plugins: [ + new CopyWebpackPlugin([ + { from: './out/**/*', to: '.', ignore: ['*.js', '*.js.map'], flatten: true } + ]) + ], + }; + + return merge(defaultConfig, extConfig); +}; 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_plus.json b/extensions/theme-defaults/themes/dark_plus.json index 0ccbaeb606d..3e2c5ac607c 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -27,6 +27,8 @@ "storage.type.boolean.go", "storage.type.string.go", "storage.type.uintptr.go", + "storage.type.error.go", + "storage.type.rune.go", "storage.type.cs", "storage.type.generic.cs", "storage.type.modifier.cs", diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index c2a15addb8d..37a510a9c9a 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" } }, { @@ -141,13 +141,13 @@ } }, { - "scope": "beginning.punctuation.definition.quote.markdown", + "scope": "punctuation.definition.quote.begin.markdown", "settings": { - "foreground": "#608b4e" + "foreground": "#6A9955" } }, { - "scope": "beginning.punctuation.definition.list.markdown", + "scope": "punctuation.definition.list.begin.markdown", "settings": { "foreground": "#6796e6" } 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-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index 4c9985d44f2..726cae4058d 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -27,6 +27,8 @@ "storage.type.boolean.go", "storage.type.string.go", "storage.type.uintptr.go", + "storage.type.error.go", + "storage.type.rune.go", "storage.type.cs", "storage.type.generic.cs", "storage.type.modifier.cs", diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index 4134ce0a4cb..66bb3d98dc9 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -140,8 +140,8 @@ }, { "scope": [ - "beginning.punctuation.definition.quote.markdown", - "beginning.punctuation.definition.list.markdown" + "punctuation.definition.quote.begin.markdown", + "punctuation.definition.list.begin.markdown" ], "settings": { "foreground": "#0451a5" 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-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 31d7bffe3a7..ed22a1fb3c4 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -10,6 +10,31 @@ let fs = require('fs'); let https = require('https'); let url = require('url'); +// list of languagesIs not shipped with VSCode. The information is used to associate an icon with a langauge association +let nonBuiltInLanguages = { // { fileNames, extensions } + "r": { extensions: ['r', 'rhistory', 'rprofile', 'rt'] }, + "argdown": { extensions: ['ad', 'adown', 'argdown', 'argdn'] }, + "elm": { extensions: ['elm'] }, + "ocaml": { extensions: ['ml', 'mli'] }, + "nunjucks": { extensions: ['nunjucks', 'nunjs', 'nunj', 'nj', 'njk', 'tmpl', 'tpl'] }, + "mustache": { extensions: ['mustache', 'mst', 'mu', 'stache'] }, + "erb": { extensions: ['erb', 'rhtml', 'html.erb'] }, + "terraform": { extensions: ['tf', 'tfvars', 'hcl'] }, + "vue": { extensions: ['vue'] }, + "sass": { extensions: ['sass'] }, + "puppet": { extensions: ['puppet'] }, + "kotlin": { extensions: ['kt'] }, + "jinja": { extensions: ['jinja'] }, + "haxe": { extensions: ['hx'] }, + "haskell": { extensions: ['hs'] }, + "gradle": { extensions: ['gradle'] }, + "elixir": { extensions: ['ex'] }, + "haml": { extensions: ['haml'] }, + "stylus": { extensions: ['styl'] }, + "vala": { extensions: ['vala'] }, + "todo": { fileNames: ['todo'] } +} + function getCommitSha(repoId, repoPath) { let commitInfo = 'https://api.github.com/repos/' + repoId + '/commits?path=' + repoPath; return download(commitInfo).then(function (content) { @@ -34,7 +59,7 @@ function download(source) { } return new Promise((c, e) => { let _url = url.parse(source); - let options = { host: _url.host, port: _url.port, path: _url.path, headers: { 'User-Agent': 'NodeJS' }}; + let options = { host: _url.host, port: _url.port, path: _url.path, headers: { 'User-Agent': 'NodeJS' } }; let content = ''; https.get(options, function (response) { response.on('data', function (data) { @@ -50,7 +75,7 @@ function download(source) { function readFile(fileName) { return new Promise((c, e) => { - fs.readFile(fileName, function(err, data) { + fs.readFile(fileName, function (err, data) { if (err) { e(err); } else { @@ -67,12 +92,12 @@ function downloadBinary(source, dest) { return new Promise((c, e) => { https.get(source, function (response) { - switch(response.statusCode) { + switch (response.statusCode) { case 200: let file = fs.createWriteStream(dest); - response.on('data', function(chunk){ + response.on('data', function (chunk) { file.write(chunk); - }).on('end', function(){ + }).on('end', function () { file.end(); c(null); }).on('error', function (err) { @@ -107,7 +132,7 @@ function copyFile(fileName, dest) { rd.on("error", handleError); let wr = fs.createWriteStream(dest); wr.on("error", handleError); - wr.on("close", function() { + wr.on("close", function () { if (!cbCalled) { c(); cbCalled = true; @@ -119,7 +144,7 @@ function copyFile(fileName, dest) { function darkenColor(color) { let res = '#'; - for (let i = 1; i < 7; i+=2) { + for (let i = 1; i < 7; i += 2) { let newVal = Math.round(parseInt('0x' + color.substr(i, 2), 16) * 0.9); let hex = newVal.toString(16); if (hex.length == 1) { @@ -133,7 +158,7 @@ function darkenColor(color) { function getLanguageMappings() { let langMappings = {}; let allExtensions = fs.readdirSync('..'); - for (let i= 0; i < allExtensions.length; i++) { + for (let i = 0; i < allExtensions.length; i++) { let dirPath = path.join('..', allExtensions[i], 'package.json'); if (fs.existsSync(dirPath)) { let content = fs.readFileSync(dirPath).toString(); @@ -158,13 +183,16 @@ function getLanguageMappings() { } } } + for (let languageId in nonBuiltInLanguages) { + langMappings[languageId] = nonBuiltInLanguages[languageId]; + } return langMappings; } //let font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; let font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; -exports.copyFont = function() { +exports.copyFont = function () { return downloadBinary(font, './icons/seti.woff'); }; @@ -172,13 +200,13 @@ exports.copyFont = function() { //let mappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; //let colors = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; -let fontMappings = '../../../seti-ui/styles/_fonts/seti.less'; -let mappings = '../../../seti-ui/styles/components/icons/mapping.less'; -let colors = '../../../seti-ui/styles/ui-variables.less'; +let fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; +let fileAssociationFile = '../../../seti-ui/styles/components/icons/mapping.less'; +let colorsFile = '../../../seti-ui/styles/ui-variables.less'; exports.update = function () { - console.log('Reading from ' + fontMappings); + console.log('Reading from ' + fontMappingsFile); let def2Content = {}; let ext2Def = {}; let fileName2Def = {}; @@ -234,7 +262,7 @@ exports.update = function () { size: "150%" }], iconDefinitions: iconDefinitions, - // folder: "_folder", + // folder: "_folder", file: "_default", fileExtensions: ext2Def, fileNames: fileName2Def, @@ -256,15 +284,15 @@ exports.update = function () { let match; - return download(fontMappings).then(function (content) { + return download(fontMappingsFile).then(function (content) { let regex = /@([\w-]+):\s*'(\\E[0-9A-F]+)';/g; let contents = {}; while ((match = regex.exec(content)) !== null) { contents[match[1]] = match[2]; } - return download(mappings).then(function (content) { - let regex2 = /\.icon-(?:set|partial)\('([\w-\.]+)',\s*'([\w-]+)',\s*(@[\w-]+)\)/g; + return download(fileAssociationFile).then(function (content) { + let regex2 = /\.icon-(?:set|partial)\(['"]([\w-\.]+)['"],\s*['"]([\w-]+)['"],\s*(@[\w-]+)\)/g; while ((match = regex2.exec(content)) !== null) { let pattern = match[1]; let def = '_' + match[2]; @@ -304,26 +332,28 @@ exports.update = function () { } if (preferredDef) { lang2Def[lang] = preferredDef; - for (let i2 = 0; i2 < exts.length; i2++) { - // remove the extension association, unless it is different from the preferred - if (ext2Def[exts[i2]] === preferredDef) { - delete ext2Def[exts[i2]]; + if (!nonBuiltInLanguages[lang]) { + for (let i2 = 0; i2 < exts.length; i2++) { + // remove the extension association, unless it is different from the preferred + if (ext2Def[exts[i2]] === preferredDef) { + delete ext2Def[exts[i2]]; + } } - } - for (let i2 = 0; i2 < fileNames.length; i2++) { - // remove the fileName association, unless it is different from the preferred - if (fileName2Def[fileNames[i2]] === preferredDef) { - delete fileName2Def[fileNames[i2]]; + for (let i2 = 0; i2 < fileNames.length; i2++) { + // remove the fileName association, unless it is different from the preferred + if (fileName2Def[fileNames[i2]] === preferredDef) { + delete fileName2Def[fileNames[i2]]; + } } } } } - return download(colors).then(function (content) { + return download(colorsFile).then(function (content) { let regex3 = /(@[\w-]+):\s*(#[0-9a-z]+)/g; while ((match = regex3.exec(content)) !== null) { - colorId2Value[match[1]] = match[2]; + colorId2Value[match[1]] = match[2]; } return getCommitSha('jesseweed/seti-ui', 'styles/_fonts/seti.less').then(function (info) { try { diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index 476c5a9946b..ea92f2627db 100644 Binary files a/extensions/theme-seti/icons/seti.woff and b/extensions/theme-seti/icons/seti.woff differ diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index 6aeb37e95c6..ad4817d51f7 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -22,1151 +22,1223 @@ } ], "iconDefinitions": { - "_asm_light": { + "_R_light": { + "fontCharacter": "\\E001", + "fontColor": "#498ba7" + }, + "_R": { + "fontCharacter": "\\E001", + "fontColor": "#519aba" + }, + "_argdown_light": { "fontCharacter": "\\E003", + "fontColor": "#498ba7" + }, + "_argdown": { + "fontCharacter": "\\E003", + "fontColor": "#519aba" + }, + "_asm_light": { + "fontCharacter": "\\E004", "fontColor": "#b8383d" }, "_asm": { - "fontCharacter": "\\E003", + "fontCharacter": "\\E004", "fontColor": "#cc3e44" }, "_audio_light": { - "fontCharacter": "\\E004", + "fontCharacter": "\\E005", "fontColor": "#9068b0" }, "_audio": { - "fontCharacter": "\\E004", + "fontCharacter": "\\E005", "fontColor": "#a074c4" }, "_babel_light": { - "fontCharacter": "\\E005", + "fontCharacter": "\\E006", "fontColor": "#b7b73b" }, "_babel": { - "fontCharacter": "\\E005", + "fontCharacter": "\\E006", "fontColor": "#cbcb41" }, "_bower_light": { - "fontCharacter": "\\E006", + "fontCharacter": "\\E007", "fontColor": "#cc6d2e" }, "_bower": { - "fontCharacter": "\\E006", + "fontCharacter": "\\E007", "fontColor": "#e37933" }, "_bsl_light": { - "fontCharacter": "\\E007", + "fontCharacter": "\\E008", "fontColor": "#b8383d" }, "_bsl": { - "fontCharacter": "\\E007", + "fontCharacter": "\\E008", "fontColor": "#cc3e44" }, "_c_light": { - "fontCharacter": "\\E009", + "fontCharacter": "\\E00A", "fontColor": "#498ba7" }, "_c": { - "fontCharacter": "\\E009", + "fontCharacter": "\\E00A", "fontColor": "#519aba" }, "_c-sharp_light": { - "fontCharacter": "\\E008", + "fontCharacter": "\\E009", "fontColor": "#498ba7" }, "_c-sharp": { - "fontCharacter": "\\E008", + "fontCharacter": "\\E009", "fontColor": "#519aba" }, "_c_1_light": { - "fontCharacter": "\\E009", + "fontCharacter": "\\E00A", "fontColor": "#9068b0" }, "_c_1": { - "fontCharacter": "\\E009", + "fontCharacter": "\\E00A", "fontColor": "#a074c4" }, "_c_2_light": { - "fontCharacter": "\\E009", + "fontCharacter": "\\E00A", "fontColor": "#b7b73b" }, "_c_2": { - "fontCharacter": "\\E009", + "fontCharacter": "\\E00A", "fontColor": "#cbcb41" }, "_cake_light": { - "fontCharacter": "\\E00A", + "fontCharacter": "\\E00B", "fontColor": "#b8383d" }, "_cake": { - "fontCharacter": "\\E00A", + "fontCharacter": "\\E00B", "fontColor": "#cc3e44" }, "_cake_php_light": { - "fontCharacter": "\\E00B", + "fontCharacter": "\\E00C", "fontColor": "#b8383d" }, "_cake_php": { - "fontCharacter": "\\E00B", + "fontCharacter": "\\E00C", "fontColor": "#cc3e44" }, "_clock_light": { - "fontCharacter": "\\E00F", + "fontCharacter": "\\E010", "fontColor": "#498ba7" }, "_clock": { - "fontCharacter": "\\E00F", + "fontCharacter": "\\E010", "fontColor": "#519aba" }, "_clock_1_light": { - "fontCharacter": "\\E00F", + "fontCharacter": "\\E010", "fontColor": "#627379" }, "_clock_1": { - "fontCharacter": "\\E00F", + "fontCharacter": "\\E010", "fontColor": "#6d8086" }, "_clojure_light": { - "fontCharacter": "\\E010", + "fontCharacter": "\\E011", "fontColor": "#7fae42" }, "_clojure": { - "fontCharacter": "\\E010", + "fontCharacter": "\\E011", "fontColor": "#8dc149" }, "_clojure_1_light": { - "fontCharacter": "\\E010", + "fontCharacter": "\\E011", "fontColor": "#498ba7" }, "_clojure_1": { - "fontCharacter": "\\E010", + "fontCharacter": "\\E011", "fontColor": "#519aba" }, "_code-climate_light": { - "fontCharacter": "\\E011", + "fontCharacter": "\\E012", "fontColor": "#7fae42" }, "_code-climate": { - "fontCharacter": "\\E011", + "fontCharacter": "\\E012", "fontColor": "#8dc149" }, "_coffee_light": { - "fontCharacter": "\\E012", + "fontCharacter": "\\E013", "fontColor": "#b7b73b" }, "_coffee": { - "fontCharacter": "\\E012", + "fontCharacter": "\\E013", "fontColor": "#cbcb41" }, "_coldfusion_light": { - "fontCharacter": "\\E014", + "fontCharacter": "\\E015", "fontColor": "#498ba7" }, "_coldfusion": { - "fontCharacter": "\\E014", + "fontCharacter": "\\E015", "fontColor": "#519aba" }, "_config_light": { - "fontCharacter": "\\E015", + "fontCharacter": "\\E016", "fontColor": "#627379" }, "_config": { - "fontCharacter": "\\E015", + "fontCharacter": "\\E016", "fontColor": "#6d8086" }, "_cpp_light": { - "fontCharacter": "\\E016", + "fontCharacter": "\\E017", "fontColor": "#498ba7" }, "_cpp": { - "fontCharacter": "\\E016", + "fontCharacter": "\\E017", "fontColor": "#519aba" }, "_cpp_1_light": { - "fontCharacter": "\\E016", + "fontCharacter": "\\E017", "fontColor": "#9068b0" }, "_cpp_1": { - "fontCharacter": "\\E016", + "fontCharacter": "\\E017", "fontColor": "#a074c4" }, "_crystal_light": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#bfc2c1" }, "_crystal": { - "fontCharacter": "\\E017", + "fontCharacter": "\\E018", "fontColor": "#d4d7d6" }, "_crystal_embedded_light": { - "fontCharacter": "\\E018", + "fontCharacter": "\\E019", "fontColor": "#bfc2c1" }, "_crystal_embedded": { - "fontCharacter": "\\E018", + "fontCharacter": "\\E019", "fontColor": "#d4d7d6" }, "_css_light": { - "fontCharacter": "\\E019", + "fontCharacter": "\\E01A", "fontColor": "#498ba7" }, "_css": { - "fontCharacter": "\\E019", + "fontCharacter": "\\E01A", "fontColor": "#519aba" }, "_csv_light": { - "fontCharacter": "\\E01A", + "fontCharacter": "\\E01B", "fontColor": "#7fae42" }, "_csv": { - "fontCharacter": "\\E01A", + "fontCharacter": "\\E01B", "fontColor": "#8dc149" }, "_d_light": { - "fontCharacter": "\\E01B", + "fontCharacter": "\\E01C", "fontColor": "#b8383d" }, "_d": { - "fontCharacter": "\\E01B", + "fontCharacter": "\\E01C", "fontColor": "#cc3e44" }, "_db_light": { - "fontCharacter": "\\E01C", + "fontCharacter": "\\E01D", "fontColor": "#dd4b78" }, "_db": { - "fontCharacter": "\\E01C", + "fontCharacter": "\\E01D", "fontColor": "#f55385" }, "_default_light": { - "fontCharacter": "\\E01D", + "fontCharacter": "\\E01E", "fontColor": "#bfc2c1" }, "_default": { - "fontCharacter": "\\E01D", + "fontCharacter": "\\E01E", "fontColor": "#d4d7d6" }, "_docker_light": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#498ba7" }, "_docker": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#519aba" }, "_docker_1_light": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#455155" }, "_docker_1": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#4d5a5e" }, "_docker_2_light": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#7fae42" }, "_docker_2": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#8dc149" }, "_docker_3_light": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#dd4b78" }, "_docker_3": { - "fontCharacter": "\\E01F", + "fontCharacter": "\\E020", "fontColor": "#f55385" }, "_ejs_light": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E022", "fontColor": "#b7b73b" }, "_ejs": { - "fontCharacter": "\\E021", + "fontCharacter": "\\E022", "fontColor": "#cbcb41" }, "_elixir_light": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#9068b0" }, "_elixir": { - "fontCharacter": "\\E022", + "fontCharacter": "\\E023", "fontColor": "#a074c4" }, "_elixir_script_light": { - "fontCharacter": "\\E023", + "fontCharacter": "\\E024", "fontColor": "#9068b0" }, "_elixir_script": { - "fontCharacter": "\\E023", + "fontCharacter": "\\E024", "fontColor": "#a074c4" }, "_elm_light": { - "fontCharacter": "\\E024", + "fontCharacter": "\\E025", "fontColor": "#498ba7" }, "_elm": { - "fontCharacter": "\\E024", + "fontCharacter": "\\E025", "fontColor": "#519aba" }, "_eslint_light": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E027", "fontColor": "#9068b0" }, "_eslint": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E027", "fontColor": "#a074c4" }, "_eslint_1_light": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E027", "fontColor": "#455155" }, "_eslint_1": { - "fontCharacter": "\\E026", + "fontCharacter": "\\E027", "fontColor": "#4d5a5e" }, "_ethereum_light": { - "fontCharacter": "\\E027", + "fontCharacter": "\\E028", "fontColor": "#498ba7" }, "_ethereum": { - "fontCharacter": "\\E027", + "fontCharacter": "\\E028", "fontColor": "#519aba" }, "_f-sharp_light": { - "fontCharacter": "\\E028", + "fontCharacter": "\\E029", "fontColor": "#498ba7" }, "_f-sharp": { - "fontCharacter": "\\E028", + "fontCharacter": "\\E029", "fontColor": "#519aba" }, "_favicon_light": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02A", "fontColor": "#b7b73b" }, "_favicon": { - "fontCharacter": "\\E029", + "fontCharacter": "\\E02A", "fontColor": "#cbcb41" }, "_firebase_light": { - "fontCharacter": "\\E02A", + "fontCharacter": "\\E02B", "fontColor": "#cc6d2e" }, "_firebase": { - "fontCharacter": "\\E02A", + "fontCharacter": "\\E02B", "fontColor": "#e37933" }, "_firefox_light": { - "fontCharacter": "\\E02B", + "fontCharacter": "\\E02C", "fontColor": "#cc6d2e" }, "_firefox": { - "fontCharacter": "\\E02B", + "fontCharacter": "\\E02C", "fontColor": "#e37933" }, "_font_light": { - "fontCharacter": "\\E02D", + "fontCharacter": "\\E02E", "fontColor": "#b8383d" }, "_font": { - "fontCharacter": "\\E02D", + "fontCharacter": "\\E02E", "fontColor": "#cc3e44" }, "_git_light": { - "fontCharacter": "\\E02E", + "fontCharacter": "\\E02F", "fontColor": "#3b4b52" }, "_git": { - "fontCharacter": "\\E02E", + "fontCharacter": "\\E02F", "fontColor": "#41535b" }, "_go_light": { - "fontCharacter": "\\E032", + "fontCharacter": "\\E033", "fontColor": "#498ba7" }, "_go": { - "fontCharacter": "\\E032", + "fontCharacter": "\\E033", "fontColor": "#519aba" }, "_go2_light": { - "fontCharacter": "\\E033", + "fontCharacter": "\\E034", "fontColor": "#498ba7" }, "_go2": { - "fontCharacter": "\\E033", + "fontCharacter": "\\E034", "fontColor": "#519aba" }, "_gradle_light": { - "fontCharacter": "\\E034", + "fontCharacter": "\\E035", "fontColor": "#7fae42" }, "_gradle": { - "fontCharacter": "\\E034", + "fontCharacter": "\\E035", "fontColor": "#8dc149" }, "_grails_light": { - "fontCharacter": "\\E035", + "fontCharacter": "\\E036", "fontColor": "#7fae42" }, "_grails": { - "fontCharacter": "\\E035", + "fontCharacter": "\\E036", "fontColor": "#8dc149" }, "_grunt_light": { - "fontCharacter": "\\E036", + "fontCharacter": "\\E037", "fontColor": "#cc6d2e" }, "_grunt": { - "fontCharacter": "\\E036", + "fontCharacter": "\\E037", "fontColor": "#e37933" }, "_gulp_light": { - "fontCharacter": "\\E037", + "fontCharacter": "\\E038", "fontColor": "#b8383d" }, "_gulp": { - "fontCharacter": "\\E037", + "fontCharacter": "\\E038", "fontColor": "#cc3e44" }, "_haml_light": { - "fontCharacter": "\\E039", + "fontCharacter": "\\E03A", "fontColor": "#b8383d" }, "_haml": { - "fontCharacter": "\\E039", + "fontCharacter": "\\E03A", "fontColor": "#cc3e44" }, "_haskell_light": { - "fontCharacter": "\\E03A", + "fontCharacter": "\\E03B", "fontColor": "#9068b0" }, "_haskell": { - "fontCharacter": "\\E03A", + "fontCharacter": "\\E03B", "fontColor": "#a074c4" }, "_haxe_light": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#cc6d2e" }, "_haxe": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#e37933" }, "_haxe_1_light": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#b7b73b" }, "_haxe_1": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#cbcb41" }, "_haxe_2_light": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#498ba7" }, "_haxe_2": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#519aba" }, "_haxe_3_light": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#9068b0" }, "_haxe_3": { - "fontCharacter": "\\E03B", + "fontCharacter": "\\E03C", "fontColor": "#a074c4" }, "_heroku_light": { - "fontCharacter": "\\E03C", + "fontCharacter": "\\E03D", "fontColor": "#9068b0" }, "_heroku": { - "fontCharacter": "\\E03C", + "fontCharacter": "\\E03D", "fontColor": "#a074c4" }, "_hex_light": { - "fontCharacter": "\\E03D", + "fontCharacter": "\\E03E", "fontColor": "#b8383d" }, "_hex": { - "fontCharacter": "\\E03D", + "fontCharacter": "\\E03E", "fontColor": "#cc3e44" }, "_html_light": { - "fontCharacter": "\\E03E", + "fontCharacter": "\\E03F", "fontColor": "#cc6d2e" }, "_html": { - "fontCharacter": "\\E03E", + "fontCharacter": "\\E03F", "fontColor": "#e37933" }, "_html_erb_light": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E040", "fontColor": "#b8383d" }, "_html_erb": { - "fontCharacter": "\\E03F", + "fontCharacter": "\\E040", "fontColor": "#cc3e44" }, "_ignored_light": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#3b4b52" }, "_ignored": { - "fontCharacter": "\\E040", + "fontCharacter": "\\E041", "fontColor": "#41535b" }, "_illustrator_light": { - "fontCharacter": "\\E041", + "fontCharacter": "\\E042", "fontColor": "#b7b73b" }, "_illustrator": { - "fontCharacter": "\\E041", + "fontCharacter": "\\E042", "fontColor": "#cbcb41" }, "_image_light": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E043", "fontColor": "#9068b0" }, "_image": { - "fontCharacter": "\\E042", + "fontCharacter": "\\E043", "fontColor": "#a074c4" }, "_info_light": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#498ba7" }, "_info": { - "fontCharacter": "\\E043", + "fontCharacter": "\\E044", "fontColor": "#519aba" }, "_ionic_light": { - "fontCharacter": "\\E044", + "fontCharacter": "\\E045", "fontColor": "#498ba7" }, "_ionic": { - "fontCharacter": "\\E044", + "fontCharacter": "\\E045", "fontColor": "#519aba" }, "_jade_light": { - "fontCharacter": "\\E045", + "fontCharacter": "\\E046", "fontColor": "#b8383d" }, "_jade": { - "fontCharacter": "\\E045", + "fontCharacter": "\\E046", "fontColor": "#cc3e44" }, "_java_light": { - "fontCharacter": "\\E046", + "fontCharacter": "\\E047", "fontColor": "#b8383d" }, "_java": { - "fontCharacter": "\\E046", + "fontCharacter": "\\E047", "fontColor": "#cc3e44" }, "_javascript_light": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#b7b73b" }, "_javascript": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#cbcb41" }, "_javascript_1_light": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#cc6d2e" }, "_javascript_1": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#e37933" }, "_javascript_2_light": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#498ba7" }, "_javascript_2": { - "fontCharacter": "\\E047", + "fontCharacter": "\\E048", "fontColor": "#519aba" }, "_jenkins_light": { - "fontCharacter": "\\E048", + "fontCharacter": "\\E049", "fontColor": "#b8383d" }, "_jenkins": { - "fontCharacter": "\\E048", + "fontCharacter": "\\E049", "fontColor": "#cc3e44" }, "_jinja_light": { - "fontCharacter": "\\E049", + "fontCharacter": "\\E04A", "fontColor": "#b8383d" }, "_jinja": { - "fontCharacter": "\\E049", + "fontCharacter": "\\E04A", "fontColor": "#cc3e44" }, "_json_light": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04C", "fontColor": "#b7b73b" }, "_json": { - "fontCharacter": "\\E04B", + "fontCharacter": "\\E04C", "fontColor": "#cbcb41" }, - "_julia_light": { + "_json_1_light": { "fontCharacter": "\\E04C", + "fontColor": "#7fae42" + }, + "_json_1": { + "fontCharacter": "\\E04C", + "fontColor": "#8dc149" + }, + "_julia_light": { + "fontCharacter": "\\E04D", "fontColor": "#9068b0" }, "_julia": { - "fontCharacter": "\\E04C", + "fontCharacter": "\\E04D", "fontColor": "#a074c4" }, "_karma_light": { - "fontCharacter": "\\E04D", + "fontCharacter": "\\E04E", "fontColor": "#7fae42" }, "_karma": { - "fontCharacter": "\\E04D", + "fontCharacter": "\\E04E", "fontColor": "#8dc149" }, + "_kotlin_light": { + "fontCharacter": "\\E04F", + "fontColor": "#cc6d2e" + }, + "_kotlin": { + "fontCharacter": "\\E04F", + "fontColor": "#e37933" + }, "_less_light": { - "fontCharacter": "\\E04E", + "fontCharacter": "\\E050", "fontColor": "#498ba7" }, "_less": { - "fontCharacter": "\\E04E", + "fontCharacter": "\\E050", "fontColor": "#519aba" }, "_license_light": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#b7b73b" }, "_license": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#cbcb41" }, "_license_1_light": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#cc6d2e" }, "_license_1": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#e37933" }, "_license_2_light": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#b8383d" }, "_license_2": { - "fontCharacter": "\\E04F", + "fontCharacter": "\\E051", "fontColor": "#cc3e44" }, "_liquid_light": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E052", "fontColor": "#7fae42" }, "_liquid": { - "fontCharacter": "\\E050", + "fontCharacter": "\\E052", "fontColor": "#8dc149" }, "_livescript_light": { - "fontCharacter": "\\E051", + "fontCharacter": "\\E053", "fontColor": "#498ba7" }, "_livescript": { - "fontCharacter": "\\E051", + "fontCharacter": "\\E053", "fontColor": "#519aba" }, "_lock_light": { - "fontCharacter": "\\E052", + "fontCharacter": "\\E054", "fontColor": "#7fae42" }, "_lock": { - "fontCharacter": "\\E052", + "fontCharacter": "\\E054", "fontColor": "#8dc149" }, "_lua_light": { - "fontCharacter": "\\E053", + "fontCharacter": "\\E055", "fontColor": "#498ba7" }, "_lua": { - "fontCharacter": "\\E053", + "fontCharacter": "\\E055", "fontColor": "#519aba" }, "_makefile_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#cc6d2e" }, "_makefile": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#e37933" }, "_makefile_1_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#9068b0" }, "_makefile_1": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#a074c4" }, "_makefile_2_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#627379" }, "_makefile_2": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#6d8086" }, "_makefile_3_light": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#498ba7" }, "_makefile_3": { - "fontCharacter": "\\E054", + "fontCharacter": "\\E056", "fontColor": "#519aba" }, "_markdown_light": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E057", "fontColor": "#498ba7" }, "_markdown": { - "fontCharacter": "\\E055", + "fontCharacter": "\\E057", "fontColor": "#519aba" }, "_maven_light": { - "fontCharacter": "\\E056", + "fontCharacter": "\\E058", "fontColor": "#b8383d" }, "_maven": { - "fontCharacter": "\\E056", + "fontCharacter": "\\E058", "fontColor": "#cc3e44" }, "_mdo_light": { - "fontCharacter": "\\E057", + "fontCharacter": "\\E059", "fontColor": "#b8383d" }, "_mdo": { - "fontCharacter": "\\E057", + "fontCharacter": "\\E059", "fontColor": "#cc3e44" }, "_mustache_light": { - "fontCharacter": "\\E058", + "fontCharacter": "\\E05A", "fontColor": "#cc6d2e" }, "_mustache": { - "fontCharacter": "\\E058", + "fontCharacter": "\\E05A", "fontColor": "#e37933" }, "_npm_light": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05C", "fontColor": "#3b4b52" }, "_npm": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05C", "fontColor": "#41535b" }, "_npm_1_light": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05C", "fontColor": "#b8383d" }, "_npm_1": { - "fontCharacter": "\\E05A", + "fontCharacter": "\\E05C", "fontColor": "#cc3e44" }, "_npm_ignored_light": { - "fontCharacter": "\\E05B", + "fontCharacter": "\\E05D", "fontColor": "#3b4b52" }, "_npm_ignored": { - "fontCharacter": "\\E05B", + "fontCharacter": "\\E05D", "fontColor": "#41535b" }, "_nunjucks_light": { - "fontCharacter": "\\E05C", + "fontCharacter": "\\E05E", "fontColor": "#7fae42" }, "_nunjucks": { - "fontCharacter": "\\E05C", + "fontCharacter": "\\E05E", "fontColor": "#8dc149" }, "_ocaml_light": { - "fontCharacter": "\\E05D", + "fontCharacter": "\\E05F", "fontColor": "#cc6d2e" }, "_ocaml": { - "fontCharacter": "\\E05D", + "fontCharacter": "\\E05F", "fontColor": "#e37933" }, "_odata_light": { - "fontCharacter": "\\E05E", + "fontCharacter": "\\E060", "fontColor": "#cc6d2e" }, "_odata": { - "fontCharacter": "\\E05E", + "fontCharacter": "\\E060", "fontColor": "#e37933" }, "_pdf_light": { - "fontCharacter": "\\E05F", + "fontCharacter": "\\E061", "fontColor": "#b8383d" }, "_pdf": { - "fontCharacter": "\\E05F", + "fontCharacter": "\\E061", "fontColor": "#cc3e44" }, "_perl_light": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E062", "fontColor": "#498ba7" }, "_perl": { - "fontCharacter": "\\E060", + "fontCharacter": "\\E062", "fontColor": "#519aba" }, "_photoshop_light": { - "fontCharacter": "\\E061", + "fontCharacter": "\\E063", "fontColor": "#498ba7" }, "_photoshop": { - "fontCharacter": "\\E061", + "fontCharacter": "\\E063", "fontColor": "#519aba" }, "_php_light": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E064", "fontColor": "#9068b0" }, "_php": { - "fontCharacter": "\\E062", + "fontCharacter": "\\E064", "fontColor": "#a074c4" }, "_powershell_light": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E065", "fontColor": "#498ba7" }, "_powershell": { - "fontCharacter": "\\E063", + "fontCharacter": "\\E065", "fontColor": "#519aba" }, "_pug_light": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E067", "fontColor": "#b8383d" }, "_pug": { - "fontCharacter": "\\E065", + "fontCharacter": "\\E067", "fontColor": "#cc3e44" }, "_puppet_light": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E068", "fontColor": "#b7b73b" }, "_puppet": { - "fontCharacter": "\\E066", + "fontCharacter": "\\E068", "fontColor": "#cbcb41" }, "_python_light": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E069", "fontColor": "#498ba7" }, "_python": { - "fontCharacter": "\\E067", + "fontCharacter": "\\E069", "fontColor": "#519aba" }, "_react_light": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06B", "fontColor": "#498ba7" }, "_react": { - "fontCharacter": "\\E069", + "fontCharacter": "\\E06B", "fontColor": "#519aba" }, "_rollup_light": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06C", "fontColor": "#b8383d" }, "_rollup": { - "fontCharacter": "\\E06A", + "fontCharacter": "\\E06C", "fontColor": "#cc3e44" }, "_ruby_light": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06D", "fontColor": "#b8383d" }, "_ruby": { - "fontCharacter": "\\E06B", + "fontCharacter": "\\E06D", "fontColor": "#cc3e44" }, "_rust_light": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06E", "fontColor": "#627379" }, "_rust": { - "fontCharacter": "\\E06C", + "fontCharacter": "\\E06E", "fontColor": "#6d8086" }, "_salesforce_light": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E06F", "fontColor": "#498ba7" }, "_salesforce": { - "fontCharacter": "\\E06D", + "fontCharacter": "\\E06F", "fontColor": "#519aba" }, "_sass_light": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E070", "fontColor": "#dd4b78" }, "_sass": { - "fontCharacter": "\\E06E", + "fontCharacter": "\\E070", "fontColor": "#f55385" }, "_sbt_light": { - "fontCharacter": "\\E06F", + "fontCharacter": "\\E071", "fontColor": "#498ba7" }, "_sbt": { - "fontCharacter": "\\E06F", + "fontCharacter": "\\E071", "fontColor": "#519aba" }, "_scala_light": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E072", "fontColor": "#b8383d" }, "_scala": { - "fontCharacter": "\\E070", + "fontCharacter": "\\E072", "fontColor": "#cc3e44" }, "_shell_light": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#455155" }, "_shell": { - "fontCharacter": "\\E073", + "fontCharacter": "\\E075", "fontColor": "#4d5a5e" }, "_slim_light": { - "fontCharacter": "\\E074", + "fontCharacter": "\\E076", "fontColor": "#cc6d2e" }, "_slim": { - "fontCharacter": "\\E074", + "fontCharacter": "\\E076", "fontColor": "#e37933" }, "_smarty_light": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#b7b73b" }, "_smarty": { - "fontCharacter": "\\E075", + "fontCharacter": "\\E077", "fontColor": "#cbcb41" }, "_spring_light": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E078", "fontColor": "#7fae42" }, "_spring": { - "fontCharacter": "\\E076", + "fontCharacter": "\\E078", "fontColor": "#8dc149" }, + "_stylelint_light": { + "fontCharacter": "\\E079", + "fontColor": "#bfc2c1" + }, + "_stylelint": { + "fontCharacter": "\\E079", + "fontColor": "#d4d7d6" + }, + "_stylelint_1_light": { + "fontCharacter": "\\E079", + "fontColor": "#455155" + }, + "_stylelint_1": { + "fontCharacter": "\\E079", + "fontColor": "#4d5a5e" + }, "_stylus_light": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E07A", "fontColor": "#7fae42" }, "_stylus": { - "fontCharacter": "\\E077", + "fontCharacter": "\\E07A", "fontColor": "#8dc149" }, "_sublime_light": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07B", "fontColor": "#cc6d2e" }, "_sublime": { - "fontCharacter": "\\E078", + "fontCharacter": "\\E07B", "fontColor": "#e37933" }, "_svg_light": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07C", "fontColor": "#9068b0" }, "_svg": { - "fontCharacter": "\\E079", + "fontCharacter": "\\E07C", "fontColor": "#a074c4" }, + "_svg_1_light": { + "fontCharacter": "\\E07C", + "fontColor": "#498ba7" + }, + "_svg_1": { + "fontCharacter": "\\E07C", + "fontColor": "#519aba" + }, "_swift_light": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E07D", "fontColor": "#cc6d2e" }, "_swift": { - "fontCharacter": "\\E07A", + "fontCharacter": "\\E07D", "fontColor": "#e37933" }, "_terraform_light": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07E", "fontColor": "#9068b0" }, "_terraform": { - "fontCharacter": "\\E07B", + "fontCharacter": "\\E07E", "fontColor": "#a074c4" }, "_tex_light": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#498ba7" }, "_tex": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#519aba" }, "_tex_1_light": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#b7b73b" }, "_tex_1": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#cbcb41" }, "_tex_2_light": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#cc6d2e" }, "_tex_2": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#e37933" }, "_tex_3_light": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#bfc2c1" }, "_tex_3": { - "fontCharacter": "\\E07C", + "fontCharacter": "\\E07F", "fontColor": "#d4d7d6" }, "_todo": { - "fontCharacter": "\\E07E" + "fontCharacter": "\\E081" }, "_twig_light": { - "fontCharacter": "\\E07F", + "fontCharacter": "\\E082", "fontColor": "#7fae42" }, "_twig": { - "fontCharacter": "\\E07F", + "fontCharacter": "\\E082", "fontColor": "#8dc149" }, "_typescript_light": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E083", "fontColor": "#498ba7" }, "_typescript": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E083", "fontColor": "#519aba" }, "_typescript_1_light": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E083", "fontColor": "#b7b73b" }, "_typescript_1": { - "fontCharacter": "\\E080", + "fontCharacter": "\\E083", "fontColor": "#cbcb41" }, "_vala_light": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E084", "fontColor": "#627379" }, "_vala": { - "fontCharacter": "\\E081", + "fontCharacter": "\\E084", "fontColor": "#6d8086" }, "_video_light": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E085", "fontColor": "#dd4b78" }, "_video": { - "fontCharacter": "\\E082", + "fontCharacter": "\\E085", "fontColor": "#f55385" }, "_vue_light": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E086", "fontColor": "#7fae42" }, "_vue": { - "fontCharacter": "\\E083", + "fontCharacter": "\\E086", "fontColor": "#8dc149" }, + "_wasm_light": { + "fontCharacter": "\\E087", + "fontColor": "#9068b0" + }, + "_wasm": { + "fontCharacter": "\\E087", + "fontColor": "#a074c4" + }, + "_wat_light": { + "fontCharacter": "\\E088", + "fontColor": "#9068b0" + }, + "_wat": { + "fontCharacter": "\\E088", + "fontColor": "#a074c4" + }, "_webpack_light": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E089", "fontColor": "#498ba7" }, "_webpack": { - "fontCharacter": "\\E084", + "fontCharacter": "\\E089", "fontColor": "#519aba" }, "_wgt_light": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E08A", "fontColor": "#498ba7" }, "_wgt": { - "fontCharacter": "\\E085", + "fontCharacter": "\\E08A", "fontColor": "#519aba" }, "_windows_light": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08B", "fontColor": "#498ba7" }, "_windows": { - "fontCharacter": "\\E086", + "fontCharacter": "\\E08B", "fontColor": "#519aba" }, "_word_light": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E08C", "fontColor": "#498ba7" }, "_word": { - "fontCharacter": "\\E087", + "fontCharacter": "\\E08C", "fontColor": "#519aba" }, "_xls_light": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08D", "fontColor": "#7fae42" }, "_xls": { - "fontCharacter": "\\E088", + "fontCharacter": "\\E08D", "fontColor": "#8dc149" }, "_xml_light": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E08E", "fontColor": "#cc6d2e" }, "_xml": { - "fontCharacter": "\\E089", + "fontCharacter": "\\E08E", "fontColor": "#e37933" }, "_yarn_light": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08F", "fontColor": "#498ba7" }, "_yarn": { - "fontCharacter": "\\E08A", + "fontCharacter": "\\E08F", "fontColor": "#519aba" }, "_yml_light": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E090", "fontColor": "#9068b0" }, "_yml": { - "fontCharacter": "\\E08B", + "fontCharacter": "\\E090", "fontColor": "#a074c4" }, "_zip_light": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E091", "fontColor": "#b8383d" }, "_zip": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E091", "fontColor": "#cc3e44" }, "_zip_1_light": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E091", "fontColor": "#627379" }, "_zip_1": { - "fontCharacter": "\\E08C", + "fontCharacter": "\\E091", "fontColor": "#6d8086" } }, @@ -1231,8 +1303,12 @@ "jinja": "_jinja", "jinja2": "_jinja", "jl": "_julia", + "kt": "_kotlin", + "kts": "_kotlin", "liquid": "_liquid", "ls": "_livescript", + "argdown": "_argdown", + "ad": "_argdown", "mustache": "_mustache", "stache": "_mustache", "njk": "_nunjucks", @@ -1254,6 +1330,7 @@ "pp": "_puppet", "epp": "_puppet", "cjsx": "_react", + "r": "_R", "erb": "_html_erb", "erb.html": "_html_erb", "html.erb": "_html_erb", @@ -1279,6 +1356,8 @@ "vala": "_vala", "vapi": "_vala", "vue": "_vue", + "wasm": "_wasm", + "wat": "_wat", "jar": "_zip", "zip": "_zip_1", "wgt": "_wgt", @@ -1310,6 +1389,12 @@ "mp3": "_audio", "ogg": "_audio", "wav": "_audio", + "flac": "_audio", + "3ds": "_svg_1", + "3dm": "_svg_1", + "stl": "_svg_1", + "obj": "_svg_1", + "dae": "_svg_1", "babelrc": "_babel", "bowerrc": "_bower", "dockerignore": "_docker_1", @@ -1323,6 +1408,12 @@ "firebaserc": "_firebase", "jshintrc": "_javascript_2", "jscsrc": "_javascript_2", + "stylelintrc": "_stylelint", + "stylelintrc.json": "_stylelint", + "stylelintrc.yaml": "_stylelint", + "stylelintrc.yml": "_stylelint", + "stylelintrc.js": "_stylelint", + "stylelintignore": "_stylelint_1", "direnv": "_config", "env": "_config", "static": "_config", @@ -1341,9 +1432,13 @@ "readme.md": "_info", "changelog.md": "_clock", "changelog": "_clock", + "changes.md": "_clock", "version.md": "_clock", "version": "_clock", "mvnw": "_maven", + "swagger.json": "_json_1", + "swagger.yml": "_json_1", + "swagger.yaml": "_json_1", "mime.types": "_config", "jenkinsfile": "_jenkins", "bower.json": "_bower", @@ -1360,6 +1455,7 @@ "ionic.project": "_ionic", "rollup.config.js": "_rollup", "sass-lint.yml": "_sass", + "stylelint.config.js": "_stylelint", "yarn.clean": "_yarn", "yarn.lock": "_yarn", "webpack.config.js": "_webpack", @@ -1405,6 +1501,7 @@ "powershell": "_powershell", "jade": "_jade", "python": "_python", + "r": "_R", "ruby": "_ruby", "rust": "_rust", "scss": "_sass", @@ -1414,7 +1511,26 @@ "typescript": "_typescript", "typescriptreact": "_react", "xml": "_xml", - "yaml": "_yml" + "yaml": "_yml", + "argdown": "_argdown", + "elm": "_elm", + "ocaml": "_ocaml", + "nunjucks": "_nunjucks", + "mustache": "_mustache", + "erb": "_html_erb", + "terraform": "_terraform", + "vue": "_vue", + "sass": "_sass", + "kotlin": "_kotlin", + "jinja": "_jinja", + "haxe": "_haxe", + "haskell": "_haskell", + "gradle": "_gradle", + "elixir": "_elixir", + "haml": "_haml", + "stylus": "_stylus", + "vala": "_vala", + "todo": "_todo" }, "light": { "file": "_default_light", @@ -1478,8 +1594,12 @@ "jinja": "_jinja_light", "jinja2": "_jinja_light", "jl": "_julia_light", + "kt": "_kotlin_light", + "kts": "_kotlin_light", "liquid": "_liquid_light", "ls": "_livescript_light", + "argdown": "_argdown_light", + "ad": "_argdown_light", "mustache": "_mustache_light", "stache": "_mustache_light", "njk": "_nunjucks_light", @@ -1501,6 +1621,7 @@ "pp": "_puppet_light", "epp": "_puppet_light", "cjsx": "_react_light", + "r": "_R_light", "erb": "_html_erb_light", "erb.html": "_html_erb_light", "html.erb": "_html_erb_light", @@ -1526,6 +1647,8 @@ "vala": "_vala_light", "vapi": "_vala_light", "vue": "_vue_light", + "wasm": "_wasm_light", + "wat": "_wat_light", "jar": "_zip_light", "zip": "_zip_1_light", "wgt": "_wgt_light", @@ -1557,6 +1680,12 @@ "mp3": "_audio_light", "ogg": "_audio_light", "wav": "_audio_light", + "flac": "_audio_light", + "3ds": "_svg_1_light", + "3dm": "_svg_1_light", + "stl": "_svg_1_light", + "obj": "_svg_1_light", + "dae": "_svg_1_light", "babelrc": "_babel_light", "bowerrc": "_bower_light", "dockerignore": "_docker_1_light", @@ -1570,6 +1699,12 @@ "firebaserc": "_firebase_light", "jshintrc": "_javascript_2_light", "jscsrc": "_javascript_2_light", + "stylelintrc": "_stylelint_light", + "stylelintrc.json": "_stylelint_light", + "stylelintrc.yaml": "_stylelint_light", + "stylelintrc.yml": "_stylelint_light", + "stylelintrc.js": "_stylelint_light", + "stylelintignore": "_stylelint_1_light", "direnv": "_config_light", "env": "_config_light", "static": "_config_light", @@ -1610,6 +1745,7 @@ "powershell": "_powershell_light", "jade": "_jade_light", "python": "_python_light", + "r": "_R_light", "ruby": "_ruby_light", "rust": "_rust_light", "scss": "_sass_light", @@ -1619,7 +1755,25 @@ "typescript": "_typescript_light", "typescriptreact": "_react_light", "xml": "_xml_light", - "yaml": "_yml_light" + "yaml": "_yml_light", + "argdown": "_argdown_light", + "elm": "_elm_light", + "ocaml": "_ocaml_light", + "nunjucks": "_nunjucks_light", + "mustache": "_mustache_light", + "erb": "_html_erb_light", + "terraform": "_terraform_light", + "vue": "_vue_light", + "sass": "_sass_light", + "kotlin": "_kotlin_light", + "jinja": "_jinja_light", + "haxe": "_haxe_light", + "haskell": "_haskell_light", + "gradle": "_gradle_light", + "elixir": "_elixir_light", + "haml": "_haml_light", + "stylus": "_stylus_light", + "vala": "_vala_light" }, "fileNames": { "mix": "_hex_light", @@ -1628,9 +1782,13 @@ "readme.md": "_info_light", "changelog.md": "_clock_light", "changelog": "_clock_light", + "changes.md": "_clock_light", "version.md": "_clock_light", "version": "_clock_light", "mvnw": "_maven_light", + "swagger.json": "_json_1_light", + "swagger.yml": "_json_1_light", + "swagger.yaml": "_json_1_light", "mime.types": "_config_light", "jenkinsfile": "_jenkins_light", "bower.json": "_bower_light", @@ -1647,6 +1805,7 @@ "ionic.project": "_ionic_light", "rollup.config.js": "_rollup_light", "sass-lint.yml": "_sass_light", + "stylelint.config.js": "_stylelint_light", "yarn.clean": "_yarn_light", "yarn.lock": "_yarn_light", "webpack.config.js": "_webpack_light", @@ -1663,5 +1822,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/188dda34a56b9555c7d363771264c24f4693983d" + "version": "https://github.com/jesseweed/seti-ui/commit/7714a720646300bb8f6d1690752cd71f50991414" } \ No newline at end of file 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/language-configuration.json b/extensions/typescript-basics/language-configuration.json index 1e8f440a420..b41053843cf 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -25,6 +25,7 @@ ["\"", "\""], ["`", "`"] ], + "autoCloseBefore": ";:.,=}])>` \n\t", "folding": { "markers": { "start": "^\\s*//\\s*#?region\\b", diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index efd1eed74e0..7abcc55889e 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/27425437b2144f43607047ae7ee9b826e36856a5", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/e2db601b62527357f772b48fb4a17965b11f844c", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -93,6 +93,10 @@ }, { "include": "#export-declaration" + }, + { + "name": "storage.modifier.ts", + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(declare|export)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" } ] }, @@ -146,10 +150,6 @@ { "name": "keyword.other.debugger.ts", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(debugger)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" - }, - { - "name": "storage.modifier.ts", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(declare)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" } ] }, @@ -282,41 +282,26 @@ ] }, "var-expr": { - "name": "meta.var.expr.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(var|let|const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", - "beginCaptures": { - "1": { - "name": "keyword.control.export.ts" - }, - "2": { - "name": "storage.type.ts" - } - }, - "end": "(?=$|^|;|}|(\\s+(of|in)\\s+))", "patterns": [ { - "include": "#destructuring-variable" - }, - { - "include": "#var-single-variable" - }, - { - "include": "#variable-initializer" - }, - { - "include": "#comment" - }, - { - "begin": "(,)\\s*(?!\\S)", - "beginCaptures": { - "1": { - "name": "punctuation.separator.comma.ts" - } - }, - "end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))", + "name": "meta.var.expr.ts", + "begin": "(?=(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(var|let)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))", + "end": "(?!(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(var|let)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))((?=;|}|(\\s+(of|in)\\s+)|^\\s*$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|((?<!^let|[^\\._$[:alnum:]]let|^var|[^\\._$[:alnum:]]var)(?=\\s*$)))", "patterns": [ { - "include": "#comment" + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(var|let)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*", + "beginCaptures": { + "1": { + "name": "keyword.control.export.ts" + }, + "2": { + "name": "storage.modifier.ts" + }, + "3": { + "name": "storage.type.ts" + } + }, + "end": "(?=\\S)" }, { "include": "#destructuring-variable" @@ -324,13 +309,116 @@ { "include": "#var-single-variable" }, + { + "include": "#variable-initializer" + }, + { + "include": "#comment" + }, + { + "begin": "(,)\\s*((?!\\S)|(?=\\/\\/))", + "beginCaptures": { + "1": { + "name": "punctuation.separator.comma.ts" + } + }, + "end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))", + "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, + { + "include": "#comment" + }, + { + "include": "#destructuring-variable" + }, + { + "include": "#var-single-variable" + }, + { + "include": "#punctuation-comma" + } + ] + }, { "include": "#punctuation-comma" } ] }, { - "include": "#punctuation-comma" + "name": "meta.var.expr.ts", + "begin": "(?=(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))", + "beginCaptures": { + "1": { + "name": "keyword.control.export.ts" + }, + "2": { + "name": "storage.modifier.ts" + }, + "3": { + "name": "storage.type.ts" + } + }, + "end": "(?!(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))((?=;|}|(\\s+(of|in)\\s+)|^\\s*$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|((?<!^const|[^\\._$[:alnum:]]const)(?=\\s*$)))", + "patterns": [ + { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*", + "beginCaptures": { + "1": { + "name": "keyword.control.export.ts" + }, + "2": { + "name": "storage.modifier.ts" + }, + "3": { + "name": "storage.type.ts" + } + }, + "end": "(?=\\S)" + }, + { + "include": "#destructuring-const" + }, + { + "include": "#var-single-const" + }, + { + "include": "#variable-initializer" + }, + { + "include": "#comment" + }, + { + "begin": "(,)\\s*((?!\\S)|(?=\\/\\/))", + "beginCaptures": { + "1": { + "name": "punctuation.separator.comma.ts" + } + }, + "end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))", + "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, + { + "include": "#comment" + }, + { + "include": "#destructuring-const" + }, + { + "include": "#var-single-const" + }, + { + "include": "#punctuation-comma" + } + ] + }, + { + "include": "#punctuation-comma" + } + ] } ] }, @@ -383,6 +471,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 +554,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.ts", + "begin": "(?<!=|:|^of|[^\\._$[:alnum:]]of|^in|[^\\._$[:alnum:]]in)\\s*(?=\\{)", + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#object-binding-pattern-const" + }, + { + "include": "#type-annotation" + }, + { + "include": "#comment" + } + ] + }, + { + "name": "meta.array-binding-pattern-variable.ts", + "begin": "(?<!=|:|^of|[^\\._$[:alnum:]]of|^in|[^\\._$[:alnum:]]in)\\s*(?=\\[)", + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#array-binding-pattern-const" + }, + { + "include": "#type-annotation" + }, + { + "include": "#comment" + } + ] + } + ] + }, "object-binding-element": { "patterns": [ { @@ -463,6 +621,37 @@ } ] }, + "object-binding-element-const": { + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?x)(?=((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", + "end": "(?=,|\\})", + "patterns": [ + { + "include": "#object-binding-element-propertyName" + }, + { + "include": "#binding-element-const" + } + ] + }, + { + "include": "#object-binding-pattern-const" + }, + { + "include": "#destructuring-variable-rest-const" + }, + { + "include": "#variable-initializer" + }, + { + "include": "#punctuation-comma" + } + ] + }, "object-binding-element-propertyName": { "begin": "(?x)(?=((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(:)", @@ -509,6 +698,28 @@ } ] }, + "binding-element-const": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#string" + }, + { + "include": "#object-binding-pattern-const" + }, + { + "include": "#array-binding-pattern-const" + }, + { + "include": "#destructuring-variable-rest-const" + }, + { + "include": "#variable-initializer" + } + ] + }, "destructuring-variable-rest": { "match": "(?:(\\.\\.\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)", "captures": { @@ -520,6 +731,17 @@ } } }, + "destructuring-variable-rest-const": { + "match": "(?:(\\.\\.\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)", + "captures": { + "1": { + "name": "keyword.operator.rest.ts" + }, + "2": { + "name": "meta.definition.variable.ts variable.other.constant.ts" + } + } + }, "object-binding-pattern": { "begin": "(?:(\\.\\.\\.)\\s*)?(\\{)", "beginCaptures": { @@ -542,6 +764,28 @@ } ] }, + "object-binding-pattern-const": { + "begin": "(?:(\\.\\.\\.)\\s*)?(\\{)", + "beginCaptures": { + "1": { + "name": "keyword.operator.rest.ts" + }, + "2": { + "name": "punctuation.definition.binding-pattern.object.ts" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.definition.binding-pattern.object.ts" + } + }, + "patterns": [ + { + "include": "#object-binding-element-const" + } + ] + }, "array-binding-pattern": { "begin": "(?:(\\.\\.\\.)\\s*)?(\\[)", "beginCaptures": { @@ -567,6 +811,31 @@ } ] }, + "array-binding-pattern-const": { + "begin": "(?:(\\.\\.\\.)\\s*)?(\\[)", + "beginCaptures": { + "1": { + "name": "keyword.operator.rest.ts" + }, + "2": { + "name": "punctuation.definition.binding-pattern.array.ts" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.binding-pattern.array.ts" + } + }, + "patterns": [ + { + "include": "#binding-element-const" + }, + { + "include": "#punctuation-comma" + } + ] + }, "parameter-name": { "patterns": [ { @@ -623,9 +892,12 @@ "patterns": [ { "name": "meta.parameter.object-binding-pattern.ts", - "begin": "(?<!=|:)\\s*(\\{)", + "begin": "(?<!=|:)\\s*(?:(\\.\\.\\.)\\s*)?(\\{)", "beginCaptures": { "1": { + "name": "keyword.operator.rest.ts" + }, + "2": { "name": "punctuation.definition.binding-pattern.object.ts" } }, @@ -643,9 +915,12 @@ }, { "name": "meta.paramter.array-binding-pattern.ts", - "begin": "(?<!=|:)\\s*(\\[)", + "begin": "(?<!=|:)\\s*(?:(\\.\\.\\.)\\s*)?(\\[)", "beginCaptures": { "1": { + "name": "keyword.operator.rest.ts" + }, + "2": { "name": "punctuation.definition.binding-pattern.array.ts" } }, @@ -779,56 +1054,50 @@ }, "field-declaration": { "name": "meta.field.declaration.ts", - "begin": "(?x)(?<!\\()(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(readonly)\\s+)?(?=\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))", + "begin": "(?x)(?<!\\()(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(readonly)\\s+)?(?=\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:|;|,|$))", "beginCaptures": { "1": { "name": "storage.modifier.ts" } }, - "end": "(?x)(?=\\}|;|,|$|(^(?!\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))))|(?<=\\})", + "end": "(?x)(?=\\}|;|,|$|(^(?!\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:|;|,|$))))|(?<=\\})", "patterns": [ { "include": "#variable-initializer" }, { - "begin": "(?x)(?=((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))", - "end": "(?x)(?=[};,=]|$|(^(?!\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))))|(?<=\\})", - "patterns": [ - { - "include": "#type-annotation" + "include": "#type-annotation" + }, + { + "include": "#string" + }, + { + "include": "#array-literal" + }, + { + "include": "#numeric-literal" + }, + { + "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*([_$[: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" }, - { - "include": "#string" - }, - { - "include": "#array-literal" - }, - { - "include": "#numeric-literal" - }, - { - "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*([_$[: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" - }, - "2": { - "name": "keyword.operator.optional.ts" - } - } - }, - { - "name": "meta.definition.property.ts variable.object.property.ts", - "match": "[_$[:alpha:]][_$[:alnum:]]*" - }, - { - "name": "keyword.operator.optional.ts", - "match": "\\?" + "2": { + "name": "keyword.operator.optional.ts" } - ] + } + }, + { + "name": "meta.definition.property.ts variable.object.property.ts", + "match": "[_$[:alpha:]][_$[:alnum:]]*" + }, + { + "name": "keyword.operator.optional.ts", + "match": "\\?" } ] }, @@ -866,21 +1135,24 @@ }, "function-declaration": { "name": "meta.function.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(export)\\s+)?(?:(async)\\s+)?(function\\b)(?:\\s*(\\*))?(?:(?:\\s+|(?<=\\*))([_$[:alpha:]][_$[:alnum:]]*))?\\s*", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?(?:(async)\\s+)?(function\\b)(?:\\s*(\\*))?(?:(?:\\s+|(?<=\\*))([_$[:alpha:]][_$[:alnum:]]*))?\\s*", "beginCaptures": { "1": { "name": "keyword.control.export.ts" }, "2": { - "name": "storage.modifier.async.ts" + "name": "storage.modifier.ts" }, "3": { - "name": "storage.type.function.ts" + "name": "storage.modifier.async.ts" }, "4": { - "name": "keyword.generator.asterisk.ts" + "name": "storage.type.function.ts" }, "5": { + "name": "keyword.generator.asterisk.ts" + }, + "6": { "name": "meta.definition.function.ts entity.name.function.ts" } }, @@ -916,6 +1188,9 @@ { "include": "#function-name" }, + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#function-body" } @@ -1279,7 +1554,7 @@ }, "class-declaration": { "name": "meta.class.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(export)\\s+)?\\b(?:(abstract)\\s+)?\\b(class)\\b(?=\\s+|/[/*])", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(?:(abstract)\\s+)?\\b(class)\\b(?=\\s+|/[/*])", "beginCaptures": { "1": { "name": "keyword.control.export.ts" @@ -1288,6 +1563,9 @@ "name": "storage.modifier.ts" }, "3": { + "name": "storage.modifier.ts" + }, + "4": { "name": "storage.type.class.ts" } }, @@ -1339,7 +1617,7 @@ }, "interface-declaration": { "name": "meta.interface.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(export)\\s+)?\\b(?:(abstract)\\s+)?\\b(interface)\\b(?=\\s+|/[/*])", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(?:(abstract)\\s+)?\\b(interface)\\b(?=\\s+|/[/*])", "beginCaptures": { "1": { "name": "keyword.control.export.ts" @@ -1348,6 +1626,9 @@ "name": "storage.modifier.ts" }, "3": { + "name": "storage.modifier.ts" + }, + "4": { "name": "storage.type.interface.ts" } }, @@ -1501,7 +1782,7 @@ }, "enum-declaration": { "name": "meta.enum.declaration.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:\\b(const)\\s+)?\\b(enum)\\s+([_$[:alpha:]][_$[:alnum:]]*)", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?(?:\\b(const)\\s+)?\\b(enum)\\s+([_$[:alpha:]][_$[:alnum:]]*)", "beginCaptures": { "1": { "name": "keyword.control.export.ts" @@ -1510,9 +1791,12 @@ "name": "storage.modifier.ts" }, "3": { - "name": "storage.type.enum.ts" + "name": "storage.modifier.ts" }, "4": { + "name": "storage.type.enum.ts" + }, + "5": { "name": "entity.name.type.enum.ts" } }, @@ -1582,16 +1866,19 @@ }, "namespace-declaration": { "name": "meta.namespace.declaration.ts", - "begin": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(namespace|module)\\s+(?=[_$[:alpha:]\"'`]))", + "begin": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(namespace|module)\\s+(?=[_$[:alpha:]\"'`]))", "beginCaptures": { "1": { "name": "keyword.control.export.ts" }, "2": { + "name": "storage.modifier.ts" + }, + "3": { "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#comment" @@ -1613,19 +1900,22 @@ }, "type-alias-declaration": { "name": "meta.type.declaration.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(type)\\b\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(type)\\b\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*", "beginCaptures": { "1": { "name": "keyword.control.export.ts" }, "2": { - "name": "storage.type.type.ts" + "name": "storage.modifier.ts" }, "3": { + "name": "storage.type.type.ts" + }, + "4": { "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#comment" @@ -1640,7 +1930,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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#type" @@ -1653,24 +1943,27 @@ "patterns": [ { "name": "meta.import-equals.external.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(require)\\s*(\\()", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(require)\\s*(\\()", "beginCaptures": { "1": { "name": "keyword.control.export.ts" }, "2": { - "name": "keyword.control.import.ts" + "name": "storage.modifier.ts" }, "3": { - "name": "variable.other.readwrite.alias.ts" + "name": "keyword.control.import.ts" }, "4": { - "name": "keyword.operator.assignment.ts" + "name": "variable.other.readwrite.alias.ts" }, "5": { - "name": "keyword.control.require.ts" + "name": "keyword.operator.assignment.ts" }, "6": { + "name": "keyword.control.require.ts" + }, + "7": { "name": "meta.brace.round.ts" } }, @@ -1691,18 +1984,21 @@ }, { "name": "meta.import-equals.internal.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(?!require\\b)", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(?!require\\b)", "beginCaptures": { "1": { "name": "keyword.control.export.ts" }, "2": { - "name": "keyword.control.import.ts" + "name": "storage.modifier.ts" }, "3": { - "name": "variable.other.readwrite.alias.ts" + "name": "keyword.control.import.ts" }, "4": { + "name": "variable.other.readwrite.alias.ts" + }, + "5": { "name": "keyword.operator.assignment.ts" } }, @@ -1735,12 +2031,15 @@ }, "import-declaration": { "name": "meta.import.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(import)(?!\\s*[:\\(])(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(import)(?!\\s*[:\\(])(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", "beginCaptures": { "1": { "name": "keyword.control.export.ts" }, "2": { + "name": "storage.modifier.ts" + }, + "3": { "name": "keyword.control.import.ts" } }, @@ -1804,8 +2103,11 @@ "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ + { + "include": "#interface-declaration" + }, { "include": "#expression" } @@ -1813,13 +2115,13 @@ }, { "name": "meta.export.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(export)(?!\\s*:)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(export)(?!\\s*:)((?=\\s*[\\{*])|((?=\\s*[_$[:alpha:]][_$[:alnum:]]*(\\s|,))(?!\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b)))", "beginCaptures": { "0": { "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#import-export-declaration" @@ -2312,13 +2614,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.ts", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2602,7 +2904,7 @@ "name": "keyword.control.as.ts" } }, - "end": "(?=$|^|[;,:})\\]]|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", + "end": "(?=$|^|[;,:})\\]]|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+)|(\\s+\\<))", "patterns": [ { "include": "#type" @@ -3291,6 +3593,10 @@ }, { "include": "#punctuation-comma" + }, + { + "name": "keyword.operator.assignment.ts", + "match": "(=)(?!>)" } ] }, @@ -3659,13 +3965,10 @@ "include": "#typeof-operator" }, { - "begin": "(?:([&|])|(=(?!>)))(?=\\s*\\{)", + "begin": "([&|\\*])(?=\\s*\\{)", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.ts" - }, - "2": { - "name": "keyword.operator.assignment.ts" } }, "end": "(?<=\\})", @@ -3676,13 +3979,10 @@ ] }, { - "begin": "([&|])|(=(?!>))", + "begin": "[&|\\*]", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.ts" - }, - "2": { - "name": "keyword.operator.assignment.ts" } }, "end": "(?=\\S)" @@ -3814,7 +4114,7 @@ "patterns": [ { "name": "string.template.ts", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.ts" @@ -3880,7 +4180,7 @@ "patterns": [ { "name": "string.regexp.ts", - "begin": "(?<!\\+\\+|--)(?<=[=(:,\\[?+!]|^return|[^\\._$[:alnum:]]return|^case|[^\\._$[:alnum:]]case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<!\\+\\+|--|})(?<=[=(:,\\[?+!]|^return|[^\\._$[:alnum:]]return|^case|[^\\._$[:alnum:]]case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.ts" @@ -3903,7 +4203,7 @@ }, { "name": "string.regexp.ts", - "begin": "(?<![_$[:alnum:])\\]]|\\+\\+|--)\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<![_$[:alnum:])\\]]|\\+\\+|--|})\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.ts" @@ -4146,11 +4446,33 @@ "name": "punctuation.decorator.internaldeclaration.ts" } }, - "end": "(?=^)", + "end": "(?=$)", "contentName": "comment.line.double-slash.ts" } ] }, + "single-line-comment-consuming-line-ending": { + "begin": "(^[ \\t]+)?((//)(?:\\s*((@)internal)(?=\\s|$))?)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.ts" + }, + "2": { + "name": "comment.line.double-slash.ts" + }, + "3": { + "name": "punctuation.definition.comment.ts" + }, + "4": { + "name": "storage.type.internaldeclaration.ts" + }, + "5": { + "name": "punctuation.decorator.internaldeclaration.ts" + } + }, + "end": "(?=^)", + "contentName": "comment.line.double-slash.ts" + }, "directives": { "name": "comment.line.triple-slash.directive.ts", "begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)", @@ -4159,7 +4481,7 @@ "name": "punctuation.definition.comment.ts" } }, - "end": "(?=^)", + "end": "(?=$)", "patterns": [ { "name": "meta.tag.ts", diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index 8d1c10a62b7..41b9e17d5e2 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/27425437b2144f43607047ae7ee9b826e36856a5", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/4407e8d57037cdb02e5fb670bcf318e0daebb40f", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -93,6 +93,10 @@ }, { "include": "#export-declaration" + }, + { + "name": "storage.modifier.tsx", + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(declare|export)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" } ] }, @@ -146,10 +150,6 @@ { "name": "keyword.other.debugger.tsx", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(debugger)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" - }, - { - "name": "storage.modifier.tsx", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(declare)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" } ] }, @@ -285,41 +285,26 @@ ] }, "var-expr": { - "name": "meta.var.expr.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(var|let|const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", - "beginCaptures": { - "1": { - "name": "keyword.control.export.tsx" - }, - "2": { - "name": "storage.type.tsx" - } - }, - "end": "(?=$|^|;|}|(\\s+(of|in)\\s+))", "patterns": [ { - "include": "#destructuring-variable" - }, - { - "include": "#var-single-variable" - }, - { - "include": "#variable-initializer" - }, - { - "include": "#comment" - }, - { - "begin": "(,)\\s*(?!\\S)", - "beginCaptures": { - "1": { - "name": "punctuation.separator.comma.tsx" - } - }, - "end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))", + "name": "meta.var.expr.tsx", + "begin": "(?=(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(var|let)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))", + "end": "(?!(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(var|let)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))((?=;|}|(\\s+(of|in)\\s+)|^\\s*$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|((?<!^let|[^\\._$[:alnum:]]let|^var|[^\\._$[:alnum:]]var)(?=\\s*$)))", "patterns": [ { - "include": "#comment" + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(var|let)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*", + "beginCaptures": { + "1": { + "name": "keyword.control.export.tsx" + }, + "2": { + "name": "storage.modifier.tsx" + }, + "3": { + "name": "storage.type.tsx" + } + }, + "end": "(?=\\S)" }, { "include": "#destructuring-variable" @@ -327,13 +312,116 @@ { "include": "#var-single-variable" }, + { + "include": "#variable-initializer" + }, + { + "include": "#comment" + }, + { + "begin": "(,)\\s*((?!\\S)|(?=\\/\\/))", + "beginCaptures": { + "1": { + "name": "punctuation.separator.comma.tsx" + } + }, + "end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))", + "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, + { + "include": "#comment" + }, + { + "include": "#destructuring-variable" + }, + { + "include": "#var-single-variable" + }, + { + "include": "#punctuation-comma" + } + ] + }, { "include": "#punctuation-comma" } ] }, { - "include": "#punctuation-comma" + "name": "meta.var.expr.tsx", + "begin": "(?=(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))", + "beginCaptures": { + "1": { + "name": "keyword.control.export.tsx" + }, + "2": { + "name": "storage.modifier.tsx" + }, + "3": { + "name": "storage.type.tsx" + } + }, + "end": "(?!(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))((?=;|}|(\\s+(of|in)\\s+)|^\\s*$|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|((?<!^const|[^\\._$[:alnum:]]const)(?=\\s*$)))", + "patterns": [ + { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(const(?!\\s+enum\\b))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*", + "beginCaptures": { + "1": { + "name": "keyword.control.export.tsx" + }, + "2": { + "name": "storage.modifier.tsx" + }, + "3": { + "name": "storage.type.tsx" + } + }, + "end": "(?=\\S)" + }, + { + "include": "#destructuring-const" + }, + { + "include": "#var-single-const" + }, + { + "include": "#variable-initializer" + }, + { + "include": "#comment" + }, + { + "begin": "(,)\\s*((?!\\S)|(?=\\/\\/))", + "beginCaptures": { + "1": { + "name": "punctuation.separator.comma.tsx" + } + }, + "end": "(?<!,)(((?==|;|}|(\\s+(of|in)\\s+)|^\\s*$))|((?<=\\S)(?=\\s*$)))", + "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, + { + "include": "#comment" + }, + { + "include": "#destructuring-const" + }, + { + "include": "#var-single-const" + }, + { + "include": "#punctuation-comma" + } + ] + }, + { + "include": "#punctuation-comma" + } + ] } ] }, @@ -386,6 +474,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 +557,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.tsx", + "begin": "(?<!=|:|^of|[^\\._$[:alnum:]]of|^in|[^\\._$[:alnum:]]in)\\s*(?=\\{)", + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#object-binding-pattern-const" + }, + { + "include": "#type-annotation" + }, + { + "include": "#comment" + } + ] + }, + { + "name": "meta.array-binding-pattern-variable.tsx", + "begin": "(?<!=|:|^of|[^\\._$[:alnum:]]of|^in|[^\\._$[:alnum:]]in)\\s*(?=\\[)", + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#array-binding-pattern-const" + }, + { + "include": "#type-annotation" + }, + { + "include": "#comment" + } + ] + } + ] + }, "object-binding-element": { "patterns": [ { @@ -466,6 +624,37 @@ } ] }, + "object-binding-element-const": { + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?x)(?=((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", + "end": "(?=,|\\})", + "patterns": [ + { + "include": "#object-binding-element-propertyName" + }, + { + "include": "#binding-element-const" + } + ] + }, + { + "include": "#object-binding-pattern-const" + }, + { + "include": "#destructuring-variable-rest-const" + }, + { + "include": "#variable-initializer" + }, + { + "include": "#punctuation-comma" + } + ] + }, "object-binding-element-propertyName": { "begin": "(?x)(?=((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(:)", @@ -512,6 +701,28 @@ } ] }, + "binding-element-const": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#string" + }, + { + "include": "#object-binding-pattern-const" + }, + { + "include": "#array-binding-pattern-const" + }, + { + "include": "#destructuring-variable-rest-const" + }, + { + "include": "#variable-initializer" + } + ] + }, "destructuring-variable-rest": { "match": "(?:(\\.\\.\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)", "captures": { @@ -523,6 +734,17 @@ } } }, + "destructuring-variable-rest-const": { + "match": "(?:(\\.\\.\\.)\\s*)?([_$[:alpha:]][_$[:alnum:]]*)", + "captures": { + "1": { + "name": "keyword.operator.rest.tsx" + }, + "2": { + "name": "meta.definition.variable.tsx variable.other.constant.tsx" + } + } + }, "object-binding-pattern": { "begin": "(?:(\\.\\.\\.)\\s*)?(\\{)", "beginCaptures": { @@ -545,6 +767,28 @@ } ] }, + "object-binding-pattern-const": { + "begin": "(?:(\\.\\.\\.)\\s*)?(\\{)", + "beginCaptures": { + "1": { + "name": "keyword.operator.rest.tsx" + }, + "2": { + "name": "punctuation.definition.binding-pattern.object.tsx" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.definition.binding-pattern.object.tsx" + } + }, + "patterns": [ + { + "include": "#object-binding-element-const" + } + ] + }, "array-binding-pattern": { "begin": "(?:(\\.\\.\\.)\\s*)?(\\[)", "beginCaptures": { @@ -570,6 +814,31 @@ } ] }, + "array-binding-pattern-const": { + "begin": "(?:(\\.\\.\\.)\\s*)?(\\[)", + "beginCaptures": { + "1": { + "name": "keyword.operator.rest.tsx" + }, + "2": { + "name": "punctuation.definition.binding-pattern.array.tsx" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.binding-pattern.array.tsx" + } + }, + "patterns": [ + { + "include": "#binding-element-const" + }, + { + "include": "#punctuation-comma" + } + ] + }, "parameter-name": { "patterns": [ { @@ -626,9 +895,12 @@ "patterns": [ { "name": "meta.parameter.object-binding-pattern.tsx", - "begin": "(?<!=|:)\\s*(\\{)", + "begin": "(?<!=|:)\\s*(?:(\\.\\.\\.)\\s*)?(\\{)", "beginCaptures": { "1": { + "name": "keyword.operator.rest.tsx" + }, + "2": { "name": "punctuation.definition.binding-pattern.object.tsx" } }, @@ -646,9 +918,12 @@ }, { "name": "meta.paramter.array-binding-pattern.tsx", - "begin": "(?<!=|:)\\s*(\\[)", + "begin": "(?<!=|:)\\s*(?:(\\.\\.\\.)\\s*)?(\\[)", "beginCaptures": { "1": { + "name": "keyword.operator.rest.tsx" + }, + "2": { "name": "punctuation.definition.binding-pattern.array.tsx" } }, @@ -782,56 +1057,50 @@ }, "field-declaration": { "name": "meta.field.declaration.tsx", - "begin": "(?x)(?<!\\()(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(readonly)\\s+)?(?=\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))", + "begin": "(?x)(?<!\\()(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(readonly)\\s+)?(?=\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:|;|,|$))", "beginCaptures": { "1": { "name": "storage.modifier.tsx" } }, - "end": "(?x)(?=\\}|;|,|$|(^(?!\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))))|(?<=\\})", + "end": "(?x)(?=\\}|;|,|$|(^(?!\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:|;|,|$))))|(?<=\\})", "patterns": [ { "include": "#variable-initializer" }, { - "begin": "(?x)(?=((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))", - "end": "(?x)(?=[};,=]|$|(^(?!\\s*((\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$))|(\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$))|(\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*\\b)| # .1\n (?:\\b[0-9][0-9_]*\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\?\\s*)?(=|:))))|(?<=\\})", - "patterns": [ - { - "include": "#type-annotation" + "include": "#type-annotation" + }, + { + "include": "#string" + }, + { + "include": "#array-literal" + }, + { + "include": "#numeric-literal" + }, + { + "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*([\\{\\[]\\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" }, - { - "include": "#string" - }, - { - "include": "#array-literal" - }, - { - "include": "#numeric-literal" - }, - { - "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*([\\{\\[]\\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" - }, - "2": { - "name": "keyword.operator.optional.tsx" - } - } - }, - { - "name": "meta.definition.property.tsx variable.object.property.tsx", - "match": "[_$[:alpha:]][_$[:alnum:]]*" - }, - { - "name": "keyword.operator.optional.tsx", - "match": "\\?" + "2": { + "name": "keyword.operator.optional.tsx" } - ] + } + }, + { + "name": "meta.definition.property.tsx variable.object.property.tsx", + "match": "[_$[:alpha:]][_$[:alnum:]]*" + }, + { + "name": "keyword.operator.optional.tsx", + "match": "\\?" } ] }, @@ -869,21 +1138,24 @@ }, "function-declaration": { "name": "meta.function.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(export)\\s+)?(?:(async)\\s+)?(function\\b)(?:\\s*(\\*))?(?:(?:\\s+|(?<=\\*))([_$[:alpha:]][_$[:alnum:]]*))?\\s*", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?(?:(async)\\s+)?(function\\b)(?:\\s*(\\*))?(?:(?:\\s+|(?<=\\*))([_$[:alpha:]][_$[:alnum:]]*))?\\s*", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" }, "2": { - "name": "storage.modifier.async.tsx" + "name": "storage.modifier.tsx" }, "3": { - "name": "storage.type.function.tsx" + "name": "storage.modifier.async.tsx" }, "4": { - "name": "keyword.generator.asterisk.tsx" + "name": "storage.type.function.tsx" }, "5": { + "name": "keyword.generator.asterisk.tsx" + }, + "6": { "name": "meta.definition.function.tsx entity.name.function.tsx" } }, @@ -919,6 +1191,9 @@ { "include": "#function-name" }, + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#function-body" } @@ -1282,7 +1557,7 @@ }, "class-declaration": { "name": "meta.class.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(export)\\s+)?\\b(?:(abstract)\\s+)?\\b(class)\\b(?=\\s+|/[/*])", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(?:(abstract)\\s+)?\\b(class)\\b(?=\\s+|/[/*])", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" @@ -1291,6 +1566,9 @@ "name": "storage.modifier.tsx" }, "3": { + "name": "storage.modifier.tsx" + }, + "4": { "name": "storage.type.class.tsx" } }, @@ -1342,7 +1620,7 @@ }, "interface-declaration": { "name": "meta.interface.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(export)\\s+)?\\b(?:(abstract)\\s+)?\\b(interface)\\b(?=\\s+|/[/*])", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(?:(abstract)\\s+)?\\b(interface)\\b(?=\\s+|/[/*])", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" @@ -1351,6 +1629,9 @@ "name": "storage.modifier.tsx" }, "3": { + "name": "storage.modifier.tsx" + }, + "4": { "name": "storage.type.interface.tsx" } }, @@ -1504,7 +1785,7 @@ }, "enum-declaration": { "name": "meta.enum.declaration.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:\\b(const)\\s+)?\\b(enum)\\s+([_$[:alpha:]][_$[:alnum:]]*)", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?(?:\\b(const)\\s+)?\\b(enum)\\s+([_$[:alpha:]][_$[:alnum:]]*)", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" @@ -1513,9 +1794,12 @@ "name": "storage.modifier.tsx" }, "3": { - "name": "storage.type.enum.tsx" + "name": "storage.modifier.tsx" }, "4": { + "name": "storage.type.enum.tsx" + }, + "5": { "name": "entity.name.type.enum.tsx" } }, @@ -1585,16 +1869,19 @@ }, "namespace-declaration": { "name": "meta.namespace.declaration.tsx", - "begin": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(namespace|module)\\s+(?=[_$[:alpha:]\"'`]))", + "begin": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(namespace|module)\\s+(?=[_$[:alpha:]\"'`]))", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" }, "2": { + "name": "storage.modifier.tsx" + }, + "3": { "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#comment" @@ -1616,19 +1903,22 @@ }, "type-alias-declaration": { "name": "meta.type.declaration.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(type)\\b\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(type)\\b\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" }, "2": { - "name": "storage.type.type.tsx" + "name": "storage.modifier.tsx" }, "3": { + "name": "storage.type.type.tsx" + }, + "4": { "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#comment" @@ -1643,7 +1933,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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#type" @@ -1656,24 +1946,27 @@ "patterns": [ { "name": "meta.import-equals.external.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(require)\\s*(\\()", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(require)\\s*(\\()", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" }, "2": { - "name": "keyword.control.import.tsx" + "name": "storage.modifier.tsx" }, "3": { - "name": "variable.other.readwrite.alias.tsx" + "name": "keyword.control.import.tsx" }, "4": { - "name": "keyword.operator.assignment.tsx" + "name": "variable.other.readwrite.alias.tsx" }, "5": { - "name": "keyword.control.require.tsx" + "name": "keyword.operator.assignment.tsx" }, "6": { + "name": "keyword.control.require.tsx" + }, + "7": { "name": "meta.brace.round.tsx" } }, @@ -1694,18 +1987,21 @@ }, { "name": "meta.import-equals.internal.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(?!require\\b)", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(import)\\s+([_$[:alpha:]][_$[:alnum:]]*)\\s*(=)\\s*(?!require\\b)", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" }, "2": { - "name": "keyword.control.import.tsx" + "name": "storage.modifier.tsx" }, "3": { - "name": "variable.other.readwrite.alias.tsx" + "name": "keyword.control.import.tsx" }, "4": { + "name": "variable.other.readwrite.alias.tsx" + }, + "5": { "name": "keyword.operator.assignment.tsx" } }, @@ -1738,12 +2034,15 @@ }, "import-declaration": { "name": "meta.import.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?\\b(import)(?!\\s*[:\\(])(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(\\bexport)\\s+)?(?:(\\bdeclare)\\s+)?\\b(import)(?!\\s*[:\\(])(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", "beginCaptures": { "1": { "name": "keyword.control.export.tsx" }, "2": { + "name": "storage.modifier.tsx" + }, + "3": { "name": "keyword.control.import.tsx" } }, @@ -1807,8 +2106,11 @@ "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ + { + "include": "#interface-declaration" + }, { "include": "#expression" } @@ -1816,13 +2118,13 @@ }, { "name": "meta.export.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(export)(?!\\s*:)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(export)(?!\\s*:)((?=\\s*[\\{*])|((?=\\s*[_$[:alpha:]][_$[:alnum:]]*(\\s|,))(?!\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b)))", "beginCaptures": { "0": { "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|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", "patterns": [ { "include": "#import-export-declaration" @@ -2315,13 +2617,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.tsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -2568,7 +2870,7 @@ "name": "keyword.control.as.tsx" } }, - "end": "(?=$|^|[;,:})\\]]|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", + "end": "(?=$|^|[;,:})\\]]|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+)|(\\s+\\<))", "patterns": [ { "include": "#type" @@ -3257,6 +3559,10 @@ }, { "include": "#punctuation-comma" + }, + { + "name": "keyword.operator.assignment.tsx", + "match": "(=)(?!>)" } ] }, @@ -3625,13 +3931,10 @@ "include": "#typeof-operator" }, { - "begin": "(?:([&|])|(=(?!>)))(?=\\s*\\{)", + "begin": "([&|\\*])(?=\\s*\\{)", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.tsx" - }, - "2": { - "name": "keyword.operator.assignment.tsx" } }, "end": "(?<=\\})", @@ -3642,13 +3945,10 @@ ] }, { - "begin": "([&|])|(=(?!>))", + "begin": "[&|\\*]", "beginCaptures": { - "1": { + "0": { "name": "keyword.operator.type.tsx" - }, - "2": { - "name": "keyword.operator.assignment.tsx" } }, "end": "(?=\\S)" @@ -3780,7 +4080,7 @@ "patterns": [ { "name": "string.template.tsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[=]|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.tsx" @@ -3846,7 +4146,7 @@ "patterns": [ { "name": "string.regexp.tsx", - "begin": "(?<!\\+\\+|--)(?<=[=(:,\\[?+!]|^return|[^\\._$[:alnum:]]return|^case|[^\\._$[:alnum:]]case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<!\\+\\+|--|})(?<=[=(:,\\[?+!]|^return|[^\\._$[:alnum:]]return|^case|[^\\._$[:alnum:]]case|=>|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.tsx" @@ -3869,7 +4169,7 @@ }, { "name": "string.regexp.tsx", - "begin": "(?<![_$[:alnum:])\\]]|\\+\\+|--)\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?<![_$[:alnum:])\\]]|\\+\\+|--|})\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.tsx" @@ -4112,11 +4412,33 @@ "name": "punctuation.decorator.internaldeclaration.tsx" } }, - "end": "(?=^)", + "end": "(?=$)", "contentName": "comment.line.double-slash.tsx" } ] }, + "single-line-comment-consuming-line-ending": { + "begin": "(^[ \\t]+)?((//)(?:\\s*((@)internal)(?=\\s|$))?)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.tsx" + }, + "2": { + "name": "comment.line.double-slash.tsx" + }, + "3": { + "name": "punctuation.definition.comment.tsx" + }, + "4": { + "name": "storage.type.internaldeclaration.tsx" + }, + "5": { + "name": "punctuation.decorator.internaldeclaration.tsx" + } + }, + "end": "(?=^)", + "contentName": "comment.line.double-slash.tsx" + }, "directives": { "name": "comment.line.triple-slash.directive.tsx", "begin": "^(///)\\s*(?=<(reference|amd-dependency|amd-module)(\\s+(path|types|no-default-lib|lib|name)\\s*=\\s*((\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")))+\\s*/>\\s*$)", @@ -4125,7 +4447,7 @@ "name": "punctuation.definition.comment.tsx" } }, - "end": "(?=^)", + "end": "(?=$)", "patterns": [ { "name": "meta.tag.tsx", @@ -4608,8 +4930,8 @@ ] }, "jsx-tag-without-attributes-in-expression": { - "begin": "(?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>))", + "begin": "(?<!\\+\\+|--)(?<=[({\\[,?=>:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))?\\s*(>))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))?\\s*(>))", "patterns": [ { "include": "#jsx-tag-without-attributes" @@ -4618,8 +4940,8 @@ }, "jsx-tag-without-attributes": { "name": "meta.tag.without-attributes.tsx", - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>)", - "end": "(</)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>)", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))?\\s*(>)", + "end": "(</)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))?\\s*(>)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.tsx" @@ -4668,8 +4990,8 @@ ] }, "jsx-tag-in-expression": { - "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*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))", + "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:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))", "patterns": [ { "include": "#jsx-tag" @@ -4678,8 +5000,8 @@ }, "jsx-tag": { "name": "meta.tag.tsx", - "begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))", - "end": "(/>)|(?:(</)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))?\\s*(>))", + "begin": "(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>))", + "end": "(/>)|(?:(</)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))?\\s*(>))", "endCaptures": { "1": { "name": "punctuation.definition.tag.end.tsx" @@ -4705,7 +5027,7 @@ }, "patterns": [ { - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$a-zA-Z][-$\\w.]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>)", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?<!\\.|-)(:))?((?:[a-z][a-z0-9]*|([_$[:alpha:]][-$[:alnum:].]*))(?<!\\.|-))(?=((<\\s*)|(\\s+))(?!\\?)|\\/?>)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.tsx" @@ -4838,7 +5160,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..bf74c1c2174 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" } @@ -1860,7 +1860,7 @@ }, { "c": " ", - "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts", + "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts meta.field.declaration.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1871,7 +1871,7 @@ }, { "c": "world", - "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts variable.other.readwrite.ts", + "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts meta.field.declaration.ts meta.definition.property.ts variable.object.property.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", diff --git a/extensions/typescript-language-features/.vscodeignore b/extensions/typescript-language-features/.vscodeignore index d257eda277b..7992329e90a 100644 --- a/extensions/typescript-language-features/.vscodeignore +++ b/extensions/typescript-language-features/.vscodeignore @@ -1,4 +1,7 @@ build/** src/** test/** -tsconfig.json \ No newline at end of file +tsconfig.json +node_modules/jsonc-parser/** +node_modules/semver/** + 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/extension.webpack.config.js b/extensions/typescript-language-features/extension.webpack.config.js new file mode 100644 index 00000000000..664dfb35f6f --- /dev/null +++ b/extensions/typescript-language-features/extension.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + node: { + __dirname: false, + }, + resolve: { + mainFields: ['module', 'main'] + }, + entry: { + extension: './src/extension.ts', + } +}); diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 3692fc231fa..50a132b565b 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -17,13 +17,13 @@ ], "dependencies": { "jsonc-parser": "^2.0.1", - "semver": "4.3.6", - "vscode-extension-telemetry": "0.0.17", + "semver": "5.5.1", + "vscode-extension-telemetry": "0.0.18", "vscode-nls": "^3.2.4" }, "devDependencies": { "@types/node": "8.0.33", - "@types/semver": "5.4.0", + "@types/semver": "^5.5.0", "vscode": "^1.1.10" }, "scripts": { @@ -42,10 +42,7 @@ "onCommand:typescript.goToProjectConfig", "onCommand:typescript.openTsServerLog", "onCommand:workbench.action.tasks.runTask", - "workspaceContains:**/tsconfig.json", - "workspaceContains:**/jsconfig.json", - "workspaceContains:**/tsconfig.*.json", - "workspaceContains:**/jsconfig.*.json" + "onLanguage:jsonc" ], "main": "./out/extension", "contributes": { @@ -73,7 +70,10 @@ "type": "boolean", "default": false, "description": "%typescript.disableAutomaticTypeAcquisition%", - "scope": "window" + "scope": "window", + "tags": [ + "usesOnlineServices" + ] }, "typescript.npm": { "type": [ @@ -366,7 +366,7 @@ "javascript.implicitProjectConfig.experimentalDecorators": { "type": "boolean", "default": false, - "description": "%javascript.implicitProjectConfig.experimentalDecorators%", + "markdownDescription": "%javascript.implicitProjectConfig.experimentalDecorators%", "scope": "window" }, "javascript.nameSuggestions": { @@ -384,6 +384,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 +424,7 @@ null ], "default": null, - "description": "%typescript.locale%", + "markdownDescription": "%typescript.locale%", "scope": "window" }, "javascript.suggestionActions.enabled": { @@ -441,7 +447,7 @@ "double" ], "default": "auto", - "description": "%typescript.preferences.quoteStyle%", + "markdownDescription": "%typescript.preferences.quoteStyle%", "scope": "resource" }, "typescript.preferences.quoteStyle": { @@ -452,7 +458,7 @@ "double" ], "default": "auto", - "description": "%typescript.preferences.quoteStyle%", + "markdownDescription": "%typescript.preferences.quoteStyle%", "scope": "resource" }, "javascript.preferences.importModuleSpecifier": { @@ -462,7 +468,7 @@ "relative", "non-relative" ], - "enumDescriptions": [ + "markdownEnumDescriptions": [ "%typescript.preferences.importModuleSpecifier.auto%", "%typescript.preferences.importModuleSpecifier.relative%", "%typescript.preferences.importModuleSpecifier.nonRelative%" @@ -478,6 +484,11 @@ "relative", "non-relative" ], + "enumDescriptions": [ + "%typescript.preferences.importModuleSpecifier.auto%", + "%typescript.preferences.importModuleSpecifier.relative%", + "%typescript.preferences.importModuleSpecifier.nonRelative%" + ], "default": "auto", "description": "%typescript.preferences.importModuleSpecifier%", "scope": "resource" @@ -489,6 +500,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 737a4fe5e21..15b80915353 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -32,31 +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.", + "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.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. 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.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." -} \ No newline at end of file +} 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/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<Proto.NavTreeResponse> ) { 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<Proto.NavTreeResponse> ): Promise<Proto.NavTreeResponse> { this.response = response; @@ -51,19 +51,19 @@ export class CachedNavTreeResponse { } } -export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider { - private onDidChangeCodeLensesEmitter = new EventEmitter<void>(); +export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensProvider { + private onDidChangeCodeLensesEmitter = new vscode.EventEmitter<void>(); public constructor( protected client: ITypeScriptServiceClient, private cachedResponse: CachedNavTreeResponse ) { } - public get onDidChangeCodeLenses(): Event<void> { + public get onDidChangeCodeLenses(): vscode.Event<void> { return this.onDidChangeCodeLensesEmitter.event; } - async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> { + async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> { 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<SyncedBuffer> { public getForPath(filePath: string): SyncedBuffer | undefined { - return this.get(Uri.file(filePath)); + return this.get(vscode.Uri.file(filePath)); } public get allBuffers(): Iterable<SyncedBuffer> { @@ -117,10 +117,16 @@ class SyncedBufferMap extends ResourceMap<SyncedBuffer> { } class PendingDiagnostics extends ResourceMap<number> { - public getFileList(): Set<string> { - return new Set(Array.from(this.entries) - .sort((a, b) => a[1] - b[1]) - .map(entry => entry[0])); + public getOrderedFileSet(): ResourceMap<void> { + const orderedResources = Array.from(this.entries) + .sort((a, b) => a.value - b.value) + .map(entry => entry.resource); + + const map = new ResourceMap<void>(); + 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<void>, 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<void>, + 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<string>; - private readonly disposables: Disposable[] = []; private readonly syncedBuffers: SyncedBufferMap; private readonly pendingDiagnostics: PendingDiagnostics; private readonly diagnosticDelayer: Delayer<any>; @@ -186,20 +193,21 @@ export default class BufferSyncSupport { client: ITypeScriptServiceClient, modeIds: string[] ) { + super(); this.client = client; this.modeIds = new Set<string>(modeIds); this.diagnosticDelayer = new Delayer<any>(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<Uri>(); + private readonly _onDelete = this._register(new vscode.EventEmitter<vscode.Uri>()); 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<R>(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<boolean>('validate.enable', true); this._validateTypeScript = tsConfig.get<boolean>('validate.enable', true); diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 54e6661cac4..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<Proto.CompletionEntry> | 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; } @@ -366,8 +366,8 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider let details: Proto.CompletionEntryDetails[] | undefined; try { - const response = await this.client.execute('completionEntryDetails', args, token); - details = response.body; + const { body } = await this.client.execute('completionEntryDetails', args, token); + details = body; } catch { 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<boolean> { // 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<Location[] | undefined> { + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): Promise<vscode.Location[] | undefined> { 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 99b9c366247..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'; @@ -20,7 +19,7 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase public async provideDefinition( document: vscode.TextDocument, position: vscode.Position, - token: vscode.CancellationToken | boolean + token: vscode.CancellationToken ): Promise<vscode.DefinitionLink[] | vscode.Definition | undefined> { if (this.client.apiVersion.gte(API.v270)) { const filepath = this.client.toPath(document.uri); @@ -30,14 +29,13 @@ 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 target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); return <vscode.DefinitionLink>{ 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 dd0fb76f735..d661e154fdd 100644 --- a/extensions/typescript-language-features/src/features/documentHighlight.ts +++ b/extensions/typescript-language-features/src/features/documentHighlight.ts @@ -26,15 +26,14 @@ class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightPro } const args = typeConverters.Position.toFileLocationRequestArgs(file, position); - let items: Proto.OccurrencesResponseItem[] | undefined; + let items: Proto.OccurrencesResponseItem[]; try { - const response = await this.client.execute('occurrences', args, token); - items = response.body; + const { body } = await this.client.execute('occurrences', args, token); + if (!body) { + return []; + } + items = body; } catch { - // noop - } - - if (!items) { return []; } diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts index 84fc9117b9a..8925d2fb3b1 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/features/documentSymbol.ts @@ -40,12 +40,14 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider return undefined; } - - let tree: Proto.NavigationTree | undefined; + let tree: Proto.NavigationTree; try { const args: Proto.FileRequestArgs = { file }; - const response = await this.client.execute('navtree', args, token); - tree = response.body; + const { body } = await this.client.execute('navtree', args, token); + if (!body) { + return undefined; + } + tree = body; } catch { return undefined; } @@ -61,26 +63,31 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } 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<T>(a: T, b: T): boolean { @@ -22,8 +23,8 @@ function objsAreEqual<T>(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<FileConfiguration>(); 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<void> { - 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<void> { 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<void> { + 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<string>('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<string>('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 89f68500c5b..b3ecff09d17 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -29,16 +29,19 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); - let edits: Proto.CodeEdit[] | undefined; + let edits: Proto.CodeEdit[]; try { const args = typeConverters.Range.toFormattingRequestArgs(file, range); - const response = await this.client.execute('format', args, token); - edits = response.body; + const { body } = await this.client.execute('format', args, token); + if (!body) { + return undefined; + } + edits = body; } catch { - // noop + return undefined; } - return (edits || []).map(typeConverters.TextEdit.fromCodeEdit); + return edits.map(typeConverters.TextEdit.fromCodeEdit); } public async provideOnTypeFormattingEdits( @@ -60,8 +63,8 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit 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<vscode.Definition | undefined> { + public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Definition | undefined> { 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<CompletionItem[]> { + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): Promise<vscode.CompletionItem[]> { 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<boolean> { 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<boolean> { + public async execute(resource: vscode.Uri, start: vscode.Position): Promise<boolean> { 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<boolean> { + private async tryInsertJsDocFromTemplate(editor: vscode.TextEditor, file: string, position: vscode.Position): Promise<boolean> { 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<SnippetString | undefined> { + public static getSnippetTemplate(client: ITypeScriptServiceClient, file: string, position: vscode.Position): Promise<vscode.SnippetString | undefined> { const args = typeConverters.Position.toFileLocationRequestArgs(file, position); + const tokenSource = new vscode.CancellationTokenSource(); return Promise.race([ - client.execute('docCommentTemplate', args), - new Promise<Proto.DocCommandTemplateResponse>((_, reject) => setTimeout(reject, 250)) + client.execute('docCommentTemplate', args, tokenSource.token), + new Promise<Proto.DocCommandTemplateResponse>((_, 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<boolean> { - const snippet = new SnippetString(`/**\n * $0\n */`); + private tryInsertDefaultDoc(editor: vscode.TextEditor, position: vscode.Position): Thenable<boolean> { + 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..b7f9b1e083e 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/features/languageConfiguration.ts @@ -9,14 +9,12 @@ * ------------------------------------------------------------------------------------------ */ 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 = { indentationRules: { - // ^(.*\*/)?\s*\}.*$ - decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/, - // ^.*\{[^}"']*$ + decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]].*$/, increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/ }, wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, @@ -64,10 +62,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 +80,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<boolean> { - 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<vscode.Diagnostic> { 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<vscode.Diagnostic[]> { + public async getFixableDiagnosticsForContext(context: vscode.CodeActionContext): Promise<DiagnosticsSet> { 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<Set<number>> { 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<vscode.CodeAction[]> { - 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<Iterable<vscode.CodeAction>> { - 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<vscode.CodeAction | undefined> { - 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 fbb3a3b88ef..7817a2fef68 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,16 +137,16 @@ 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 refactorings: Proto.ApplicableRefactorInfo[]; try { - const response = await this.client.execute('getApplicableRefactors', args, token); - if (!response.body) { + const { body } = await this.client.execute('getApplicableRefactors', args, token); + if (!body) { return undefined; } - refactorings = response.body; + refactorings = body; } catch { return undefined; } @@ -200,7 +200,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { return false; } - return rangeOrSelection instanceof vscode.Selection; + return rangeOrSelection instanceof vscode.Selection && !rangeOrSelection.isEmpty; } private static getKind(refactor: Proto.RefactorActionInfo) { 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<vscode.WorkspaceEdit>(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/tsconfig.ts b/extensions/typescript-language-features/src/features/tsconfig.ts index 414c0f01148..a7e89ea1332 100644 --- a/extensions/typescript-language-features/src/features/tsconfig.ts +++ b/extensions/typescript-language-features/src/features/tsconfig.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ 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 mapNode<R>(node: jsonc.Node | undefined, f: (x: jsonc.Node) => R): R[] { +function mapChildren<R>(node: jsonc.Node | undefined, f: (x: jsonc.Node) => R): R[] { return node && node.type === 'array' && node.children ? node.children.map(f) : []; @@ -33,19 +33,38 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { } private getExendsLink(document: vscode.TextDocument, root: jsonc.Node): vscode.DocumentLink | undefined { - return this.pathNodeToLink(document, jsonc.findNodeAtLocation(root, ['extends'])); + const extendsNode = jsonc.findNodeAtLocation(root, ['extends']); + if (!this.isPathValue(extendsNode)) { + return undefined; + } + + 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 mapNode( + return mapChildren( jsonc.findNodeAtLocation(root, ['files']), - node => this.pathNodeToLink(document, node)); + child => this.pathNodeToLink(document, child)); } private getReferencesLinks(document: vscode.TextDocument, root: jsonc.Node) { - return mapNode( + return mapChildren( jsonc.findNodeAtLocation(root, ['references']), - child => this.pathNodeToLink(document, jsonc.findNodeAtLocation(child, ['path']))); + child => { + const pathNode = jsonc.findNodeAtLocation(child, ['path']); + if (!this.isPathValue(pathNode)) { + return undefined; + } + + return new vscode.DocumentLink(this.getRange(document, pathNode), + basename(pathNode.value).match('.json$') + ? this.getFileTarget(document, pathNode) + : this.getFolderTarget(document, pathNode)); + }); } private pathNodeToLink( @@ -53,7 +72,7 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { node: jsonc.Node | undefined ): vscode.DocumentLink | undefined { return this.isPathValue(node) - ? new vscode.DocumentLink(this.getRange(document, node), this.getTarget(document, node)) + ? new vscode.DocumentLink(this.getRange(document, node), this.getFileTarget(document, node)) : undefined; } @@ -61,13 +80,17 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { return extendsNode && extendsNode.type === 'string' && extendsNode.value - && !(extendsNode.value as string).includes('*'); + && !(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); 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<vscode.Definition | undefined> { + public provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Definition | undefined> { 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..3d266ef00ec 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<void> { - 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; } @@ -247,13 +245,15 @@ export class UpdateImportsOnFileRenameHandler { const edits: Proto.FileCodeEdits[] = []; for (const edit of response.body) { // Workaround for https://github.com/Microsoft/vscode/issues/52675 - if ((edit as Proto.FileCodeEdits).fileName.match(/[\/\\]node_modules[\/\\]/gi)) { - continue; - } - for (const change of (edit as Proto.FileCodeEdits).textChanges) { - if (change.newText.match(/\/node_modules\//gi)) { + if (!this.client.apiVersion.gte(API.v300)) { + if ((edit as Proto.FileCodeEdits).fileName.match(/[\/\\]node_modules[\/\\]/gi)) { continue; } + for (const change of (edit as Proto.FileCodeEdits).textChanges) { + if (change.newText.match(/\/node_modules\//gi)) { + continue; + } + } } edits.push(await this.fixEdit(edit, isDirectoryRename, oldFile, newFile)); @@ -304,3 +304,11 @@ export class UpdateImportsOnFileRenameHandler { } } +export function register( + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, + handles: (uri: vscode.Uri) => Promise<boolean>, +) { + 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 31ba616b8c1..aad5f1a3a44 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -10,7 +10,7 @@ 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,8 +21,7 @@ import TypingsStatus from './utils/typingsStatus'; const validateSetting = 'validate.enable'; const suggestionSetting = 'suggestionActions.enabled'; -export default class LanguageProvider { - private readonly disposables: vscode.Disposable[] = []; +export default class LanguageProvider extends Disposable { constructor( private readonly client: TypeScriptServiceClient, @@ -32,7 +31,8 @@ export default class LanguageProvider { private readonly typingsStatus: TypingsStatus, private readonly fileConfigurationManager: FileConfigurationManager ) { - vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables); + super(); + vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this._disposables); this.configurationChanged(); client.onReady(async () => { @@ -40,9 +40,6 @@ export default class LanguageProvider { }); } - public dispose(): void { - disposeAll(this.disposables); - } @memoize private get documentSelector(): vscode.DocumentFilter[] { @@ -60,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.client.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 { diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index f675bd4f572..afff7001672 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -8,17 +8,17 @@ * 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 { Disposable } from './utils/dispose'; import { LanguageDescription, DiagnosticLanguage } from './utils/languageDescription'; import LogDirectoryProvider from './utils/logDirectoryProvider'; import { TypeScriptServerPlugin } from './utils/plugins'; @@ -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<string, LanguageProvider>(); - 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(() => { @@ -126,7 +124,7 @@ export default class TypeScriptServiceClientHost { }; 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); } }); @@ -135,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(); } @@ -147,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<boolean> { + public async handles(resource: vscode.Uri): Promise<boolean> { const provider = await this.findLanguage(resource); if (provider) { return true; @@ -173,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<LanguageProvider | undefined> { + private async findLanguage(resource: vscode.Uri): Promise<LanguageProvider | undefined> { 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; @@ -199,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(); } @@ -208,7 +198,7 @@ export default class TypeScriptServiceClientHost { private async diagnosticsReceived( kind: DiagnosticKind, - resource: Uri, + resource: vscode.Uri, diagnostics: Proto.Diagnostic[] ): Promise<void> { const language = await this.findLanguage(resource); @@ -234,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; @@ -256,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)]); }); } }); @@ -274,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) { @@ -294,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 110f41d2658..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<API>; - readonly onProjectLanguageServiceStateChanged: Event<Proto.ProjectLanguageServiceStateEventBody>; - readonly onDidBeginInstallTypings: Event<Proto.BeginInstallTypesEventBody>; - readonly onDidEndInstallTypings: Event<Proto.EndInstallTypesEventBody>; - readonly onTypesInstallerInitializationFailed: Event<Proto.TypesInstallerInitializationFailedEventBody>; + readonly onTsServerStarted: vscode.Event<API>; + readonly onProjectLanguageServiceStateChanged: vscode.Event<Proto.ProjectLanguageServiceStateEventBody>; + readonly onDidBeginInstallTypings: vscode.Event<Proto.BeginInstallTypesEventBody>; + readonly onDidEndInstallTypings: vscode.Event<Proto.EndInstallTypesEventBody>; + readonly onTypesInstallerInitializationFailed: vscode.Event<Proto.TypesInstallerInitializationFailedEventBody>; readonly apiVersion: API; readonly plugins: TypeScriptServerPlugin[]; @@ -45,42 +78,22 @@ export interface ITypeScriptServiceClient { readonly logger: Logger; readonly bufferSyncSupport: BufferSyncSupport; - execute(command: 'configure', args: Proto.ConfigureRequestArguments, token?: CancellationToken): Promise<Proto.ConfigureResponse>; - execute(command: 'open', args: Proto.OpenRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise<any>; - execute(command: 'close', args: Proto.FileRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise<any>; - execute(command: 'change', args: Proto.ChangeRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise<any>; - execute(command: 'quickinfo', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.QuickInfoResponse>; - execute(command: 'completions', args: Proto.CompletionsRequestArgs, token?: CancellationToken): Promise<Proto.CompletionsResponse>; - execute(command: 'completionInfo', args: Proto.CompletionsRequestArgs, token?: CancellationToken): Promise<Proto.CompletionInfoResponse>; - execute(command: 'completionEntryDetails', args: Proto.CompletionDetailsRequestArgs, token?: CancellationToken): Promise<Proto.CompletionDetailsResponse>; - execute(command: 'signatureHelp', args: Proto.SignatureHelpRequestArgs, token?: CancellationToken): Promise<Proto.SignatureHelpResponse>; - execute(command: 'definition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.DefinitionResponse>; - execute(command: 'definitionAndBoundSpan', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.DefinitionInfoAndBoundSpanReponse>; - execute(command: 'implementation', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.ImplementationResponse>; - execute(command: 'typeDefinition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.TypeDefinitionResponse>; - execute(command: 'references', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.ReferencesResponse>; - execute(command: 'navto', args: Proto.NavtoRequestArgs, token?: CancellationToken): Promise<Proto.NavtoResponse>; - execute(command: 'format', args: Proto.FormatRequestArgs, token?: CancellationToken): Promise<Proto.FormatResponse>; - execute(command: 'formatonkey', args: Proto.FormatOnKeyRequestArgs, token?: CancellationToken): Promise<Proto.FormatResponse>; - execute(command: 'rename', args: Proto.RenameRequestArgs, token?: CancellationToken): Promise<Proto.RenameResponse>; - execute(command: 'occurrences', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.OccurrencesResponse>; - execute(command: 'projectInfo', args: Proto.ProjectInfoRequestArgs, token?: CancellationToken): Promise<Proto.ProjectInfoResponse>; - execute(command: 'reloadProjects', args: any, expectedResult: boolean, token?: CancellationToken): Promise<any>; - execute(command: 'reload', args: Proto.ReloadRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise<any>; - execute(command: 'compilerOptionsForInferredProjects', args: Proto.SetCompilerOptionsForInferredProjectsArgs, token?: CancellationToken): Promise<any>; - execute(command: 'navtree', args: Proto.FileRequestArgs, token?: CancellationToken): Promise<Proto.NavTreeResponse>; - execute(command: 'getCodeFixes', args: Proto.CodeFixRequestArgs, token?: CancellationToken): Promise<Proto.GetCodeFixesResponse>; - execute(command: 'getSupportedCodeFixes', args: null, token?: CancellationToken): Promise<Proto.GetSupportedCodeFixesResponse>; - execute(command: 'getCombinedCodeFix', args: Proto.GetCombinedCodeFixRequestArgs, token?: CancellationToken): Promise<Proto.GetCombinedCodeFixResponse>; - execute(command: 'docCommentTemplate', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.DocCommandTemplateResponse>; - execute(command: 'getApplicableRefactors', args: Proto.GetApplicableRefactorsRequestArgs, token?: CancellationToken): Promise<Proto.GetApplicableRefactorsResponse>; - execute(command: 'getEditsForRefactor', args: Proto.GetEditsForRefactorRequestArgs, token?: CancellationToken): Promise<Proto.GetEditsForRefactorResponse>; - execute(command: 'applyCodeActionCommand', args: Proto.ApplyCodeActionCommandRequestArgs, token?: CancellationToken): Promise<Proto.ApplyCodeActionCommandResponse>; - execute(command: 'organizeImports', args: Proto.OrganizeImportsRequestArgs, token?: CancellationToken): Promise<Proto.OrganizeImportsResponse>; - execute(command: 'getOutliningSpans', args: Proto.FileRequestArgs, token: CancellationToken): Promise<Proto.OutliningSpansResponse>; - execute(command: 'getEditsForFileRename', args: Proto.GetEditsForFileRenameRequestArgs): Promise<Proto.GetEditsForFileRenameResponse>; - execute(command: 'jsxClosingTag', args: Proto.JsxClosingTagRequestArgs, token: CancellationToken): Promise<Proto.JsxClosingTagResponse>; - execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise<any>; + execute<K extends keyof TypeScriptRequestTypes>( + command: K, + args: TypeScriptRequestTypes[K][0], + token: vscode.CancellationToken + ): Promise<TypeScriptRequestTypes[K][1]>; - executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: CancellationToken): Promise<any>; + 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<any>; + + /** + * Cancel on going geterr requests and re-queue them after `f` has been evaluated. + */ + interuptGetErr<R>(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 88ad494c3f6..7d177d982a8 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -6,7 +6,7 @@ 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, DiagnosticsManager } from './features/diagnostics'; @@ -14,7 +14,7 @@ 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,18 +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(); @@ -235,9 +231,9 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.diagnosticsManager = new DiagnosticsManager('typescript'); this.bufferSyncSupport.onDelete(resource => { this.diagnosticsManager.delete(resource); - }, null, this.disposables); + }, null, this._disposables); - workspace.onDidChangeConfiguration(() => { + vscode.workspace.onDidChangeConfiguration(() => { const oldConfiguration = this._configuration; this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); @@ -256,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() { @@ -266,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 { @@ -302,28 +291,28 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } } - private readonly _onTsServerStarted = new EventEmitter<API>(); + private readonly _onTsServerStarted = this._register(new vscode.EventEmitter<API>()); public readonly onTsServerStarted = this._onTsServerStarted.event; - private readonly _onDiagnosticsReceived = new EventEmitter<TsDiagnostics>(); + private readonly _onDiagnosticsReceived = this._register(new vscode.EventEmitter<TsDiagnostics>()); public readonly onDiagnosticsReceived = this._onDiagnosticsReceived.event; - private readonly _onConfigDiagnosticsReceived = new EventEmitter<Proto.ConfigFileDiagnosticEvent>(); + private readonly _onConfigDiagnosticsReceived = this._register(new vscode.EventEmitter<Proto.ConfigFileDiagnosticEvent>()); public readonly onConfigDiagnosticsReceived = this._onConfigDiagnosticsReceived.event; - private readonly _onResendModelsRequested = new EventEmitter<void>(); + private readonly _onResendModelsRequested = this._register(new vscode.EventEmitter<void>()); public readonly onResendModelsRequested = this._onResendModelsRequested.event; - private readonly _onProjectLanguageServiceStateChanged = new EventEmitter<Proto.ProjectLanguageServiceStateEventBody>(); + private readonly _onProjectLanguageServiceStateChanged = this._register(new vscode.EventEmitter<Proto.ProjectLanguageServiceStateEventBody>()); public readonly onProjectLanguageServiceStateChanged = this._onProjectLanguageServiceStateChanged.event; - private readonly _onDidBeginInstallTypings = new EventEmitter<Proto.BeginInstallTypesEventBody>(); + private readonly _onDidBeginInstallTypings = this._register(new vscode.EventEmitter<Proto.BeginInstallTypesEventBody>()); public readonly onDidBeginInstallTypings = this._onDidBeginInstallTypings.event; - private readonly _onDidEndInstallTypings = new EventEmitter<Proto.EndInstallTypesEventBody>(); + private readonly _onDidEndInstallTypings = this._register(new vscode.EventEmitter<Proto.EndInstallTypesEventBody>()); public readonly onDidEndInstallTypings = this._onDidEndInstallTypings.event; - private readonly _onTypesInstallerInitializationFailed = new EventEmitter<Proto.TypesInstallerInitializationFailedEventBody>(); + private readonly _onTypesInstallerInitializationFailed = this._register(new vscode.EventEmitter<Proto.TypesInstallerInitializationFailedEventBody>()); public readonly onTypesInstallerInitializationFailed = this._onTypesInstallerInitializationFailed.event; public get apiVersion(): API { @@ -366,12 +355,13 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } } + private token: number = 0; private startService(resendModels: boolean = false): Promise<ForkedTsServerProcess> { let currentVersion = this.versionPicker.currentVersion; 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; @@ -383,6 +373,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.requestQueue = new RequestQueue(); this.callbacks = new CallbackMap(); this.lastError = null; + let mytoken = ++this.token; return this.servicePromise = new Promise<ForkedTsServerProcess>(async (resolve, reject) => { try { @@ -391,76 +382,83 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient const tsServerForkOptions: electron.IForkOptions = { execArgv: debugPort ? [`--inspect=${debugPort}`] : [] // [`--debug-brk=5859`] }; - electron.fork(currentVersion.tsServerPath, tsServerForkArgs, tsServerForkOptions, this.logger, (err: any, childProcess: cp.ChildProcess | null) => { - 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)); - /* __GDPR__ - "error" : { - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.logTelemetry('error'); - this.resetClientVersion(); + const childProcess = electron.fork(currentVersion.tsServerPath, tsServerForkArgs, tsServerForkOptions, this.logger); + childProcess.once('error', (err: Error) => { + this.lastError = err; + this.error('Starting TSServer failed with error.', err); + vscode.window.showErrorMessage(localize('serverCouldNotBeStarted', 'TypeScript language server couldn\'t be started. Error message is: {0}', err.message || err)); + /* __GDPR__ + "error" : { + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.logTelemetry('error'); + this.resetClientVersion(); + return; + }); + + this.info('Started TSServer'); + const handle = new ForkedTsServerProcess(childProcess); + this.lastStart = Date.now(); + + handle.onError((err: Error) => { + if (this.token !== mytoken) { + // this is coming from an old process return; } - - this.info('Started TSServer'); - const handle = new ForkedTsServerProcess(childProcess); - this.lastStart = Date.now(); - - handle.onError((err: Error) => { - this.lastError = err; - this.error('TSServer errored with error.', err); - if (this.tsServerLogFile) { - this.error(`TSServer log file: ${this.tsServerLogFile}`); + this.lastError = err; + this.error('TSServer errored with error.', err); + if (this.tsServerLogFile) { + this.error(`TSServer log file: ${this.tsServerLogFile}`); + } + /* __GDPR__ + "tsserver.error" : { + "${include}": [ + "${TypeScriptCommonProperties}" + ] } + */ + this.logTelemetry('tsserver.error'); + this.serviceExited(false); + }); + handle.onExit((code: any) => { + if (this.token !== mytoken) { + // this is coming from an old process + return; + } + if (code === null || typeof code === 'undefined') { + this.info('TSServer exited'); + } else { + this.error(`TSServer exited with code: ${code}`); /* __GDPR__ - "tsserver.error" : { + "tsserver.exitWithCode" : { + "code" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, "${include}": [ "${TypeScriptCommonProperties}" ] } */ - this.logTelemetry('tsserver.error'); - this.serviceExited(false); - }); - handle.onExit((code: any) => { - if (code === null || typeof code === 'undefined') { - this.info('TSServer exited'); - } else { - this.error(`TSServer exited with code: ${code}`); - /* __GDPR__ - "tsserver.exitWithCode" : { - "code" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.logTelemetry('tsserver.exitWithCode', { code: code }); - } + this.logTelemetry('tsserver.exitWithCode', { code: code }); + } - if (this.tsServerLogFile) { - this.info(`TSServer log file: ${this.tsServerLogFile}`); - } - this.serviceExited(!this.isRestarting); - this.isRestarting = false; - }); - - handle.createReader( - msg => { this.dispatchMessage(msg); }, - error => { this.error('ReaderError', error); }); - - this._onReady!.resolve(); - resolve(handle); - this._onTsServerStarted.fire(currentVersion.version); - - this.serviceStarted(resendModels); + if (this.tsServerLogFile) { + this.info(`TSServer log file: ${this.tsServerLogFile}`); + } + this.serviceExited(!this.isRestarting); + this.isRestarting = false; }); + + handle.createReader( + msg => { this.dispatchMessage(msg); }, + error => { this.error('ReaderError', error); }); + + this._onReady!.resolve(); + resolve(handle); + this._onTsServerStarted.fire(currentVersion.version); + + this.serviceStarted(resendModels); } catch (error) { reject(error); } @@ -482,7 +480,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient public async openTsServerLogFile(): Promise<boolean> { if (!this.apiVersion.gte(API.v222)) { - window.showErrorMessage( + vscode.window.showErrorMessage( localize( 'typescript.openTsServerLog.notSupported', 'TS Server logging requires TS 2.2.2+')); @@ -490,7 +488,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } if (this._configuration.tsServerLogLevel === TsServerLogLevel.Off) { - window.showErrorMessage<MessageItem>( + vscode.window.showErrorMessage<vscode.MessageItem>( localize( 'typescript.openTsServerLog.loggingNotEnabled', 'TS Server logging is off. Please set `typescript.tsserver.log` and restart the TS server to enable logging'), @@ -501,7 +499,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(); }); } @@ -511,17 +509,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; @@ -532,7 +530,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(); @@ -547,7 +545,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 { @@ -564,7 +562,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient reportIssue } - interface MyMessageItem extends MessageItem { + interface MyMessageItem extends vscode.MessageItem { id: MessageAction; } @@ -585,7 +583,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient if (diff < 10 * 1000 /* 10 seconds */) { this.lastStart = Date.now(); startService = false; - prompt = window.showErrorMessage<MyMessageItem>( + prompt = vscode.window.showErrorMessage<MyMessageItem>( 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'), @@ -602,7 +600,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.resetClientVersion(); } else if (diff < 60 * 1000 /* 1 Minutes */) { this.lastStart = Date.now(); - prompt = window.showWarningMessage<MyMessageItem>( + prompt = vscode.window.showWarningMessage<MyMessageItem>( localize('serverDied', 'The TypeScript language service died unexpectedly 5 times in the last 5 Minutes.'), { title: localize('serverDiedReportIssue', 'Report Issue'), @@ -612,7 +610,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; }); @@ -624,7 +622,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); @@ -646,7 +644,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); } @@ -654,11 +652,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); @@ -672,8 +670,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; } @@ -690,22 +688,31 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return undefined; } - public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: CancellationToken): Promise<any> { - return this.executeImpl(command, args, { isAsync: true, token, expectsResult: true }); + public execute(command: string, args: any, token: vscode.CancellationToken): Promise<any> { + return this.executeImpl(command, args, { + isAsync: false, + token, + expectsResult: true + }); } - public execute(command: string, args: any, expectsResultOrToken?: boolean | CancellationToken): Promise<any> { - 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<any> { + public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<any> { + 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<any> { const request = this.requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request: request, @@ -740,6 +747,10 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return result; } + public interuptGetErr<R>(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. @@ -885,7 +896,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; @@ -1059,7 +1070,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/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<boolean> { 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<boolean> { - 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<string>('typescript.tsserver.log', 'off'); return TsServerLogLevel.fromString(setting); } - private static readTsServerPluginPaths(configuration: WorkspaceConfiguration): string[] { + private static readTsServerPluginPaths(configuration: vscode.WorkspaceConfiguration): string[] { return configuration.get<string[]>('typescript.tsserver.pluginPaths', []); } - private static readCheckJs(configuration: WorkspaceConfiguration): boolean { + private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get<boolean>('javascript.implicitProjectConfig.checkJs', false); } - private static readExperimentalDecorators(configuration: WorkspaceConfiguration): boolean { + private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get<boolean>('javascript.implicitProjectConfig.experimentalDecorators', false); } - private static readNpmLocation(configuration: WorkspaceConfiguration): string | null { + private static readNpmLocation(configuration: vscode.WorkspaceConfiguration): string | null { return configuration.get<string | null>('typescript.npm', null); } - private static readDisableAutomaticTypeAcquisition(configuration: WorkspaceConfiguration): boolean { + private static readDisableAutomaticTypeAcquisition(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get<boolean>('typescript.disableAutomaticTypeAcquisition', false); } - private static extractLocale(configuration: WorkspaceConfiguration): string | null { + private static extractLocale(configuration: vscode.WorkspaceConfiguration): string | null { return configuration.get<string | null>('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<T extends vscode.Disposable>(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 46129f60f0e..b6ed76ceea0 100644 --- a/extensions/typescript-language-features/src/utils/electron.ts +++ b/extensions/typescript-language-features/src/utils/electron.ts @@ -7,7 +7,6 @@ import Logger from './logger'; import * as temp from './temp'; import path = require('path'); import fs = require('fs'); -import net = require('net'); import cp = require('child_process'); export interface IForkOptions { @@ -32,32 +31,10 @@ export function getTempFile(prefix: string): string { return path.join(getRootTempDir(), `${prefix}-${temp.makeRandomHexString(20)}.tmp`); } -function generatePipeName(): string { - return getPipeName(temp.makeRandomHexString(40)); -} - -function getPipeName(name: string): string { - const fullName = 'vscode-' + name; - if (process.platform === 'win32') { - return '\\\\.\\pipe\\' + fullName + '-sock'; - } - - // Mac/Unix: use socket file - return path.join(getRootTempDir(), fullName + '.sock'); -} - -function generatePatchedEnv( - env: any, - stdInPipeName: string, - stdOutPipeName: string, - stdErrPipeName: string -): any { +function generatePatchedEnv(env: any): any { const newEnv = Object.assign({}, env); // Set the two unique pipe names and the electron flag as process env - newEnv['STDIN_PIPE_NAME'] = stdInPipeName; - newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName; - newEnv['STDERR_PIPE_NAME'] = stdErrPipeName; newEnv['ELECTRON_RUN_AS_NODE'] = '1'; // Ensure we always have a PATH set @@ -69,87 +46,18 @@ export function fork( modulePath: string, args: string[], options: IForkOptions, - logger: Logger, - callback: (error: any, cp: cp.ChildProcess | null) => void, -): void { - - let callbackCalled = false; - const resolve = (result: cp.ChildProcess) => { - if (callbackCalled) { - return; - } - callbackCalled = true; - callback(null, result); - }; - const reject = (err: any) => { - if (callbackCalled) { - return; - } - callbackCalled = true; - callback(err, null); - }; - - // Generate three unique pipe names - const stdInPipeName = generatePipeName(); - const stdOutPipeName = generatePipeName(); - const stdErrPipeName = generatePipeName(); - - - const newEnv = generatePatchedEnv(process.env, stdInPipeName, stdOutPipeName, stdErrPipeName); + logger: Logger +): cp.ChildProcess { + const newEnv = generatePatchedEnv(process.env); newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..'); - let childProcess: cp.ChildProcess; - - // Begin listening to stderr pipe - let stdErrServer = net.createServer((stdErrStream) => { - // From now on the childProcess.stderr is available for reading - childProcess.stderr = stdErrStream; - }); - stdErrServer.listen(stdErrPipeName); - - // Begin listening to stdout pipe - let stdOutServer = net.createServer((stdOutStream) => { - // The child process will write exactly one chunk with content `ready` when it has installed a listener to the stdin pipe - - stdOutStream.once('data', (_chunk: Buffer) => { - // The child process is sending me the `ready` chunk, time to connect to the stdin pipe - childProcess.stdin = <any>net.connect(stdInPipeName); - - // From now on the childProcess.stdout is available for reading - childProcess.stdout = stdOutStream; - - resolve(childProcess); - }); - }); - stdOutServer.listen(stdOutPipeName); - - let serverClosed = false; - const closeServer = () => { - if (serverClosed) { - return; - } - serverClosed = true; - stdOutServer.close(); - stdErrServer.close(); - }; // Create the process logger.info('Forking TSServer', `PATH: ${newEnv['PATH']} `); - const bootstrapperPath = require.resolve('./electronForkStart'); - childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), { + return cp.fork(modulePath, args, { silent: true, cwd: options.cwd, env: newEnv, execArgv: options.execArgv }); - - childProcess.once('error', (err: Error) => { - closeServer(); - reject(err); - }); - - childProcess.once('exit', (err: Error) => { - closeServer(); - reject(err); - }); } diff --git a/extensions/typescript-language-features/src/utils/electronForkStart.ts b/extensions/typescript-language-features/src/utils/electronForkStart.ts deleted file mode 100644 index a29d9bca680..00000000000 --- a/extensions/typescript-language-features/src/utils/electronForkStart.ts +++ /dev/null @@ -1,198 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -var net = require('net'), - fs = require('fs'); - -var ENABLE_LOGGING = false; - -var log = (function () { - if (!ENABLE_LOGGING) { - return function () { }; - } - var isFirst = true; - var LOG_LOCATION = 'C:\\stdFork.log'; - return function log(str: any) { - if (isFirst) { - isFirst = false; - fs.writeFileSync(LOG_LOCATION, str + '\n'); - return; - } - fs.appendFileSync(LOG_LOCATION, str + '\n'); - }; -})(); - -var stdInPipeName = process.env['STDIN_PIPE_NAME']; -var stdOutPipeName = process.env['STDOUT_PIPE_NAME']; -var stdErrPipeName = process.env['STDERR_PIPE_NAME']; - -log('STDIN_PIPE_NAME: ' + stdInPipeName); -log('STDOUT_PIPE_NAME: ' + stdOutPipeName); -log('STDERR_PIPE_NAME: ' + stdErrPipeName); -log('ELECTRON_RUN_AS_NODE: ' + process.env['ELECTRON_RUN_AS_NODE']); - -// stdout redirection to named pipe -(function () { - log('Beginning stdout redirection...'); - - // Create a writing stream to the stdout pipe - var stdOutStream = net.connect(stdOutPipeName); - - // unref stdOutStream to behave like a normal standard out - stdOutStream.unref(); - - // handle process.stdout - (<any>process).__defineGetter__('stdout', function () { return stdOutStream; }); - - // Create a writing stream to the stderr pipe - var stdErrStream = net.connect(stdErrPipeName); - - // unref stdErrStream to behave like a normal standard out - stdErrStream.unref(); - - // handle process.stderr - (<any>process).__defineGetter__('stderr', function () { return stdErrStream; }); - - var fsWriteSyncString = function (fd: number, str: string, _position: number, encoding?: string) { - // fs.writeSync(fd, string[, position[, encoding]]); - var buf = Buffer.from(str, encoding || 'utf8'); - return fsWriteSyncBuffer(fd, buf, 0, buf.length); - }; - - var fsWriteSyncBuffer = function (fd: number, buffer: Buffer, off: number, len: number) { - off = Math.abs(off | 0); - len = Math.abs(len | 0); - - // fs.writeSync(fd, buffer, offset, length[, position]); - var buffer_length = buffer.length; - - if (off > buffer_length) { - throw new Error('offset out of bounds'); - } - if (len > buffer_length) { - throw new Error('length out of bounds'); - } - if (((off + len) | 0) < off) { - throw new Error('off + len overflow'); - } - if (buffer_length - off < len) { - // Asking for more than is left over in the buffer - throw new Error('off + len > buffer.length'); - } - - var slicedBuffer = buffer; - if (off !== 0 || len !== buffer_length) { - slicedBuffer = buffer.slice(off, off + len); - } - - if (fd === 1) { - stdOutStream.write(slicedBuffer); - } else { - stdErrStream.write(slicedBuffer); - } - return slicedBuffer.length; - }; - - // handle fs.writeSync(1, ...) - var originalWriteSync = fs.writeSync; - fs.writeSync = function (fd: number, data: any, _position: number, _encoding?: string) { - if (fd !== 1 && fd !== 2) { - return originalWriteSync.apply(fs, arguments); - } - // usage: - // fs.writeSync(fd, buffer, offset, length[, position]); - // OR - // fs.writeSync(fd, string[, position[, encoding]]); - - if (data instanceof Buffer) { - return fsWriteSyncBuffer.apply(null, arguments); - } - - // For compatibility reasons with fs.writeSync, writing null will write "null", etc - if (typeof data !== 'string') { - data += ''; - } - - return fsWriteSyncString.apply(null, arguments); - }; - - log('Finished defining process.stdout, process.stderr and fs.writeSync'); -})(); - -// stdin redirection to named pipe -(function () { - - // Begin listening to stdin pipe - var server = net.createServer(function (stream: any) { - // Stop accepting new connections, keep the existing one alive - server.close(); - - log('Parent process has connected to my stdin. All should be good now.'); - - // handle process.stdin - (<any>process).__defineGetter__('stdin', function () { - return stream; - }); - - // Remove myself from process.argv - process.argv.splice(1, 1); - - // Load the actual program - var program = process.argv[1]; - log('Loading program: ' + program); - - // Unset the custom environmental variables that should not get inherited - delete process.env['STDIN_PIPE_NAME']; - delete process.env['STDOUT_PIPE_NAME']; - delete process.env['STDERR_PIPE_NAME']; - delete process.env['ELECTRON_RUN_AS_NODE']; - - require(program); - - log('Finished loading program.'); - - var stdinIsReferenced = true; - var timer = setInterval(function () { - var listenerCount = ( - stream.listeners('data').length + - stream.listeners('end').length + - stream.listeners('close').length + - stream.listeners('error').length - ); - // log('listenerCount: ' + listenerCount); - if (listenerCount <= 1) { - // No more "actual" listeners, only internal node - if (stdinIsReferenced) { - stdinIsReferenced = false; - // log('unreferencing stream!!!'); - stream.unref(); - } - } else { - // There are "actual" listeners - if (!stdinIsReferenced) { - stdinIsReferenced = true; - stream.ref(); - } - } - // log( - // '' + stream.listeners('data').length + - // ' ' + stream.listeners('end').length + - // ' ' + stream.listeners('close').length + - // ' ' + stream.listeners('error').length - // ); - }, 1000); - - if ((<any>timer).unref) { - (<any>timer).unref(); - } - }); - - - server.listen(stdInPipeName, function () { - // signal via stdout that the parent process can now begin writing to stdin pipe - process.stdout.write('ready'); - }); - -})(); \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts b/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts index fba55ea03e1..5f97d445a90 100644 --- a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts +++ b/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts @@ -28,13 +28,13 @@ export default class LogDirectoryProvider { @memoize private logDirectory(): string | undefined { try { - const path = this.context.logDirectory; + const path = this.context.logPath; if (!fs.existsSync(path)) { fs.mkdirSync(path); } - return this.context.logDirectory; + return this.context.logPath; } catch { return undefined; } } -} \ No newline at end of file +} 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<T> { - private readonly _map = new Map<string, T>(); + private readonly _map = new Map<string, { resource: vscode.Uri, value: T }>(); 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<T> { + 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/telemetry.ts b/extensions/typescript-language-features/src/utils/telemetry.ts index 8f83e2cc904..4ac7a953937 100644 --- a/extensions/typescript-language-features/src/utils/telemetry.ts +++ b/extensions/typescript-language-features/src/utils/telemetry.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 * as vscode from 'vscode'; import VsCodeTelemetryReporter from 'vscode-extension-telemetry'; import { memoize } from './memoize'; @@ -59,15 +59,14 @@ export default class TelemetryReporter { @memoize private get packageInfo(): IPackageInfo | null { - const packagePath = path.join(__dirname, '..', '..', 'package.json'); - const extensionPackage = require(packagePath); - if (extensionPackage) { + const { packageJSON } = vscode.extensions.getExtension('vscode.typescript-language-features')!; + if (packageJSON) { return { - name: extensionPackage.name, - version: extensionPackage.version, - aiKey: extensionPackage.aiKey + name: packageJSON.name, + version: packageJSON.version, + aiKey: packageJSON.aiKey }; } return null; } -} \ No newline at end of file +} 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<string>('typescript.tsserver.trace', 'off')); + let result: Trace = Trace.fromString(vscode.workspace.getConfiguration().get<string>('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<number, Function>(); - 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<boolean>('check.npmIsInstalled', true)) { - window.showWarningMessage<MyMessageItem>( + if (vscode.workspace.getConfiguration('typescript').get<boolean>('check.npmIsInstalled', true)) { + vscode.window.showWarningMessage<MyMessageItem>( 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<MyQuickPickItem>(pickOptions, { + const selected = await vscode.window.showQuickPick<MyQuickPickItem>(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..8787d174667 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<string | undefined>('typescript.tsdk_version', undefined); + const tsdkVersion = vscode.workspace.getConfiguration().get<string | undefined>('typescript.tsdk_version', undefined); if (tsdkVersion) { return API.fromVersionString(tsdkVersion); } @@ -143,16 +140,16 @@ export class TypeScriptVersionProvider { public get bundledVersion(): TypeScriptVersion { try { - const bundledVersion = new TypeScriptVersion( - path.dirname(require.resolve('typescript/lib/tsserver.js')), - ''); + const { extensionPath } = vscode.extensions.getExtension('vscode.typescript-language-features')!; + const typescriptPath = path.join(extensionPath, '../node_modules/typescript/lib'); + const bundledVersion = new TypeScriptVersion(typescriptPath, ''); if (bundledVersion.isValid) { return bundledVersion; } } 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..ccd1c649622 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -6,9 +6,9 @@ version "8.0.33" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" -"@types/semver@5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.4.0.tgz#f3658535af7f1f502acd6da7daf405ffeb1f7ee4" +"@types/semver@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" ajv@^5.1.0: version "5.5.2" @@ -1274,9 +1274,9 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" -semver@4.3.6: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" +semver@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" semver@^5.3.0, semver@^5.4.1: version "5.5.0" @@ -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/package.json b/extensions/vscode-api-tests/package.json index 72fa2b8dcd5..4c3f93d4fc2 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -4,10 +4,9 @@ "version": "0.0.1", "publisher": "vscode", "enableProposedApi": true, - "private": true, - "main": "horse", - "activationEvents": [ - ], + "private": true, + "main": "horse", + "activationEvents": [], "engines": { "vscode": "1.25.0" }, @@ -50,7 +49,7 @@ }, "devDependencies": { "@types/mocha": "2.2.43", - "@types/node": "7.0.43", + "@types/node": "^8.10.25", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "typescript": "^1.6.2", diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 27877a8608a..ab90746f65a 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -688,7 +688,7 @@ suite('window namespace tests', () => { const renderer = window.createTerminalRenderer('foo'); }); - test('Terminal.sendText should fire Termnial.onInput', (done) => { + test('Terminal.sendText should fire Terminal.onInput', (done) => { const reg1 = window.onDidOpenTerminal(terminal => { reg1.dispose(); const reg2 = renderer.onDidAcceptInput(data => { 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..fbfe2e98bab 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -511,15 +511,35 @@ suite('workspace-namespace', () => { }); test('findTextInFiles', async () => { + const options: vscode.FindTextInFilesOptions = { + include: '*.ts', + previewOptions: { + leadingChars: 2, + maxLines: 1, + totalChars: 100 + } + }; + const results: vscode.TextSearchResult[] = []; - await vscode.workspace.findTextInFiles({ pattern: 'foo' }, { include: '*.ts' }, result => { + await vscode.workspace.findTextInFiles({ pattern: 'foo' }, options, result => { results.push(result); }); assert.equal(results.length, 1); + assert.equal(results[0].preview.text, 'n foo(): void {'); 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/vscode-api-tests/yarn.lock b/extensions/vscode-api-tests/yarn.lock index 8397cfb2eef..5fee636122b 100644 --- a/extensions/vscode-api-tests/yarn.lock +++ b/extensions/vscode-api-tests/yarn.lock @@ -6,9 +6,9 @@ version "2.2.43" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27" -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" ajv@^5.1.0: version "5.3.0" diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index 756d09c8650..9b43cfa2a6c 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -12,7 +12,7 @@ "postinstall": "node ./node_modules/vscode/bin/install" }, "devDependencies": { - "@types/node": "7.0.43", + "@types/node": "^8.10.25", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "vscode": "1.1.5" diff --git a/extensions/vscode-colorize-tests/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index 5aa4e04741c..b4e9355d74f 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@types/node@7.0.43": - version "7.0.43" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.43.tgz#a187e08495a075f200ca946079c914e1a5fe962c" +"@types/node@^8.10.25": + version "8.10.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" ajv@^5.1.0: version "5.3.0" 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" } @@ -806,9 +806,9 @@ "c": " The stuff below was added for extra tokenizer testing ", "t": "text.xml comment.block.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" } @@ -817,9 +817,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 1d5962931ac..67d803d4e5e 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,6 +2,6 @@ # yarn lockfile v1 -typescript@3.0.1-insiders.20180723: - version "3.0.1-insiders.20180723" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1-insiders.20180723.tgz#266fbafb349a6429777ab3525cda3bb0a2adc661" +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 ebdca25bcb4..fe899377d79 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -36,42 +36,12 @@ gulp.task('clean-build', ['clean-client-build', 'clean-extensions-build']); gulp.task('compile-build', ['compile-client-build', 'compile-extensions-build']); gulp.task('watch-build', ['watch-client-build', 'watch-extensions-build']); -var ALL_EDITOR_TASKS = [ - // Always defined tasks - 'clean-client', - 'compile-client', - 'watch-client', - 'clean-client-build', - 'compile-client-build', - 'watch-client-build', - - // Editor tasks (defined in gulpfile.editor) - 'clean-optimized-editor', - 'optimize-editor', - 'clean-minified-editor', - 'minify-editor', - 'clean-editor-distro', - 'editor-distro', - 'analyze-editor-distro', - - // hygiene tasks - 'tslint', - 'hygiene', -]; - -var runningEditorTasks = process.argv.length > 2 && process.argv.slice(2).every(function (arg) { return (ALL_EDITOR_TASKS.indexOf(arg) !== -1); }); - process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); process.exit(1); }); -if (runningEditorTasks) { - require(`./build/gulpfile.editor`); - require(`./build/gulpfile.hygiene`); -} else { - // Load all the gulpfiles only if running tasks other than the editor tasks - const build = path.join(__dirname, 'build'); - require('glob').sync('gulpfile.*.js', { cwd: build }) - .forEach(f => require(`./build/${f}`)); -} +// Load all the gulpfiles only if running tasks other than the editor tasks +const build = path.join(__dirname, 'build'); +require('glob').sync('gulpfile.*.js', { cwd: build }) + .forEach(f => require(`./build/${f}`)); diff --git a/package.json b/package.json index 6968445d439..501755f95a4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.26.0", - "distro": "4b5c6aa6ea6f222d62d08ca5653049b200be93a5", + "version": "1.27.0", + "distro": "280911fc2dd6bc4d1fe8320e5b7d611c034b87fb", "author": { "name": "Microsoft Corporation" }, @@ -22,8 +22,7 @@ "update-localization-extension": "node build/npm/update-localization-extension.js", "smoketest": "cd test/smoke && node test/index.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", - "download-builtin-extensions": "node build/lib/builtInExtensions.js", - "check-monaco-editor-compilation": "tsc -p src/tsconfig.monaco.json --noEmit" + "download-builtin-extensions": "node build/lib/builtInExtensions.js" }, "dependencies": { "applicationinsights": "0.18.0", @@ -46,13 +45,14 @@ "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": "1.0.17", - "vscode-ripgrep": "^1.0.1", + "vscode-ripgrep": "1.1.0", "vscode-textmate": "^4.0.1", - "vscode-uri": "1.0.5", - "vscode-xterm": "3.6.0-beta5", - "yauzl": "^2.9.1" + "vscode-xterm": "3.7.0-beta5", + "winreg": "^1.2.4", + "yauzl": "^2.9.1", + "yazl": "^2.4.3" }, "devDependencies": { "7zip": "0.0.6", @@ -60,10 +60,11 @@ "@types/minimist": "1.2.0", "@types/mocha": "2.2.39", "@types/sinon": "1.16.34", + "@types/webpack": "^4.4.10", "asar": "^0.14.0", - "azure-storage": "^0.3.1", "chromium-pickle-js": "^0.2.0", "clean-css": "3.4.6", + "copy-webpack-plugin": "^4.5.2", "coveralls": "^2.11.11", "cson-parser": "^1.3.3", "debounce": "^1.0.0", @@ -103,7 +104,8 @@ "istanbul": "^0.3.17", "jsdom-no-contextify": "^3.1.0", "lazy.js": "^0.4.2", - "mime": "1.2.11", + "merge-options": "^1.0.1", + "mime": "^1.4.1", "minimatch": "^2.0.10", "mkdirp": "^0.5.0", "mocha": "^2.2.5", @@ -117,6 +119,7 @@ "rimraf": "^2.2.8", "sinon": "^1.17.2", "source-map": "^0.4.4", + "ts-loader": "^4.4.2", "tslint": "^5.9.1", "typescript": "2.9.2", "typescript-formatter": "7.1.0", @@ -125,7 +128,10 @@ "vinyl": "^0.4.5", "vinyl-fs": "^2.4.3", "vsce": "1.33.2", - "vscode-nls-dev": "3.0.7" + "vscode-nls-dev": "3.2.2", + "webpack": "^4.16.5", + "webpack-cli": "^3.1.0", + "webpack-stream": "^5.1.1" }, "repository": { "type": "git", 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 57f7c2075ff..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 +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 <vscode-linux@microsoft.com> diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index e78bf4f85ca..c2ae8b8fe31 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -4,7 +4,7 @@ "libpthread.so.0(GLIBC_2.2.5)(64bit)", "libpthread.so.0(GLIBC_2.3.2)(64bit)", "libpthread.so.0(GLIBC_2.3.3)(64bit)", - "libgtk-x11-2.0.so.0()(64bit)", + "libgtk-3.so.0()(64bit)", "libgdk-x11-2.0.so.0()(64bit)", "libatk-1.0.so.0()(64bit)", "libgio-2.0.so.0()(64bit)", @@ -114,7 +114,7 @@ "libglib-2.0.so.0", "libgmodule-2.0.so.0", "libgobject-2.0.so.0", - "libgtk-x11-2.0.so.0", + "libgtk-3.so.0", "libm.so.6", "libm.so.6(GLIBC_2.0)", "libm.so.6(GLIBC_2.1)", diff --git a/scripts/code-cli.bat b/scripts/code-cli.bat index f08ddb744e0..7bca260314d 100644 --- a/scripts/code-cli.bat +++ b/scripts/code-cli.bat @@ -29,7 +29,7 @@ set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 :: Launch Code -%CODE% --debug=5874 out\cli.js . %* +%CODE% --inspect=5874 out\cli.js . %* popd endlocal diff --git a/scripts/code-cli.sh b/scripts/code-cli.sh index 89e518322fc..ba2121d9bb9 100755 --- a/scripts/code-cli.sh +++ b/scripts/code-cli.sh @@ -32,7 +32,7 @@ function code() { VSCODE_DEV=1 \ ELECTRON_ENABLE_LOGGING=1 \ ELECTRON_ENABLE_STACK_DUMPING=1 \ - "$CODE" --debug=5874 "$ROOT/out/cli.js" . "$@" + "$CODE" --inspect=5874 "$ROOT/out/cli.js" . "$@" } code "$@" diff --git a/scripts/code.sh b/scripts/code.sh index f6d103ceda5..26332faea6c 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -3,6 +3,10 @@ if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } ROOT=$(dirname "$(dirname "$(realpath "$0")")") + + # On Linux with Electron 2.0.x running out of a VM causes + # a freeze so we only enable this flag on macOS + export ELECTRON_ENABLE_LOGGING=1 else ROOT=$(dirname "$(dirname "$(readlink -f $0)")") fi @@ -40,7 +44,6 @@ function code() { export NODE_ENV=development export VSCODE_DEV=1 export VSCODE_CLI=1 - export ELECTRON_ENABLE_LOGGING=1 export ELECTRON_ENABLE_STACK_DUMPING=1 # Launch Code diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index d2225c4475d..dc5e38c31e5 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -17,6 +17,7 @@ cd $ROOT ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started ./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started ./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started +./scripts/code.sh $ROOT/extensions/search-rg/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/search-rg --extensionTestsPath=$ROOT/extensions/search-rg/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started mkdir $ROOT/extensions/emmet/test-fixtures ./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started . diff --git a/scripts/test.sh b/scripts/test.sh index d88a28c5e2d..ac96627846f 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -4,6 +4,10 @@ if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } ROOT=$(dirname $(dirname $(realpath "$0"))) + + # On Linux with Electron 2.0.x running out of a VM causes + # a freeze so we only enable this flag on macOS + export ELECTRON_ENABLE_LOGGING=1 else ROOT=$(dirname $(dirname $(readlink -f $0))) fi @@ -25,7 +29,6 @@ test -d node_modules || yarn node build/lib/electron.js || ./node_modules/.bin/gulp electron # Unit Tests -export ELECTRON_ENABLE_LOGGING=1 if [[ "$OSTYPE" == "darwin"* ]]; then cd $ROOT ; ulimit -n 4096 ; \ "$CODE" \ diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 752d996f6b9..c9a33c0d3d8 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -9,12 +9,11 @@ var loader = require('./vs/loader'); function uriFromPath(_path) { var pathName = path.resolve(_path).replace(/\\/g, '/'); - if (pathName.length > 0 && pathName.charAt(0) !== '/') { pathName = '/' + pathName; } - return encodeURI('file://' + pathName); + return encodeURI('file://' + pathName).replace(/#/g, '%23'); } function readFile(file) { diff --git a/src/bootstrap.js b/src/bootstrap.js index 707513f7235..c61fc326a8f 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -76,9 +76,9 @@ if (!!process.send && process.env.PIPE_LOGGING === 'true') { res = JSON.stringify(argsArray, function (key, value) { // Objects get special treatment to prevent circles - if (value && Object.prototype.toString.call(value) === '[object Object]') { + if (isObject(value) || Array.isArray(value)) { if (seen.indexOf(value) !== -1) { - return Object.create(null); // prevent circular references! + return '[Circular]'; } seen.push(value); @@ -105,6 +105,14 @@ if (!!process.send && process.env.PIPE_LOGGING === 'true') { } } + function isObject(obj) { + return typeof obj === 'object' + && obj !== null + && !Array.isArray(obj) + && !(obj instanceof RegExp) + && !(obj instanceof Date); + } + // Pass console logging to the outside so that we have it in the main side if told so if (process.env.VERBOSE_LOGGING === 'true') { console.log = function () { safeSend({ type: '__$console', severity: 'log', arguments: safeToArray(arguments) }); }; diff --git a/src/main.js b/src/main.js index 850cda67f2d..711217d64e2 100644 --- a/src/main.js +++ b/src/main.js @@ -7,9 +7,6 @@ const perf = require('./vs/base/common/performance'); perf.mark('main:started'); -// Perf measurements -global.perfStartTime = Date.now(); - Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) const fs = require('fs'); @@ -81,6 +78,12 @@ if (isTempPortable) { const app = require('electron').app; +// TODO@Ben Electron 2.0.x: prevent localStorage migration from SQLite to LevelDB due to issues +app.commandLine.appendSwitch('disable-mojo-local-storage'); + +// Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791) +app.commandLine.appendSwitch('disable-features', 'ColorCorrectRendering'); + const minimist = require('minimist'); const paths = require('./paths'); @@ -257,9 +260,9 @@ function getNLSConfiguration(locale) { // We have a built version so we have extracted nls file. Try to find // the right file to use. - // Check if we have an English locale. If so fall to default since that is our + // Check if we have an English or English US locale. If so fall to default since that is our // English translation (we don't ship *.nls.en.json files) - if (locale && (locale == 'en' || locale.startsWith('en-'))) { + if (locale && (locale === 'en' || locale === 'en-us')) { return Promise.resolve({ locale: locale, availableLanguages: {} }); } diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index daf41dbc736..a13e294afe5 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 1.7.9 +// Type definitions for Electron 2.0.5 // Project: http://electron.atom.io/ // Definitions by: The Electron Team <https://github.com/electron/electron> // Definitions: https://github.com/electron/electron-typescript-definitions @@ -58,6 +58,7 @@ declare namespace Electron { dialog: Dialog; DownloadItem: typeof DownloadItem; globalShortcut: GlobalShortcut; + inAppPurchase: InAppPurchase; IncomingMessage: typeof IncomingMessage; ipcMain: IpcMain; Menu: typeof Menu; @@ -94,6 +95,7 @@ declare namespace Electron { const desktopCapturer: DesktopCapturer; const dialog: Dialog; const globalShortcut: GlobalShortcut; + const inAppPurchase: InAppPurchase; const ipcMain: IpcMain; const ipcRenderer: IpcRenderer; type nativeImage = NativeImage; @@ -102,7 +104,7 @@ declare namespace Electron { const powerMonitor: PowerMonitor; const powerSaveBlocker: PowerSaveBlocker; const protocol: Protocol; - const remote: Remote; + // const remote: Remote; ### VSCODE CHANGE (we do not want to use remote) const screen: Screen; type session = Session; const session: typeof Session; @@ -157,12 +159,54 @@ declare namespace Electron { hasVisibleWindows: boolean) => void): this; removeListener(event: 'activate', listener: (event: Event, hasVisibleWindows: boolean) => void): this; + /** + * Emitted during Handoff after an activity from this device was successfully + * resumed on another one. + */ + on(event: 'activity-was-continued', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; + once(event: 'activity-was-continued', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; + addListener(event: 'activity-was-continued', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; + removeListener(event: 'activity-was-continued', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; /** * Emitted before the application starts closing its windows. Calling * event.preventDefault() will prevent the default behaviour, which is terminating * the application. Note: If application quit was initiated by * autoUpdater.quitAndInstall() then before-quit is emitted after emitting close - * event on all windows and closing them. + * event on all windows and closing them. Note: On Windows, this event will not be + * emitted if the app is closed due to a shutdown/restart of the system or a user + * logout. */ on(event: 'before-quit', listener: (event: Event) => void): this; once(event: 'before-quit', listener: (event: Event) => void): this; @@ -286,6 +330,46 @@ declare namespace Electron { * Contains app-specific state stored by the activity on another device. */ userInfo: any) => void): this; + /** + * Emitted during Handoff when an activity from a different device fails to be + * resumed. + */ + on(event: 'continue-activity-error', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * A string with the error's localized description. + */ + error: string) => void): this; + once(event: 'continue-activity-error', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * A string with the error's localized description. + */ + error: string) => void): this; + addListener(event: 'continue-activity-error', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * A string with the error's localized description. + */ + error: string) => void): this; + removeListener(event: 'continue-activity-error', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * A string with the error's localized description. + */ + error: string) => void): this; /** * Emitted when the gpu process crashes or is killed. */ @@ -364,7 +448,9 @@ declare namespace Electron { removeListener(event: 'open-url', listener: (event: Event, url: string) => void): this; /** - * Emitted when the application is quitting. + * Emitted when the application is quitting. Note: On Windows, this event will not + * be emitted if the app is closed due to a shutdown/restart of the system or a + * user logout. */ on(event: 'quit', listener: (event: Event, exitCode: number) => void): this; @@ -410,6 +496,49 @@ declare namespace Electron { url: string, certificateList: Certificate[], callback: (certificate?: Certificate) => void) => void): this; + /** + * Emitted when Handoff is about to be resumed on another device. If you need to + * update the state to be transferred, you should call event.preventDefault() + * immediately, construct a new userInfo dictionary and call + * app.updateCurrentActiviy() in a timely manner. Otherwise the operation will fail + * and continue-activity-error will be called. + */ + on(event: 'update-activity-state', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; + once(event: 'update-activity-state', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; + addListener(event: 'update-activity-state', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; + removeListener(event: 'update-activity-state', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string, + /** + * Contains app-specific state stored by the activity. + */ + userInfo: any) => void): this; /** * Emitted when a new webContents is created. */ @@ -421,6 +550,31 @@ declare namespace Electron { webContents: WebContents) => void): this; removeListener(event: 'web-contents-created', listener: (event: Event, webContents: WebContents) => void): this; + /** + * Emitted during Handoff before an activity from a different device wants to be + * resumed. You should call event.preventDefault() if you want to handle this + * event. + */ + on(event: 'will-continue-activity', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string) => void): this; + once(event: 'will-continue-activity', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string) => void): this; + addListener(event: 'will-continue-activity', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string) => void): this; + removeListener(event: 'will-continue-activity', listener: (event: Event, + /** + * A string identifying the activity. Maps to . + */ + type: string) => void): this; /** * Emitted when the application has finished basic startup. On Windows and Linux, * the will-finish-launching event is the same as the ready event; on macOS, this @@ -437,7 +591,9 @@ declare namespace Electron { * Emitted when all windows have been closed and the application will quit. Calling * event.preventDefault() will prevent the default behaviour, which is terminating * the application. See the description of the window-all-closed event for the - * differences between the will-quit and window-all-closed events. + * differences between the will-quit and window-all-closed events. Note: On + * Windows, this event will not be emitted if the app is closed due to a + * shutdown/restart of the system or a user logout. */ on(event: 'will-quit', listener: (event: Event) => void): this; once(event: 'will-quit', listener: (event: Event) => void): this; @@ -482,7 +638,7 @@ declare namespace Electron { */ enableMixedSandbox(): void; /** - * Exits immediately with exitCode. exitCode defaults to 0. All windows will be + * Exits immediately with exitCode. exitCode defaults to 0. All windows will be * closed immediately without asking user and the before-quit and will-quit events * will not be emitted. */ @@ -492,7 +648,6 @@ declare namespace Electron { * the active app. On Windows, focuses on the application's first window. */ focus(): void; - getAppMemoryInfo(): ProcessMetric[]; getAppMetrics(): ProcessMetric[]; getAppPath(): string; getBadgeCount(): number; @@ -501,24 +656,24 @@ declare namespace Electron { * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; /** * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; getGPUFeatureStatus(): GPUFeatureStatus; getJumpListSettings(): JumpListSettings; /** - * Note: When distributing your packaged app, you have to also ship the locales - * folder. Note: On Windows you have to call it after the ready events gets - * emitted. + * To set the locale, you'll want to use a command line switch at app startup, + * which may be found here. Note: When distributing your packaged app, you have to + * also ship the locales folder. Note: On Windows you have to call it after the + * ready events gets emitted. */ getLocale(): string; /** * If you provided path and args options to app.setLoginItemSettings then you need - * to pass the same arguments here for openAtLogin to be set correctly. Note: This - * API has no effect on MAS builds. + * to pass the same arguments here for openAtLogin to be set correctly. */ getLoginItemSettings(options?: LoginItemSettingsOptions): LoginItemSettings; /** @@ -544,6 +699,10 @@ declare namespace Electron { * net_error_list. */ importCertificate(options: ImportCertificateOptions, callback: (result: number) => void): void; + /** + * Invalidates the current Handoff user activity. + */ + invalidateCurrentActivity(type: string): void; isAccessibilitySupportEnabled(): boolean; /** * This method checks if the current executable is the default handler for a @@ -555,6 +714,7 @@ declare namespace Electron { * the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. */ isDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + isInApplicationsFolder(): boolean; isReady(): boolean; isUnityRunning(): boolean; /** @@ -578,6 +738,15 @@ declare namespace Electron { * instance starts: */ makeSingleInstance(callback: (argv: string[], workingDirectory: string) => void): boolean; + /** + * No confirmation dialog will be presented by default, if you wish to allow the + * user to confirm the operation you may do so using the dialog API. NOTE: This + * method throws errors if anything other than the user causes the move to fail. + * For instance if the user cancels the authorization dialog this method returns + * false. If we fail to perform the copy then this method will throw an error. The + * message in the error should be informative and tell you exactly what went wrong + */ + moveToApplicationsFolder(): boolean; /** * Try to close all windows. The before-quit event will be emitted first. If all * windows are successfully closed, the will-quit event will be emitted and by @@ -615,6 +784,15 @@ declare namespace Electron { * .plist file. See the Apple docs for more details. */ setAboutPanelOptions(options: AboutPanelOptionsOptions): void; + /** + * Manually enables Chrome's accessibility support, allowing to expose + * accessibility switch to users in application settings. + * https://www.chromium.org/developers/design-documents/accessibility for more + * details. Disabled by default. Note: Rendering accessibility tree can + * significantly affect the performance of your app. It should not be enabled by + * default. + */ + setAccessibilitySupportEnabled(enabled: boolean): void; /** * Changes the Application User Model ID to id. */ @@ -660,8 +838,7 @@ declare namespace Electron { /** * Set the app's login item settings. To work with Electron's autoUpdater on * Windows, which uses Squirrel, you'll want to set the launch path to Update.exe, - * and pass arguments that specify your application name. For example: Note: This - * API has no effect on MAS builds. + * and pass arguments that specify your application name. For example: */ setLoginItemSettings(settings: Settings): void; /** @@ -694,6 +871,18 @@ declare namespace Electron { * them. */ show(): void; + /** + * Start accessing a security scoped resource. With this method electron + * applications that are packaged for the Mac App Store may reach outside their + * sandbox to access files chosen by the user. See Apple's documentation for a + * description of how this system works. + */ + startAccessingSecurityScopedResource(bookmarkData: string): Function; + /** + * Updates the current activity if its type matches type, merging the entries from + * userInfo into its current userInfo dictionary. + */ + updateCurrentActivity(type: string, userInfo: any): void; commandLine: CommandLine; dock: Dock; } @@ -763,16 +952,18 @@ declare namespace Electron { getFeedURL(): string; /** * Restarts the app and installs the update after it has been downloaded. It should - * only be called after update-downloaded has been emitted. Note: - * autoUpdater.quitAndInstall() will close all application windows first and only - * emit before-quit event on app after that. This is different from the normal quit - * event sequence. + * only be called after update-downloaded has been emitted. Under the hood calling + * autoUpdater.quitAndInstall() will close all application windows first, and + * automatically call app.quit() after all windows have been closed. Note: If the + * application is quit without calling this API after the update-downloaded event + * has been emitted, the application will still be replaced by the updated one on + * the next run. */ quitAndInstall(): void; /** * Sets the url and initialize the auto updater. */ - setFeedURL(url: string, requestHeaders?: any): void; + setFeedURL(options: FeedURLOptions): void; } interface BluetoothDevice { @@ -789,6 +980,15 @@ declare namespace Electron { constructor(options?: BrowserViewConstructorOptions); static fromId(id: number): BrowserView; + static fromWebContents(webContents: WebContents): BrowserView | null; + static getAllViews(): BrowserView[]; + /** + * Force closing the view, the unload and beforeunload events won't be emitted for + * the web page. After you're done with a view, call this function in order to free + * memory and other resources as soon as possible. + */ + destroy(): void; + isDestroyed(): boolean; setAutoResize(options: AutoResizeOptions): void; setBackgroundColor(color: string): void; /** @@ -831,7 +1031,11 @@ declare namespace Electron { * cancel the close. Usually you would want to use the beforeunload handler to * decide whether the window should be closed, which will also be called when the * window is reloaded. In Electron, returning any value other than undefined would - * cancel the close. For example: + * cancel the close. For example: Note: There is a subtle difference between the + * behaviors of window.onbeforeunload = handler and + * window.addEventListener('beforeunload', handler). It is recommended to always + * set the event.returnValue explicitly, instead of just returning a value, as the + * former works more consistently within Electron. */ on(event: 'close', listener: (event: Event) => void): this; once(event: 'close', listener: (event: Event) => void): this; @@ -1056,6 +1260,7 @@ declare namespace Electron { * This API cannot be called before the ready event of the app module is emitted. */ static addExtension(path: string): void; + static fromBrowserView(browserView: BrowserView): BrowserWindow | null; static fromId(id: number): BrowserWindow; static fromWebContents(webContents: WebContents): BrowserWindow; static getAllWindows(): BrowserWindow[]; @@ -1080,6 +1285,10 @@ declare namespace Electron { * ready event of the app module is emitted. */ static removeExtension(name: string): void; + /** + * Adds a window as a tab on this window, after the tab for the window instance. + */ + addTabbedWindow(browserWindow: BrowserWindow): void; /** * Removes focus from the window. */ @@ -1123,6 +1332,11 @@ declare namespace Electron { focus(): void; focusOnWebView(): void; getBounds(): Rectangle; + /** + * Note: The BrowserView API is currently experimental and may change or be removed + * in future Electron releases. + */ + getBrowserView(): BrowserView | null; getChildWindows(): BrowserWindow[]; getContentBounds(): Rectangle; getContentSize(): number[]; @@ -1133,6 +1347,7 @@ declare namespace Electron { * (unsigned long) on Linux. */ getNativeWindowHandle(): Buffer; + getOpacity(): number; getParentWindow(): BrowserWindow; getPosition(): number[]; getRepresentedFilename(): string; @@ -1184,12 +1399,18 @@ declare namespace Electron { */ isMovable(): boolean; isResizable(): boolean; + isSimpleFullScreen(): boolean; isVisible(): boolean; /** * Note: This API always returns false on Windows. */ isVisibleOnAllWorkspaces(): boolean; isWindowMessageHooked(message: number): boolean; + /** + * Same as webContents.loadFile, filePath should be a path to an HTML file relative + * to the root of your application. See the webContents docs for more information. + */ + loadFile(filePath: string): void; /** * Same as webContents.loadURL(url[, options]). The url can be a remote address * (e.g. http://) or a path to a local HTML file using the file:// protocol. To @@ -1203,11 +1424,21 @@ declare namespace Electron { * being displayed already. */ maximize(): void; + /** + * Merges all windows into one window with multiple tabs when native tabs are + * enabled and there is more than one open window. + */ + mergeAllWindows(): void; /** * Minimizes the window. On some platforms the minimized window will be shown in * the Dock. */ minimize(): void; + /** + * Moves the current tab into a new window if native tabs are enabled and there is + * more than one tab in the current window. + */ + moveTabToNewWindow(): void; /** * Uses Quick Look to preview a file at a given path. */ @@ -1220,6 +1451,16 @@ declare namespace Electron { * Restores the window from minimized state to its previous state. */ restore(): void; + /** + * Selects the next tab when native tabs are enabled and there are other tabs in + * the window. + */ + selectNextTab(): void; + /** + * Selects the previous tab when native tabs are enabled and there are other tabs + * in the window. + */ + selectPreviousTab(): void; /** * Sets whether the window should show always on top of other windows. After * setting this, the window is still a normal window, not a toolbox window which @@ -1261,10 +1502,6 @@ declare namespace Electron { * Resizes and moves the window to the supplied bounds */ setBounds(bounds: Rectangle, animate?: boolean): void; - /** - * Note: The BrowserView API is currently experimental and may change or be removed - * in future Electron releases. - */ setBrowserView(browserView: BrowserView): void; /** * Sets whether the window can be manually closed by user. On Linux does nothing. @@ -1290,6 +1527,10 @@ declare namespace Electron { * bar will become gray when set to true. */ setDocumentEdited(edited: boolean): void; + /** + * Disable or enable the window. + */ + setEnabled(enable: boolean): void; /** * Changes whether the window can be focused. */ @@ -1316,7 +1557,7 @@ declare namespace Electron { * window will be passed to the window below this window, but if this window has * focus, it will still receive keyboard events. */ - setIgnoreMouseEvents(ignore: boolean): void; + setIgnoreMouseEvents(ignore: boolean, options?: IgnoreMouseEventsOptions): void; /** * Enters or leaves the kiosk mode. */ @@ -1353,6 +1594,10 @@ declare namespace Electron { * Sets whether the window can be moved by user. On Linux does nothing. */ setMovable(movable: boolean): void; + /** + * Sets the opacity of the window. On Linux does nothing. + */ + setOpacity(opacity: number): void; /** * Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to * convey some sort of application status or to passively notify the user. @@ -1393,6 +1638,11 @@ declare namespace Electron { * HTML-rendered toolbar. For example: */ setSheetOffset(offsetY: number, offsetX?: number): void; + /** + * Enters or leaves simple fullscreen mode. Simple fullscreen mode emulates the + * native fullscreen behavior found in versions of Mac OS X prior to Lion (10.7). + */ + setSimpleFullScreen(flag: boolean): void; /** * Resizes the window to width and height. */ @@ -1456,6 +1706,11 @@ declare namespace Electron { * Shows the window but doesn't focus on it. */ showInactive(): void; + /** + * Toggles the visibility of the tab bar if native tabs are enabled and there is + * only one tab in the current window. + */ + toggleTabBar(): void; /** * Unhooks all of the window messages. */ @@ -1689,7 +1944,7 @@ declare namespace Electron { * An object representing the HTTP response message. */ response: IncomingMessage) => void): this; - constructor(options: any | string); + constructor(options: 'method' | 'url' | 'session' | 'partition' | 'protocol' | 'host' | 'hostname' | 'port' | 'path' | 'redirect'); /** * Cancels an ongoing HTTP transaction. If the request has already emitted the * close event, the abort operation will have no effect. Otherwise an ongoing event @@ -1917,7 +2172,7 @@ declare namespace Electron { */ on(event: 'changed', listener: (event: Event, /** - * The cookie that was changed + * The cookie that was changed. */ cookie: Cookie, /** @@ -1930,7 +2185,7 @@ declare namespace Electron { removed: boolean) => void): this; once(event: 'changed', listener: (event: Event, /** - * The cookie that was changed + * The cookie that was changed. */ cookie: Cookie, /** @@ -1943,7 +2198,7 @@ declare namespace Electron { removed: boolean) => void): this; addListener(event: 'changed', listener: (event: Event, /** - * The cookie that was changed + * The cookie that was changed. */ cookie: Cookie, /** @@ -1956,7 +2211,7 @@ declare namespace Electron { removed: boolean) => void): this; removeListener(event: 'changed', listener: (event: Event, /** - * The cookie that was changed + * The cookie that was changed. */ cookie: Cookie, /** @@ -1972,8 +2227,8 @@ declare namespace Electron { */ flushStore(callback: Function): void; /** - * Sends a request to get all cookies matching details, callback will be called - * with callback(error, cookies) on complete. + * Sends a request to get all cookies matching filter, callback will be called with + * callback(error, cookies) on complete. */ get(filter: Filter, callback: (error: Error, cookies: Cookie[]) => void): void; /** @@ -1994,7 +2249,7 @@ declare namespace Electron { /** * The number of average idle cpu wakeups per second since the last call to - * getCPUUsage. First call returns 0. + * getCPUUsage. First call returns 0. Will always return 0 on Windows. */ idleWakeupsPerSecond: number; /** @@ -2007,19 +2262,31 @@ declare namespace Electron { // Docs: http://electron.atom.io/docs/api/structures/crash-report - date: string; - ID: number; + date: Date; + id: string; } interface CrashReporter extends EventEmitter { // Docs: http://electron.atom.io/docs/api/crash-reporter + /** + * Set an extra parameter to be sent with the crash report. The values specified + * here will be sent in addition to any values set via the extra option when start + * was called. This API is only available on macOS, if you need to add/update extra + * parameters on Linux and Windows after your first call to start you can call + * start again with the updated extra options. + */ + addExtraParameter(key: string, value: string): void; /** * Returns the date and ID of the last crash report. If no crash reports have been * sent or the crash reporter has not been started, null is returned. */ getLastCrashReport(): CrashReport; + /** + * See all of the current parameters being passed to the crash reporter. + */ + getParameters(): void; /** * Returns all uploaded crash reports. Each report contains the date and uploaded * ID. @@ -2030,13 +2297,10 @@ declare namespace Electron { */ getUploadToServer(): boolean; /** - * Set an extra parameter to be sent with the crash report. The values specified - * here will be sent in addition to any values set via the extra option when start - * was called. This API is only available on macOS, if you need to add/update extra - * parameters on Linux and Windows after your first call to start you can call - * start again with the updated extra options. + * Remove a extra parameter from the current set of parameters so that it will not + * be sent with the crash report. */ - setExtraParameter(key: string, value: string): void; + removeExtraParameter(key: string): void; /** * This would normally be controlled by user preferences. This has no effect if * called before start is called. Note: This API can only be called from the main @@ -2057,7 +2321,7 @@ declare namespace Electron { * well. This will start the process that will monitor and send the crash reports. * Replace submitURL, productName and crashesDirectory with appropriate values. * Note: If you need send additional/updated extra parameters after your first call - * start you can call setExtraParameter on macOS or call start again with the + * start you can call addExtraParameter on macOS or call start again with the * new/updated extra parameters on Linux and Windows. Note: On macOS, Electron uses * a new crashpad client for crash collection and reporting. If you want to enable * crash reporting, initializing crashpad from the main process using @@ -2225,7 +2489,7 @@ declare namespace Electron { /** * Displays a modal dialog that shows an error message. This API can be called * safely before the ready event the app module emits, it is usually used to report - * errors in early stage of startup. If called before the app readyevent on Linux, + * errors in early stage of startup. If called before the app readyevent on Linux, * the message will be emitted to stderr, and no GUI dialog will appear. */ showErrorBox(title: string, content: string): void; @@ -2253,11 +2517,11 @@ declare namespace Electron { * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use * the '*' wildcard (no other wildcard is supported). If a callback is passed, the * API call will be asynchronous and the result will be passed via - * callback(filenames) Note: On Windows and Linux an open dialog can not be both a + * callback(filenames). Note: On Windows and Linux an open dialog can not be both a * file selector and a directory selector, so if you set properties to ['openFile', * 'openDirectory'] on these platforms, a directory selector will be shown. */ - showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (filePaths: string[]) => void): string[]; + showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): string[]; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can @@ -2266,27 +2530,27 @@ declare namespace Electron { * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use * the '*' wildcard (no other wildcard is supported). If a callback is passed, the * API call will be asynchronous and the result will be passed via - * callback(filenames) Note: On Windows and Linux an open dialog can not be both a + * callback(filenames). Note: On Windows and Linux an open dialog can not be both a * file selector and a directory selector, so if you set properties to ['openFile', * 'openDirectory'] on these platforms, a directory selector will be shown. */ - showOpenDialog(options: OpenDialogOptions, callback?: (filePaths: string[]) => void): string[]; + showOpenDialog(options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): string[]; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can * be displayed, see dialog.showOpenDialog for an example. If a callback is passed, * the API call will be asynchronous and the result will be passed via - * callback(filename) + * callback(filename). */ - showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (filename: string) => void): string; + showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): string; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can * be displayed, see dialog.showOpenDialog for an example. If a callback is passed, * the API call will be asynchronous and the result will be passed via - * callback(filename) + * callback(filename). */ - showSaveDialog(options: SaveDialogOptions, callback?: (filename: string) => void): string; + showSaveDialog(options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): string; } interface Display { @@ -2325,33 +2589,54 @@ declare namespace Electron { * download that can't be resumed. The state can be one of following: */ on(event: 'done', listener: (event: Event, - state: string) => void): this; + /** + * Can be `completed`, `cancelled` or `interrupted`. + */ + state: ('completed' | 'cancelled' | 'interrupted')) => void): this; once(event: 'done', listener: (event: Event, - state: string) => void): this; + /** + * Can be `completed`, `cancelled` or `interrupted`. + */ + state: ('completed' | 'cancelled' | 'interrupted')) => void): this; addListener(event: 'done', listener: (event: Event, - state: string) => void): this; + /** + * Can be `completed`, `cancelled` or `interrupted`. + */ + state: ('completed' | 'cancelled' | 'interrupted')) => void): this; removeListener(event: 'done', listener: (event: Event, - state: string) => void): this; + /** + * Can be `completed`, `cancelled` or `interrupted`. + */ + state: ('completed' | 'cancelled' | 'interrupted')) => void): this; /** * Emitted when the download has been updated and is not done. The state can be one * of following: */ on(event: 'updated', listener: (event: Event, - state: string) => void): this; + /** + * Can be `progressing` or `interrupted`. + */ + state: ('progressing' | 'interrupted')) => void): this; once(event: 'updated', listener: (event: Event, - state: string) => void): this; + /** + * Can be `progressing` or `interrupted`. + */ + state: ('progressing' | 'interrupted')) => void): this; addListener(event: 'updated', listener: (event: Event, - state: string) => void): this; + /** + * Can be `progressing` or `interrupted`. + */ + state: ('progressing' | 'interrupted')) => void): this; removeListener(event: 'updated', listener: (event: Event, - state: string) => void): this; + /** + * Can be `progressing` or `interrupted`. + */ + state: ('progressing' | 'interrupted')) => void): this; /** * Cancels the download operation. */ cancel(): void; - /** - * Resumes Boolean - Whether the download can resume. - */ - canResume(): void; + canResume(): boolean; getContentDisposition(): string; getETag(): string; /** @@ -2491,6 +2776,38 @@ declare namespace Electron { webgl2: string; } + interface InAppPurchase extends EventEmitter { + + // Docs: http://electron.atom.io/docs/api/in-app-purchase + + /** + * Emitted when one or more transactions have been updated. + */ + on(event: 'transactions-updated', listener: (event: Event, + /** + * Array of transactions. + */ + transactions: Transaction[]) => void): this; + once(event: 'transactions-updated', listener: (event: Event, + /** + * Array of transactions. + */ + transactions: Transaction[]) => void): this; + addListener(event: 'transactions-updated', listener: (event: Event, + /** + * Array of transactions. + */ + transactions: Transaction[]) => void): this; + removeListener(event: 'transactions-updated', listener: (event: Event, + /** + * Array of transactions. + */ + transactions: Transaction[]) => void): this; + canMakePayments(): boolean; + getReceiptURL(): string; + purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; + } + class IncomingMessage extends EventEmitter { // Docs: http://electron.atom.io/docs/api/incoming-message @@ -2624,7 +2941,7 @@ declare namespace Electron { /** * Removes all listeners, or those of the specified channel. */ - removeAllListeners(channel?: string): this; + removeAllListeners(channel: string): this; /** * Removes the specified listener from the listener array for the specified * channel. @@ -2645,7 +2962,11 @@ declare namespace Electron { * event.returnValue. Note: Sending a synchronous message will block the whole * renderer process, unless you know what you are doing you should never use it. */ - sendSync(channel: string, ...args: any[]): any; + // sendSync(channel: string, ...args: any[]): any; ### VSCODE CHANGE (we do not want to use sendSync) + /** + * Sends a message to a window with windowid via channel. + */ + sendTo(windowId: number, channel: string, ...args: any[]): void; /** * Like ipcRenderer.send but the event will be sent to the <webview> element in the * host page instead of the main process. @@ -2760,6 +3081,20 @@ declare namespace Electron { // Docs: http://electron.atom.io/docs/api/menu + /** + * Emitted when a popup is closed either manually or with menu.closePopup(). + */ + on(event: 'menu-will-close', listener: (event: Event) => void): this; + once(event: 'menu-will-close', listener: (event: Event) => void): this; + addListener(event: 'menu-will-close', listener: (event: Event) => void): this; + removeListener(event: 'menu-will-close', listener: (event: Event) => void): this; + /** + * Emitted when menu.popup() is called. + */ + on(event: 'menu-will-show', listener: (event: Event) => void): this; + once(event: 'menu-will-show', listener: (event: Event) => void): this; + addListener(event: 'menu-will-show', listener: (event: Event) => void): this; + removeListener(event: 'menu-will-show', listener: (event: Event) => void): this; constructor(); /** * Generally, the template is just an array of options for constructing a MenuItem. @@ -2772,7 +3107,7 @@ declare namespace Electron { * Note: The returned Menu instance doesn't support dynamic addition or removal of * menu items. Instance properties can still be dynamically modified. */ - static getApplicationMenu(): Menu; + static getApplicationMenu(): Menu | null; /** * Sends the action to the first responder of application. This is used for * emulating default macOS menu behaviors. Usually you would just use the role @@ -2786,7 +3121,7 @@ declare namespace Electron { * Windows and Linux but has no effect on macOS. Note: This API has to be called * after the ready event of app module. */ - static setApplicationMenu(menu: Menu): void; + static setApplicationMenu(menu: Menu | null): void; /** * Appends the menuItem to the menu. */ @@ -2795,14 +3130,15 @@ declare namespace Electron { * Closes the context menu in the browserWindow. */ closePopup(browserWindow?: BrowserWindow): void; + getMenuItemById(id: string): MenuItem; /** * Inserts the menuItem to the pos position of the menu. */ insert(pos: number, menuItem: MenuItem): void; /** - * Pops up this menu as a context menu in the browserWindow. + * Pops up this menu as a context menu in the BrowserWindow. */ - popup(browserWindow?: BrowserWindow, options?: PopupOptions): void; + popup(options: PopupOptions): void; items: MenuItem[]; } @@ -2848,6 +3184,13 @@ declare namespace Electron { * Creates a new NativeImage instance from dataURL. */ static createFromDataURL(dataURL: string): NativeImage; + /** + * Creates a new NativeImage instance from the NSImage that maps to the given image + * name. See NSImageName for a list of possible values. The hslShift is applied to + * the image with the following rules This means that [-1, 0, 1] will make the + * image completely white and [-1, 1, 0] will make the image completely black. + */ + static createFromNamedImage(imageName: string, hslShift: number[]): NativeImage; /** * Creates a new NativeImage instance from a file located at path. This method * returns an empty image if the path does not exist, cannot be read, or is not a @@ -2911,22 +3254,22 @@ declare namespace Electron { on(event: 'action', listener: (event: Event, /** - * The index of the action that was activated + * The index of the action that was activated. */ index: number) => void): this; once(event: 'action', listener: (event: Event, /** - * The index of the action that was activated + * The index of the action that was activated. */ index: number) => void): this; addListener(event: 'action', listener: (event: Event, /** - * The index of the action that was activated + * The index of the action that was activated. */ index: number) => void): this; removeListener(event: 'action', listener: (event: Event, /** - * The index of the action that was activated + * The index of the action that was activated. */ index: number) => void): this; /** @@ -2938,7 +3281,7 @@ declare namespace Electron { removeListener(event: 'click', listener: (event: Event) => void): this; /** * Emitted when the notification is closed by manual intervention from the user. - * This event is not guarunteed to be emitted in all cases where the notification + * This event is not guaranteed to be emitted in all cases where the notification * is closed. */ on(event: 'close', listener: (event: Event) => void): this; @@ -2951,22 +3294,22 @@ declare namespace Electron { */ on(event: 'reply', listener: (event: Event, /** - * The string the user entered into the inline reply field + * The string the user entered into the inline reply field. */ reply: string) => void): this; once(event: 'reply', listener: (event: Event, /** - * The string the user entered into the inline reply field + * The string the user entered into the inline reply field. */ reply: string) => void): this; addListener(event: 'reply', listener: (event: Event, /** - * The string the user entered into the inline reply field + * The string the user entered into the inline reply field. */ reply: string) => void): this; removeListener(event: 'reply', listener: (event: Event, /** - * The string the user entered into the inline reply field + * The string the user entered into the inline reply field. */ reply: string) => void): this; /** @@ -2980,11 +3323,17 @@ declare namespace Electron { removeListener(event: 'show', listener: (event: Event) => void): this; constructor(options: NotificationConstructorOptions); static isSupported(): boolean; + /** + * Dismisses the notification. + */ + close(): void; /** * Immediately shows the notification to the user, please note this means unlike * the HTML5 Notification implementation, simply instantiating a new Notification * does not immediately show it to the user, you need to call this method before - * the OS will display it. + * the OS will display it. If the notification has been shown before, this method + * will dismiss the previously shown notification and create a new one with + * identical properties. */ show(): void; } @@ -3036,6 +3385,16 @@ declare namespace Electron { once(event: 'resume', listener: Function): this; addListener(event: 'resume', listener: Function): this; removeListener(event: 'resume', listener: Function): this; + /** + * Emitted when the system is about to reboot or shut down. If the event handler + * invokes e.preventDefault(), Electron will attempt to delay system shutdown in + * order for the app to exit cleanly. If e.preventDefault() is called, the app + * should exit as soon as possible by calling something like app.quit(). + */ + on(event: 'shutdown', listener: Function): this; + once(event: 'shutdown', listener: Function): this; + addListener(event: 'shutdown', listener: Function): this; + removeListener(event: 'shutdown', listener: Function): this; /** * Emitted when the system is suspending. */ @@ -3118,6 +3477,11 @@ declare namespace Electron { * sends a new HTTP request as a response. */ interceptHttpProtocol(scheme: string, handler: (request: InterceptHttpProtocolRequest, callback: (redirectRequest: RedirectRequest) => void) => void, completion?: (error: Error) => void): void; + /** + * Same as protocol.registerStreamProtocol, except that it replaces an existing + * protocol handler. + */ + interceptStreamProtocol(scheme: string, handler: (request: InterceptStreamProtocolRequest, callback: (stream?: ReadableStream | StreamProtocolResponse) => void) => void, completion?: (error: Error) => void): void; /** * Intercepts scheme protocol and uses handler as the protocol's new handler which * sends a String as a response. @@ -3178,6 +3542,15 @@ declare namespace Electron { * the ready event of the app module gets emitted. */ registerStandardSchemes(schemes: string[], options?: RegisterStandardSchemesOptions): void; + /** + * Registers a protocol of scheme that will send a Readable as a response. The + * usage is similar to the other register{Any}Protocol, except that the callback + * should be called with either a Readable object or an object that has the data, + * statusCode, and headers properties. Example: It is possible to pass any object + * that implements the readable stream API (emits data/end/error events). For + * example, here's how a file could be returned: + */ + registerStreamProtocol(scheme: string, handler: (request: RegisterStreamProtocolRequest, callback: (stream?: ReadableStream | StreamProtocolResponse) => void) => void, completion?: (error: Error) => void): void; /** * Registers a protocol of scheme that will send a String as a response. The usage * is the same with registerFileProtocol, except that the callback should be called @@ -3380,7 +3753,7 @@ declare namespace Electron { * options, you have to ensure the Session with the partition has never been used * before. There is no way to change the options of an existing Session object. */ - static fromPartition(partition: string, options: FromPartitionOptions): Session; + static fromPartition(partition: string, options?: FromPartitionOptions): Session; /** * A Session object, the default session object of the app. */ @@ -3444,11 +3817,12 @@ declare namespace Electron { * Writes any unwritten DOMStorage data to disk. */ flushStorageData(): void; - getBlobData(identifier: string, callback: (result: Buffer) => void): Blob; + getBlobData(identifier: string, callback: (result: Buffer) => void): void; /** * Callback is invoked with the session's current cache size. */ getCacheSize(callback: (size: number) => void): void; + getPreloads(): string[]; getUserAgent(): string; /** * Resolves the proxy information for url. The callback will be called with @@ -3471,9 +3845,14 @@ declare namespace Electron { /** * Sets the handler which can be used to respond to permission requests for the * session. Calling callback(true) will allow the permission and callback(false) - * will reject it. + * will reject it. To clear the handler, call setPermissionRequestHandler(null). */ - setPermissionRequestHandler(handler: (webContents: WebContents, permission: string, callback: (permissionGranted: boolean) => void) => void): void; + setPermissionRequestHandler(handler: (webContents: WebContents, permission: string, callback: (permissionGranted: boolean) => void, details: PermissionRequestHandlerDetails) => void | null): void; + /** + * Adds scripts that will be executed on ALL web contents that are associated with + * this session just before normal preload scripts run. + */ + setPreloads(preloads: string[]): void; /** * Sets the proxy settings. When pacScript and proxyRules are provided together, * the proxyRules option is ignored and pacScript configuration is applied. The @@ -3578,6 +3957,24 @@ declare namespace Electron { width: number; } + interface StreamProtocolResponse { + + // Docs: http://electron.atom.io/docs/api/structures/stream-protocol-response + + /** + * A Node.js readable stream representing the response body + */ + data: ReadableStream; + /** + * An object containing the response headers + */ + headers: Headers; + /** + * The HTTP response code + */ + statusCode: number; + } + interface SystemPreferences extends EventEmitter { // Docs: http://electron.atom.io/docs/api/system-preferences @@ -3633,7 +4030,7 @@ declare namespace Electron { getAccentColor(): string; getColor(color: '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text'): string; /** - * This API uses NSUserDefaults on macOS. Some popular key and types are: + * Some popular key and types are: */ getUserDefault(key: string, type: 'string' | 'boolean' | 'integer' | 'float' | 'double' | 'url' | 'array' | 'dictionary'): any; /** @@ -3655,14 +4052,22 @@ declare namespace Electron { */ postNotification(event: string, userInfo: any): void; /** - * Set the value of key in system preferences. Note that type should match actual - * type of value. An exception is thrown if they don't. This API uses - * NSUserDefaults on macOS. Some popular key and types are: + * Add the specified defaults to your application's NSUserDefaults. + */ + registerDefaults(defaults: any): void; + /** + * Removes the key in NSUserDefaults. This can be used to restore the default or + * global value of a key previously set with setUserDefault. + */ + removeUserDefault(key: string): void; + /** + * Set the value of key in NSUserDefaults. Note that type should match actual type + * of value. An exception is thrown if they don't. Some popular key and types are: */ setUserDefault(key: string, type: string, value: string): void; /** * Same as subscribeNotification, but uses NSNotificationCenter for local defaults. - * This is necessary for events such as NSUserDefaultsDidChangeNotification + * This is necessary for events such as NSUserDefaultsDidChangeNotification. */ subscribeLocalNotification(event: string, callback: (event: string, userInfo: any) => void): void; /** @@ -3830,7 +4235,7 @@ declare namespace Electron { // Docs: http://electron.atom.io/docs/api/touch-bar constructor(options: TouchBarConstructorOptions); - escapeItem: any; + escapeItem: (TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer | null); static TouchBarButton: typeof TouchBarButton; static TouchBarColorPicker: typeof TouchBarColorPicker; static TouchBarGroup: typeof TouchBarGroup; @@ -3842,6 +4247,23 @@ declare namespace Electron { static TouchBarSpacer: typeof TouchBarSpacer; } + interface Transaction { + + // Docs: http://electron.atom.io/docs/api/structures/transaction + + errorCode: number; + errorMessage: string; + originalTransactionIdentifier: string; + payment: Payment; + transactionDate: string; + transactionIdentifier: string; + /** + * The transaction sate ("purchasing", "purchased", "failed", "restored", or + * "deferred") + */ + transactionState: string; + } + class Tray extends EventEmitter { // Docs: http://electron.atom.io/docs/api/tray @@ -3873,45 +4295,61 @@ declare namespace Electron { */ on(event: 'click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ - bounds: Rectangle) => void): this; + bounds: Rectangle, + /** + * The position of the event. + */ + position: Point) => void): this; once(event: 'click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ - bounds: Rectangle) => void): this; + bounds: Rectangle, + /** + * The position of the event. + */ + position: Point) => void): this; addListener(event: 'click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ - bounds: Rectangle) => void): this; + bounds: Rectangle, + /** + * The position of the event. + */ + position: Point) => void): this; removeListener(event: 'click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ - bounds: Rectangle) => void): this; + bounds: Rectangle, + /** + * The position of the event. + */ + position: Point) => void): this; /** * Emitted when the tray icon is double clicked. */ on(event: 'double-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; once(event: 'double-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; addListener(event: 'double-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; removeListener(event: 'double-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; /** @@ -3970,22 +4408,22 @@ declare namespace Electron { */ on(event: 'drop-text', listener: (event: Event, /** - * the dropped text string + * the dropped text string. */ text: string) => void): this; once(event: 'drop-text', listener: (event: Event, /** - * the dropped text string + * the dropped text string. */ text: string) => void): this; addListener(event: 'drop-text', listener: (event: Event, /** - * the dropped text string + * the dropped text string. */ text: string) => void): this; removeListener(event: 'drop-text', listener: (event: Event, /** - * the dropped text string + * the dropped text string. */ text: string) => void): this; /** @@ -3993,22 +4431,22 @@ declare namespace Electron { */ on(event: 'mouse-enter', listener: (event: Event, /** - * The position of the event + * The position of the event. */ position: Point) => void): this; once(event: 'mouse-enter', listener: (event: Event, /** - * The position of the event + * The position of the event. */ position: Point) => void): this; addListener(event: 'mouse-enter', listener: (event: Event, /** - * The position of the event + * The position of the event. */ position: Point) => void): this; removeListener(event: 'mouse-enter', listener: (event: Event, /** - * The position of the event + * The position of the event. */ position: Point) => void): this; /** @@ -4016,22 +4454,45 @@ declare namespace Electron { */ on(event: 'mouse-leave', listener: (event: Event, /** - * The position of the event + * The position of the event. */ position: Point) => void): this; once(event: 'mouse-leave', listener: (event: Event, /** - * The position of the event + * The position of the event. */ position: Point) => void): this; addListener(event: 'mouse-leave', listener: (event: Event, /** - * The position of the event + * The position of the event. */ position: Point) => void): this; removeListener(event: 'mouse-leave', listener: (event: Event, /** - * The position of the event + * The position of the event. + */ + position: Point) => void): this; + /** + * Emitted when the mouse moves in the tray icon. + */ + on(event: 'mouse-move', listener: (event: Event, + /** + * The position of the event. + */ + position: Point) => void): this; + once(event: 'mouse-move', listener: (event: Event, + /** + * The position of the event. + */ + position: Point) => void): this; + addListener(event: 'mouse-move', listener: (event: Event, + /** + * The position of the event. + */ + position: Point) => void): this; + removeListener(event: 'mouse-move', listener: (event: Event, + /** + * The position of the event. */ position: Point) => void): this; /** @@ -4039,22 +4500,22 @@ declare namespace Electron { */ on(event: 'right-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; once(event: 'right-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; addListener(event: 'right-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; removeListener(event: 'right-click', listener: (event: Event, /** - * The bounds of tray icon + * The bounds of tray icon. */ bounds: Rectangle) => void): this; constructor(image: NativeImage | string); @@ -4096,7 +4557,8 @@ declare namespace Electron { */ setPressedImage(image: NativeImage): void; /** - * Sets the title displayed aside of the tray icon in the status bar. + * Sets the title displayed aside of the tray icon in the status bar (Support ANSI + * colors). */ setTitle(title: string): void; /** @@ -4150,7 +4612,7 @@ declare namespace Electron { */ length: number; /** - * Last Modification time in number of seconds sine the UNIX epoch. + * Last Modification time in number of seconds since the UNIX epoch. */ modificationTime: number; /** @@ -4176,7 +4638,7 @@ declare namespace Electron { */ length: number; /** - * Last Modification time in number of seconds sine the UNIX epoch. + * Last Modification time in number of seconds since the UNIX epoch. */ modificationTime: number; /** @@ -4217,22 +4679,22 @@ declare namespace Electron { */ on(event: 'before-input-event', listener: (event: Event, /** - * Input properties + * Input properties. */ input: Input) => void): this; once(event: 'before-input-event', listener: (event: Event, /** - * Input properties + * Input properties. */ input: Input) => void): this; addListener(event: 'before-input-event', listener: (event: Event, /** - * Input properties + * Input properties. */ input: Input) => void): this; removeListener(event: 'before-input-event', listener: (event: Event, /** - * Input properties + * Input properties. */ input: Input) => void): this; /** @@ -4242,7 +4704,7 @@ declare namespace Electron { on(event: 'certificate-error', listener: (event: Event, url: string, /** - * The error code + * The error code. */ error: string, certificate: Certificate, @@ -4250,7 +4712,7 @@ declare namespace Electron { once(event: 'certificate-error', listener: (event: Event, url: string, /** - * The error code + * The error code. */ error: string, certificate: Certificate, @@ -4258,7 +4720,7 @@ declare namespace Electron { addListener(event: 'certificate-error', listener: (event: Event, url: string, /** - * The error code + * The error code. */ error: string, certificate: Certificate, @@ -4266,11 +4728,31 @@ declare namespace Electron { removeListener(event: 'certificate-error', listener: (event: Event, url: string, /** - * The error code + * The error code. */ error: string, certificate: Certificate, callback: (isTrusted: boolean) => void) => void): this; + /** + * Emitted when the associated window logs a console message. Will not be emitted + * for windows with offscreen rendering enabled. + */ + on(event: 'console-message', listener: (level: number, + message: string, + line: number, + sourceId: string) => void): this; + once(event: 'console-message', listener: (level: number, + message: string, + line: number, + sourceId: string) => void): this; + addListener(event: 'console-message', listener: (level: number, + message: string, + line: number, + sourceId: string) => void): this; + removeListener(event: 'console-message', listener: (level: number, + message: string, + line: number, + sourceId: string) => void): this; /** * Emitted when there is a new context menu that needs to be handled. */ @@ -4300,69 +4782,69 @@ declare namespace Electron { * nwse-resize, col-resize, row-resize, m-panning, e-panning, n-panning, * ne-panning, nw-panning, s-panning, se-panning, sw-panning, w-panning, move, * vertical-text, cell, context-menu, alias, progress, nodrop, copy, none, - * not-allowed, zoom-in, zoom-out, grab, grabbing, custom. If the type parameter is - * custom, the image parameter will hold the custom cursor image in a NativeImage, - * and scale, size and hotspot will hold additional information about the custom - * cursor. + * not-allowed, zoom-in, zoom-out, grab, grabbing or custom. If the type parameter + * is custom, the image parameter will hold the custom cursor image in a + * NativeImage, and scale, size and hotspot will hold additional information about + * the custom cursor. */ on(event: 'cursor-changed', listener: (event: Event, type: string, image?: NativeImage, /** - * scaling factor for the custom cursor + * scaling factor for the custom cursor. */ scale?: number, /** - * the size of the `image` + * the size of the `image`. */ size?: Size, /** - * coordinates of the custom cursor's hotspot + * coordinates of the custom cursor's hotspot. */ hotspot?: Point) => void): this; once(event: 'cursor-changed', listener: (event: Event, type: string, image?: NativeImage, /** - * scaling factor for the custom cursor + * scaling factor for the custom cursor. */ scale?: number, /** - * the size of the `image` + * the size of the `image`. */ size?: Size, /** - * coordinates of the custom cursor's hotspot + * coordinates of the custom cursor's hotspot. */ hotspot?: Point) => void): this; addListener(event: 'cursor-changed', listener: (event: Event, type: string, image?: NativeImage, /** - * scaling factor for the custom cursor + * scaling factor for the custom cursor. */ scale?: number, /** - * the size of the `image` + * the size of the `image`. */ size?: Size, /** - * coordinates of the custom cursor's hotspot + * coordinates of the custom cursor's hotspot. */ hotspot?: Point) => void): this; removeListener(event: 'cursor-changed', listener: (event: Event, type: string, image?: NativeImage, /** - * scaling factor for the custom cursor + * scaling factor for the custom cursor. */ scale?: number, /** - * the size of the `image` + * the size of the `image`. */ size?: Size, /** - * coordinates of the custom cursor's hotspot + * coordinates of the custom cursor's hotspot. */ hotspot?: Point) => void): this; /** @@ -4400,14 +4882,53 @@ declare namespace Electron { once(event: 'devtools-reload-page', listener: Function): this; addListener(event: 'devtools-reload-page', listener: Function): this; removeListener(event: 'devtools-reload-page', listener: Function): this; + /** + * Emitted when a <webview> has been attached to this web contents. + */ + on(event: 'did-attach-webview', listener: (event: Event, + /** + * The guest web contents that is used by the `<webview>`. + */ + webContents: WebContents) => void): this; + once(event: 'did-attach-webview', listener: (event: Event, + /** + * The guest web contents that is used by the `<webview>`. + */ + webContents: WebContents) => void): this; + addListener(event: 'did-attach-webview', listener: (event: Event, + /** + * The guest web contents that is used by the `<webview>`. + */ + webContents: WebContents) => void): this; + removeListener(event: 'did-attach-webview', listener: (event: Event, + /** + * The guest web contents that is used by the `<webview>`. + */ + webContents: WebContents) => void): this; /** * Emitted when a page's theme color changes. This is usually due to encountering a * meta tag: */ - on(event: 'did-change-theme-color', listener: Function): this; - once(event: 'did-change-theme-color', listener: Function): this; - addListener(event: 'did-change-theme-color', listener: Function): this; - removeListener(event: 'did-change-theme-color', listener: Function): this; + on(event: 'did-change-theme-color', listener: (event: Event, + /** + * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. + */ + color: string | null) => void): this; + once(event: 'did-change-theme-color', listener: (event: Event, + /** + * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. + */ + color: string | null) => void): this; + addListener(event: 'did-change-theme-color', listener: (event: Event, + /** + * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. + */ + color: string | null) => void): this; + removeListener(event: 'did-change-theme-color', listener: (event: Event, + /** + * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. + */ + color: string | null) => void): this; /** * This event is like did-finish-load but emitted when the load failed or was * cancelled, e.g. window.stop() is invoked. The full list of error codes and their @@ -4643,7 +5164,7 @@ declare namespace Electron { */ disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), /** - * The options which will be used for creating the new `BrowserWindow`. + * The options which will be used for creating the new . */ options: any, /** @@ -4660,7 +5181,7 @@ declare namespace Electron { */ disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), /** - * The options which will be used for creating the new `BrowserWindow`. + * The options which will be used for creating the new . */ options: any, /** @@ -4677,7 +5198,7 @@ declare namespace Electron { */ disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), /** - * The options which will be used for creating the new `BrowserWindow`. + * The options which will be used for creating the new . */ options: any, /** @@ -4694,7 +5215,7 @@ declare namespace Electron { */ disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), /** - * The options which will be used for creating the new `BrowserWindow`. + * The options which will be used for creating the new . */ options: any, /** @@ -4707,22 +5228,22 @@ declare namespace Electron { */ on(event: 'page-favicon-updated', listener: (event: Event, /** - * Array of URLs + * Array of URLs. */ favicons: string[]) => void): this; once(event: 'page-favicon-updated', listener: (event: Event, /** - * Array of URLs + * Array of URLs. */ favicons: string[]) => void): this; addListener(event: 'page-favicon-updated', listener: (event: Event, /** - * Array of URLs + * Array of URLs. */ favicons: string[]) => void): this; removeListener(event: 'page-favicon-updated', listener: (event: Event, /** - * Array of URLs + * Array of URLs. */ favicons: string[]) => void): this; /** @@ -4771,8 +5292,8 @@ declare namespace Electron { /** * Emitted when bluetooth device needs to be selected on call to * navigator.bluetooth.requestDevice. To use navigator.bluetooth api webBluetooth - * should be enabled. If event.preventDefault is not called, first available - * device will be selected. callback should be called with deviceId to be selected, + * should be enabled. If event.preventDefault is not called, first available device + * will be selected. callback should be called with deviceId to be selected, * passing empty string to callback will cancel the request. */ on(event: 'select-bluetooth-device', listener: (event: Event, @@ -4935,13 +5456,13 @@ declare namespace Electron { * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + capturePage(callback: (image: NativeImage) => void): void; /** * Captures a snapshot of the page within rect. Upon completion callback will be * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. */ - capturePage(callback: (image: NativeImage) => void): void; + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; /** * Clears the navigation history. */ @@ -4988,16 +5509,15 @@ declare namespace Electron { * requestFullScreen can only be invoked by a gesture from the user. Setting * userGesture to true will remove this limitation. If the result of the executed * code is a promise the callback result will be the resolved value of the promise. - * We recommend that you use the returned Promise to handle code that results in a + * We recommend that you use the returned Promise to handle code that results in a * Promise. */ executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise<any>; /** - * Starts a request to find all matches for the text in the web page and returns an - * Integer representing the request id used for the request. The result of the - * request can be obtained by subscribing to found-in-page event. + * Starts a request to find all matches for the text in the web page. The result of + * the request can be obtained by subscribing to found-in-page event. */ - findInPage(text: string, options?: FindInPageOptions): void; + findInPage(text: string, options?: FindInPageOptions): number; /** * Focuses the web page. */ @@ -5076,6 +5596,12 @@ declare namespace Electron { isOffscreen(): boolean; isPainting(): boolean; isWaitingForResponse(): boolean; + /** + * Loads the given file in the window, filePath should be a path to an HTML file + * relative to the root of your application. For instance an app structure like + * this: Would require code like this + */ + loadFile(filePath: string): void; /** * Loads the url in the window. The url must contain the protocol prefix, e.g. the * http:// or file://. If the load should bypass http cache then use the pragma @@ -5083,7 +5609,9 @@ declare namespace Electron { */ loadURL(url: string, options?: LoadURLOptions): void; /** - * Opens the devtools. + * Opens the devtools. When contents is a <webview> tag, the mode would be detach + * by default, explicitly passing an empty mode can force using last used dock + * state. */ openDevTools(options?: OpenDevToolsOptions): void; /** @@ -5101,7 +5629,7 @@ declare namespace Electron { * webContents.print({silent: false, printBackground: false, deviceName: ''}). Use * page-break-before: always; CSS style to force to print to a new page. */ - print(options?: PrintOptions): void; + print(options?: PrintOptions, callback?: (success: boolean) => void): void; /** * Prints window's web page as PDF with Chromium's preview printing custom * settings. The callback will be called with callback(error, data) on completion. @@ -5160,6 +5688,19 @@ declare namespace Electron { * Mute the audio on the current web page. */ setAudioMuted(muted: boolean): void; + /** + * Uses the devToolsWebContents as the target WebContents to show devtools. The + * devToolsWebContents must not have done any navigation, and it should not be used + * for other purposes after the call. By default Electron manages the devtools by + * creating an internal WebContents with native view, which developers have very + * limited control of. With the setDevToolsWebContents method, developers can use + * any WebContents to show the devtools in it, including BrowserWindow, BrowserView + * and <webview> tag. Note that closing the devtools does not destroy the + * devToolsWebContents, it is caller's responsibility to destroy + * devToolsWebContents. An example of showing devtools in a <webview> tag: An + * example of showing devtools in a BrowserWindow: + */ + setDevToolsWebContents(devToolsWebContents: WebContents): void; /** * If offscreen rendering is enabled sets the frame rate to the specified number. * Only values between 1 and 60 are accepted. @@ -5187,7 +5728,7 @@ declare namespace Electron { setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; /** * Setting the WebRTC IP handling policy allows you to control which IPs are - * exposed via WebRTC. See BrowserLeaks for more details. + * exposed via WebRTC. See BrowserLeaks for more details. */ setWebRTCIPHandlingPolicy(policy: 'default' | 'default_public_interface_only' | 'default_public_and_private_interfaces' | 'disable_non_proxied_udp'): void; /** @@ -5198,14 +5739,10 @@ declare namespace Electron { /** * Changes the zoom level to the specified level. The original size is 0 and each * increment above or below represents zooming 20% larger or smaller to default - * limits of 300% and 50% of original size, respectively. + * limits of 300% and 50% of original size, respectively. The formula for this is + * scale := 1.2 ^ level. */ setZoomLevel(level: number): void; - /** - * Deprecated: Call setVisualZoomLevelLimits instead to set the visual zoom level - * limits. This method will be removed in Electron 2.0. - */ - setZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; /** * Shows pop-up dictionary that searches the selected word on the page. */ @@ -5276,6 +5813,10 @@ declare namespace Electron { * userGesture to true will remove this limitation. */ executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise<any>; + /** + * Work like executeJavaScript but evaluates scripts in isolated context. + */ + executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): void; /** * Returns an object describing usage information of Blink's internal memory * caches. This will generate: @@ -5305,6 +5846,18 @@ declare namespace Electron { * cannot be corrupted by active network attackers. */ registerURLSchemeAsSecure(scheme: string): void; + /** + * Set the content security policy of the isolated world. + */ + setIsolatedWorldContentSecurityPolicy(worldId: number, csp: string): void; + /** + * Set the name of the isolated world. Useful in devtools. + */ + setIsolatedWorldHumanReadableName(worldId: number, name: string): void; + /** + * Set the security origin of the isolated world. + */ + setIsolatedWorldSecurityOrigin(worldId: number, securityOrigin: string): void; /** * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. */ @@ -5330,22 +5883,28 @@ declare namespace Electron { * limits of 300% and 50% of original size, respectively. */ setZoomLevel(level: number): void; - /** - * Deprecated: Call setVisualZoomLevelLimits instead to set the visual zoom level - * limits. This method will be removed in Electron 2.0. - */ - setZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; } class WebRequest extends EventEmitter { // Docs: http://electron.atom.io/docs/api/web-request + /** + * The listener will be called with listener(details) when a server initiated + * redirect is about to occur. + */ + onBeforeRedirect(listener: (details: OnBeforeRedirectDetails) => void): void; /** * The listener will be called with listener(details) when a server initiated * redirect is about to occur. */ onBeforeRedirect(filter: OnBeforeRedirectFilter, listener: (details: OnBeforeRedirectDetails) => void): void; + /** + * The listener will be called with listener(details, callback) when a request is + * about to occur. The uploadData is an array of UploadData objects. The callback + * has to be called with an response object. + */ + onBeforeRequest(listener: (details: OnBeforeRequestDetails, callback: (response: Response) => void) => void): void; /** * The listener will be called with listener(details, callback) when a request is * about to occur. The uploadData is an array of UploadData objects. The callback @@ -5359,10 +5918,25 @@ declare namespace Electron { * has to be called with an response object. */ onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: Function): void; + /** + * The listener will be called with listener(details, callback) before sending an + * HTTP request, once the request headers are available. This may occur after a TCP + * connection is made to the server, but before any http data is sent. The callback + * has to be called with an response object. + */ + onBeforeSendHeaders(listener: Function): void; /** * The listener will be called with listener(details) when a request is completed. */ onCompleted(filter: OnCompletedFilter, listener: (details: OnCompletedDetails) => void): void; + /** + * The listener will be called with listener(details) when a request is completed. + */ + onCompleted(listener: (details: OnCompletedDetails) => void): void; + /** + * The listener will be called with listener(details) when an error occurs. + */ + onErrorOccurred(listener: (details: OnErrorOccurredDetails) => void): void; /** * The listener will be called with listener(details) when an error occurs. */ @@ -5373,6 +5947,18 @@ declare namespace Electron { * response object. */ onHeadersReceived(filter: OnHeadersReceivedFilter, listener: Function): void; + /** + * The listener will be called with listener(details, callback) when HTTP response + * headers of a request have been received. The callback has to be called with an + * response object. + */ + onHeadersReceived(listener: Function): void; + /** + * The listener will be called with listener(details) when first byte of the + * response body is received. For HTTP requests, this means that the status line + * and response headers are available. + */ + onResponseStarted(listener: (details: OnResponseStartedDetails) => void): void; /** * The listener will be called with listener(details) when first byte of the * response body is received. For HTTP requests, this means that the status line @@ -5385,6 +5971,24 @@ declare namespace Electron { * response are visible by the time this listener is fired. */ onSendHeaders(filter: OnSendHeadersFilter, listener: (details: OnSendHeadersDetails) => void): void; + /** + * The listener will be called with listener(details) just before a request is + * going to be sent to the server, modifications of previous onBeforeSendHeaders + * response are visible by the time this listener is fired. + */ + onSendHeaders(listener: (details: OnSendHeadersDetails) => void): void; + } + + interface WebSource { + + // Docs: http://electron.atom.io/docs/api/structures/web-source + + code: string; + /** + * Default is 1. + */ + startLine?: number; + url?: string; } interface WebviewTag extends HTMLElement { @@ -5575,6 +6179,10 @@ declare namespace Electron { */ addEventListener(event: 'devtools-focused', listener: (event: Event) => void, useCapture?: boolean): this; removeEventListener(event: 'devtools-focused', listener: (event: Event) => void): this; + addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; + removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; canGoBack(): boolean; canGoForward(): boolean; canGoToOffset(offset: number): boolean; @@ -5613,13 +6221,12 @@ declare namespace Electron { * context in the page. HTML APIs like requestFullScreen, which require user * action, can take advantage of this option for automation. */ - executeJavaScript(code: string, userGesture: boolean, callback?: (result: any) => void): void; + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): void; /** - * Starts a request to find all matches for the text in the web page and returns an - * Integer representing the request id used for the request. The result of the - * request can be obtained by subscribing to found-in-page event. + * Starts a request to find all matches for the text in the web page. The result of + * the request can be obtained by subscribing to found-in-page event. */ - findInPage(text: string, options?: FindInPageOptions): void; + findInPage(text: string, options?: FindInPageOptions): number; getTitle(): string; getURL(): string; getUserAgent(): string; @@ -5767,7 +6374,7 @@ declare namespace Electron { * When this attribute is present the guest page will be allowed to open new * windows. Popups are disabled by default. */ - allowpopups?: string; + // allowpopups?: string; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### /** * When this attribute is present the webview container will automatically resize * within the bounds specified by the attributes minwidth, minheight, maxwidth, and @@ -5800,7 +6407,7 @@ declare namespace Electron { * When this attribute is present the guest page will have web security disabled. * Web security is enabled by default. */ - disablewebsecurity?: string; + // disablewebsecurity?: string; ### VSCODE CHANGE(https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### /** * A value that links the webview to a specific webContents. When a webview first * loads a new webContents is created and this attribute is set to its instance @@ -6062,6 +6669,10 @@ declare namespace Electron { * is true. */ fullscreenable?: boolean; + /** + * Use pre-Lion fullscreen on macOS. Default is false. + */ + simpleFullscreen?: boolean; /** * Whether to show the window in taskbar. Default is false. */ @@ -6115,7 +6726,7 @@ declare namespace Electron { */ enableLargerThanScreen?: boolean; /** - * Window's background color as Hexadecimal value, like #66CD00 or #FFF or + * Window's background color as a hexadecimal value, like #66CD00 or #FFF or * #80FFFFFF (alpha is supported). Default is #FFF (white). */ backgroundColor?: string; @@ -6124,6 +6735,11 @@ declare namespace Electron { * is true. */ hasShadow?: boolean; + /** + * Set the initial opacity of the window, between 0.0 (fully transparent) and 1.0 + * (fully opaque). This is only implemented on Windows and macOS. + */ + opacity?: number; /** * Forces using dark theme for the window, only works on some GTK+3 desktop * environments. Default is false. @@ -6140,9 +6756,9 @@ declare namespace Electron { /** * The style of window title bar. Default is default. Possible values are: */ - titleBarStyle?: ('default' | 'hidden' | 'hidden-inset' | 'hiddenInset' | 'customButtonsOnHover'); + titleBarStyle?: ('default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover'); /** - * Shows the title in the tile bar in full screen mode on macOS for all + * Shows the title in the title bar in full screen mode on macOS for all * titleBarStyle options. Default is false. */ fullscreenWindowTitle?: boolean; @@ -6155,7 +6771,8 @@ declare namespace Electron { /** * Add a type of vibrancy effect to the window, only on macOS. Can be * appearance-based, light, dark, titlebar, selection, menu, popover, sidebar, - * medium-light or ultra-dark. + * medium-light or ultra-dark. Please note that using frame: false in combination + * with a vibrancy value requires that you use a non-default titleBarStyle as well. */ vibrancy?: ('appearance-based' | 'light' | 'dark' | 'titlebar' | 'selection' | 'menu' | 'popover' | 'sidebar' | 'medium-light' | 'ultra-dark'); /** @@ -6196,7 +6813,11 @@ declare namespace Electron { /** * Verification result from chromium. */ - error: string; + verificationResult: string; + /** + * Error code. + */ + errorCode: number; } interface ClearStorageDataOptions { @@ -6206,7 +6827,7 @@ declare namespace Electron { origin?: string; /** * The types of storages to clear, can contain: appcache, cookies, filesystem, - * indexdb, localstorage, shadercache, websql, serviceworkers + * indexdb, localstorage, shadercache, websql, serviceworkers. */ storages?: string[]; /** @@ -6253,11 +6874,11 @@ declare namespace Electron { interface ContextMenuParams { /** - * x coordinate + * x coordinate. */ x: number; /** - * y coordinate + * y coordinate. */ y: number; /** @@ -6317,8 +6938,8 @@ declare namespace Electron { */ inputFieldType: string; /** - * Input source that invoked the context menu. Can be none, mouse, keyboard, touch, - * touchMenu. + * Input source that invoked the context menu. Can be none, mouse, keyboard, touch + * or touchMenu. */ menuSourceType: ('none' | 'mouse' | 'keyboard' | 'touch' | 'touchMenu'); /** @@ -6355,9 +6976,10 @@ declare namespace Electron { * properties are sent correctly. Nested objects are not supported and the property * names and values must be less than 64 characters long. */ - extra?: any; + extra?: Extra; /** - * Only used when the crash reporter is used in a forked process (macOS only). + * Directory to store the crashreports temporarily (only used when the crash + * reporter is started via process.crashReporter.start). */ crashesDirectory?: string; } @@ -6502,9 +7124,12 @@ declare namespace Electron { } interface DisplayBalloonOptions { + /** + * - + */ icon?: NativeImage | string; - title?: string; - content?: string; + title: string; + content: string; } interface Dock { @@ -6569,6 +7194,18 @@ declare namespace Electron { interface Extensions { } + interface FeedURLOptions { + url: string; + /** + * HTTP request headers. + */ + headers?: Headers; + /** + * Either json or default, see the README for more information. + */ + serverType?: string; + } + interface FileIconOptions { size: ('small' | 'normal' | 'large'); } @@ -6584,7 +7221,7 @@ declare namespace Electron { */ name?: string; /** - * Retrieves cookies whose domains match or are subdomains of domains + * Retrieves cookies whose domains match or are subdomains of domains. */ domain?: string; /** @@ -6644,6 +7281,18 @@ declare namespace Electron { name: string; } + interface Headers { + } + + interface IgnoreMouseEventsOptions { + /** + * If true, forwards mouse move messages to Chromium, enabling mouse related events + * such as mouseleave. Only used when ignore is true. If ignore is false, + * forwarding is always disabled regardless of this value. + */ + forward?: boolean; + } + interface ImportCertificateOptions { /** * Path for the pkcs12 file. @@ -6657,35 +7306,35 @@ declare namespace Electron { interface Input { /** - * Either keyUp or keyDown + * Either keyUp or keyDown. */ type: string; /** - * Equivalent to + * Equivalent to . */ key: string; /** - * Equivalent to + * Equivalent to . */ code: string; /** - * Equivalent to + * Equivalent to . */ isAutoRepeat: boolean; /** - * Equivalent to + * Equivalent to . */ shift: boolean; /** - * Equivalent to + * Equivalent to . */ control: boolean; /** - * Equivalent to + * Equivalent to . */ alt: boolean; /** - * Equivalent to + * Equivalent to . */ meta: boolean; } @@ -6711,6 +7360,14 @@ declare namespace Electron { uploadData: UploadData[]; } + interface InterceptStreamProtocolRequest { + url: string; + headers: Headers; + referrer: string; + method: string; + uploadData: UploadData[]; + } + interface InterceptStringProtocolRequest { url: string; referrer: string; @@ -6767,6 +7424,9 @@ declare namespace Electron { * Extra headers separated by "\n" */ extraHeaders?: string; + /** + * - + */ postData?: UploadRawData[] | UploadFile[] | UploadFileSystem[] | UploadBlob[]; /** * Base url (with trailing path separator) for files to be loaded by the data url. @@ -6783,25 +7443,25 @@ declare namespace Electron { */ openAtLogin: boolean; /** - * true if the app is set to open as hidden at login. This setting is only - * supported on macOS. + * true if the app is set to open as hidden at login. This setting is not available + * on . */ openAsHidden: boolean; /** - * true if the app was opened at login automatically. This setting is only - * supported on macOS. + * true if the app was opened at login automatically. This setting is not available + * on . */ wasOpenedAtLogin: boolean; /** * true if the app was opened as a hidden login item. This indicates that the app - * should not open any windows at startup. This setting is only supported on macOS. + * should not open any windows at startup. This setting is not available on . */ wasOpenedAsHidden: boolean; /** * true if the app was opened as a login item that should restore the state from * the previous session. This indicates that the app should restore the windows - * that were open the last time the app was closed. This setting is only supported - * on macOS. + * that were open the last time the app was closed. This setting is not available + * on . */ restoreState: boolean; } @@ -6827,7 +7487,7 @@ declare namespace Electron { * Define the action of the menu item, when specified the click property will be * ignored. See . */ - role?: MenuItemRole; + role?: string; /** * Can be normal, separator, submenu, checkbox or radio. */ @@ -6850,7 +7510,7 @@ declare namespace Electron { checked?: boolean; /** * Should be specified for submenu type menu items. If submenu is specified, the - * type: 'submenu' can be omitted. If the value is not a Menu then it will be + * type: 'submenu' can be omitted. If the value is not a then it will be * automatically converted to one using Menu.buildFromTemplate. */ submenu?: MenuItemConstructorOptions[] | Menu; @@ -6940,7 +7600,7 @@ declare namespace Electron { */ disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'); /** - * The options which should be used for creating the new `BrowserWindow`. + * The options which should be used for creating the new . */ options: Options; } @@ -6948,7 +7608,7 @@ declare namespace Electron { interface NotificationConstructorOptions { /** * A title for the notification, which will be shown at the top of the notification - * window when it is shown + * window when it is shown. */ title: string; /** @@ -6957,17 +7617,17 @@ declare namespace Electron { subtitle?: string; /** * The body text of the notification, which will be displayed below the title or - * subtitle + * subtitle. */ body: string; /** - * Whether or not to emit an OS notification noise when showing the notification + * Whether or not to emit an OS notification noise when showing the notification. */ silent?: boolean; /** - * An icon to use in the notification + * An icon to use in the notification. */ - icon?: NativeImage; + icon?: string | NativeImage; /** * Whether or not to add an inline reply option to the notification. */ @@ -6982,15 +7642,21 @@ declare namespace Electron { sound?: string; /** * Actions to add to the notification. Please read the available actions and - * limitations in the NotificationAction documentation + * limitations in the NotificationAction documentation. */ actions?: NotificationAction[]; + /** + * A custom title for the close button of an alert. An empty string will cause the + * default localized text to be used. + */ + closeButtonText?: string; } interface OnBeforeRedirectDetails { - id: string; + id: number; url: string; method: string; + webContentsId?: number; resourceType: string; timestamp: number; redirectURL: string; @@ -7015,6 +7681,7 @@ declare namespace Electron { id: number; url: string; method: string; + webContentsId?: number; resourceType: string; timestamp: number; uploadData: UploadData[]; @@ -7040,6 +7707,7 @@ declare namespace Electron { id: number; url: string; method: string; + webContentsId?: number; resourceType: string; timestamp: number; responseHeaders: ResponseHeaders; @@ -7060,6 +7728,7 @@ declare namespace Electron { id: number; url: string; method: string; + webContentsId?: number; resourceType: string; timestamp: number; fromCache: boolean; @@ -7089,6 +7758,7 @@ declare namespace Electron { id: number; url: string; method: string; + webContentsId?: number; resourceType: string; timestamp: number; responseHeaders: ResponseHeaders; @@ -7112,6 +7782,7 @@ declare namespace Electron { id: number; url: string; method: string; + webContentsId?: number; resourceType: string; timestamp: number; requestHeaders: RequestHeaders; @@ -7152,6 +7823,10 @@ declare namespace Electron { * Message to display above input boxes. */ message?: string; + /** + * Create when packaged for the Mac App Store. + */ + securityScopedBookmarks?: boolean; } interface OpenExternalOptions { @@ -7175,50 +7850,56 @@ declare namespace Electron { interface Parameters { /** - * Specify the screen type to emulate (default: desktop) + * Specify the screen type to emulate (default: desktop): */ screenPosition: ('desktop' | 'mobile'); /** - * Set the emulated screen size (screenPosition == mobile) + * Set the emulated screen size (screenPosition == mobile). */ screenSize: Size; /** * Position the view on the screen (screenPosition == mobile) (default: {x: 0, y: - * 0}) + * 0}). */ viewPosition: Point; /** * Set the device scale factor (if zero defaults to original device scale factor) - * (default: 0) + * (default: 0). */ deviceScaleFactor: number; /** * Set the emulated view size (empty means no override) */ viewSize: Size; - /** - * Whether emulated view should be scaled down if necessary to fit into available - * space (default: false) - */ - fitToView: boolean; - /** - * Offset of the emulated view inside available space (not in fit to view mode) - * (default: {x: 0, y: 0}) - */ - offset: Point; /** * Scale of emulated view inside available space (not in fit to view mode) - * (default: 1) + * (default: 1). */ scale: number; } + interface Payment { + productIdentifier: string; + quantity: number; + } + + interface PermissionRequestHandlerDetails { + /** + * The url of the openExternal request. + */ + externalURL: string; + } + interface PluginCrashedEvent extends Event { name: string; version: string; } interface PopupOptions { + /** + * Default is the focused window. + */ + window?: BrowserWindow; /** * Default is the current mouse cursor position. Must be declared if y is declared. */ @@ -7227,16 +7908,15 @@ declare namespace Electron { * Default is the current mouse cursor position. Must be declared if x is declared. */ y?: number; - /** - * Set to true to have this method return immediately called, false to return after - * the menu has been selected or closed. Defaults to false. - */ - async?: boolean; /** * The index of the menu item to be positioned under the mouse cursor at the * specified coordinates. Default is -1. */ positioningItem?: number; + /** + * Called when menu is closed. + */ + callback?: () => void; } interface PrintOptions { @@ -7295,21 +7975,21 @@ declare namespace Electron { privateBytes: number; /** * The amount of memory shared between processes, typically memory consumed by the - * Electron code itself + * Electron code itself. */ sharedBytes: number; } interface ProgressBarOptions { /** - * Mode for the progress bar. Can be none, normal, indeterminate, error, or paused. + * Mode for the progress bar. Can be none, normal, indeterminate, error or paused. */ - mode: ('none' | 'normal' | 'indeterminate' | 'error'); + mode: ('none' | 'normal' | 'indeterminate' | 'error' | 'paused'); } interface Provider { /** - * Returns Boolean + * Returns Boolean. */ spellCheck: (text: string) => void; } @@ -7354,6 +8034,14 @@ declare namespace Electron { secure?: boolean; } + interface RegisterStreamProtocolRequest { + url: string; + headers: Headers; + referrer: string; + method: string; + uploadData: UploadData[]; + } + interface RegisterStringProtocolRequest { url: string; referrer: string; @@ -7401,7 +8089,7 @@ declare namespace Electron { */ width?: number; /** - * Defaults to the image's height + * Defaults to the image's height. */ height?: number; /** @@ -7472,6 +8160,11 @@ declare namespace Electron { * Show the tags input box, defaults to true. */ showsTagField?: boolean; + /** + * Create a when packaged for the Mac App Store. If this option is enabled and the + * file doesn't already exist a blank file will be created at the chosen path. + */ + securityScopedBookmarks?: boolean; } interface Settings { @@ -7484,7 +8177,7 @@ declare namespace Electron { * true to open the app as hidden. Defaults to false. The user can edit this * setting from the System Preferences so * app.getLoginItemStatus().wasOpenedAsHidden should be checked when the app is - * opened to know the current value. This setting is only supported on macOS. + * opened to know the current value. This setting is not available on . */ openAsHidden?: boolean; /** @@ -7499,11 +8192,26 @@ declare namespace Electron { } interface SizeOptions { + /** + * true to make the webview container automatically resize within the bounds + * specified by the attributes normal, min and max. + */ + enableAutoSize?: boolean; /** * Normal size of the page. This can be used in combination with the attribute to * manually resize the webview guest contents. */ - normal?: Normal; + normal?: Size; + /** + * Minimum size of the page. This can be used in combination with the attribute to + * manually resize the webview guest contents. + */ + min?: Size; + /** + * Maximium size of the page. This can be used in combination with the attribute to + * manually resize the webview guest contents. + */ + max?: Size; } interface SourcesOptions { @@ -7585,7 +8293,7 @@ declare namespace Electron { /** * Can be left, right or overlay. */ - iconPosition: ('left' | 'right' | 'overlay'); + iconPosition?: ('left' | 'right' | 'overlay'); /** * Function to call when the button is clicked. */ @@ -7608,8 +8316,8 @@ declare namespace Electron { } interface TouchBarConstructorOptions { - items: (TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer)[]; - escapeItem?: TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer; + items: Array<TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer>; + escapeItem?: TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer | null; } interface TouchBarGroupConstructorOptions { @@ -7652,15 +8360,15 @@ declare namespace Electron { interface TouchBarScrubberConstructorOptions { /** - * An array of items to place in this scrubber + * An array of items to place in this scrubber. */ items: ScrubberItem[]; /** - * Called when the user taps an item that was not the last tapped item + * Called when the user taps an item that was not the last tapped item. */ select: (selectedIndex: number) => void; /** - * Called when the user taps any item + * Called when the user taps any item. */ highlight: (highlightedIndex: number) => void; /** @@ -7704,7 +8412,7 @@ declare namespace Electron { */ selectedIndex?: number; /** - * Called when the user selects a new segment + * Called when the user selects a new segment. */ change: (selectedIndex: number, isSelected: boolean) => void; } @@ -7809,9 +8517,6 @@ declare namespace Electron { finalUpdate: boolean; } - interface Headers { - } - interface MediaFlags { /** * Whether the media element has crashed. @@ -7847,11 +8552,6 @@ declare namespace Electron { canRotate: boolean; } - interface Normal { - width: number; - height: number; - } - interface Options { } @@ -7911,6 +8611,15 @@ declare namespace Electron { * session. */ partition?: string; + /** + * When specified, web pages with the same affinity will run in the same renderer + * process. Note that due to reusing the renderer process, certain webPreferences + * options will also be shared between the web pages even when you specified + * different values for them, including but not limited to preload, sandbox and + * nodeIntegration. So it is suggested to use exact same webPreferences for web + * pages with the same affinity. + */ + affinity?: string; /** * The default zoom factor of the page, 3.0 represents 300%. Default is 1.0. */ @@ -7924,12 +8633,12 @@ declare namespace Electron { * websites by people), and set allowRunningInsecureContent to true if this options * has not been set by user. Default is true. */ - webSecurity?: boolean; + // webSecurity?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### /** * Allow an https page to run JavaScript, CSS or plugins from http URLs. Default is * false. */ - allowRunningInsecureContent?: boolean; + // allowRunningInsecureContent?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### /** * Enables image support. Default is true. */ @@ -7953,7 +8662,7 @@ declare namespace Electron { /** * Enables Chromium's experimental features. Default is false. */ - experimentalFeatures?: boolean; + // experimentalFeatures?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### /** * Enables Chromium's experimental canvas features. Default is false. */ @@ -7994,7 +8703,7 @@ declare namespace Electron { defaultEncoding?: string; /** * Whether to throttle animations and timers when the page becomes background. This - * also affects the [Page Visibility API][#page-visibility]. Defaults to true. + * also affects the . Defaults to true. */ backgroundThrottling?: boolean; /** @@ -8031,6 +8740,12 @@ declare namespace Electron { * alter the <webview>'s initial settings. */ webviewTag?: boolean; + /** + * A list of strings that will be appended to process.argv in the renderer process + * of this app. Useful for passing small bits of data down to renderer process + * preload scripts. + */ + additionArguments?: string[]; } interface DefaultFontFamily { @@ -8108,6 +8823,7 @@ declare namespace NodeJS { // Docs: http://electron.atom.io/docs/api/process + // ### BEGIN VSCODE MODIFICATION ### // /** // * Emitted when Electron has loaded its internal initialization script and is // * beginning to load the web page or the main script. It can be used by the preload @@ -8118,6 +8834,8 @@ declare namespace NodeJS { // once(event: 'loaded', listener: Function): this; // addListener(event: 'loaded', listener: Function): this; // removeListener(event: 'loaded', listener: Function): this; + // ### END VSCODE MODIFICATION ### + /** * Causes the main thread of the current process crash. */ @@ -8160,8 +8878,8 @@ declare namespace NodeJS { noAsar?: boolean; /** * A Boolean that controls whether or not deprecation warnings are printed to - * stderr. Setting this to true will silence deprecation warnings. This property - * is used instead of the --no-deprecation command line flag. + * stderr. Setting this to true will silence deprecation warnings. This property is + * used instead of the --no-deprecation command line flag. */ noDeprecation?: boolean; /** @@ -8170,21 +8888,21 @@ declare namespace NodeJS { resourcesPath?: string; /** * A Boolean that controls whether or not deprecation warnings will be thrown as - * exceptions. Setting this to true will throw errors for deprecations. This + * exceptions. Setting this to true will throw errors for deprecations. This * property is used instead of the --throw-deprecation command line flag. */ throwDeprecation?: boolean; /** * A Boolean that controls whether or not deprecations printed to stderr include - * their stack trace. Setting this to true will print stack traces for + * their stack trace. Setting this to true will print stack traces for * deprecations. This property is instead of the --trace-deprecation command line * flag. */ traceDeprecation?: boolean; /** * A Boolean that controls whether or not process warnings printed to stderr - * include their stack trace. Setting this to true will print stack traces for - * process warnings (including deprecations). This property is instead of the + * include their stack trace. Setting this to true will print stack traces for + * process warnings (including deprecations). This property is instead of the * --trace-warnings command line flag. */ traceProcessWarnings?: boolean; diff --git a/src/typings/iconv-lite.d.ts b/src/typings/iconv-lite.d.ts index 2b66468d456..0ed342db377 100644 --- a/src/typings/iconv-lite.d.ts +++ b/src/typings/iconv-lite.d.ts @@ -6,9 +6,9 @@ /// <reference path='./node.d.ts'/> declare module 'iconv-lite' { - export function decode(buffer: NodeBuffer, encoding: string): string; + export function decode(buffer: Buffer, encoding: string): string; - export function encode(content: string | NodeBuffer, encoding: string, options?: { addBOM?: boolean }): NodeBuffer; + export function encode(content: string | Buffer, encoding: string, options?: { addBOM?: boolean }): Buffer; export function encodingExists(encoding: string): boolean; diff --git a/src/typings/jschardet.d.ts b/src/typings/jschardet.d.ts index 9c553b129eb..f252a47fd09 100644 --- a/src/typings/jschardet.d.ts +++ b/src/typings/jschardet.d.ts @@ -3,7 +3,7 @@ declare module 'jschardet' { encoding: string, confidence: number } - export function detect(buffer: NodeBuffer): IDetectedMap; + export function detect(buffer: Buffer): IDetectedMap; export const Constants: { MINIMUM_THRESHOLD: number, diff --git a/src/typings/node.d.ts b/src/typings/node.d.ts index 1b6661edd71..c597abdda90 100644 --- a/src/typings/node.d.ts +++ b/src/typings/node.d.ts @@ -1,41 +1,67 @@ -// Type definitions for Node.js v7.x +// Type definitions for Node.js 8.9.x // Project: http://nodejs.org/ // Definitions by: Microsoft TypeScript <http://typescriptlang.org> // DefinitelyTyped <https://github.com/DefinitelyTyped/DefinitelyTyped> // Parambir Singh <https://github.com/parambirs> -// Roberto Desideri <https://github.com/RobDesideri> // Christian Vaagland Tellnes <https://github.com/tellnes> // Wilco Bakker <https://github.com/WilcoBakker> -// Daniel Imms <https://github.com/Tyriar> +// Nicolas Voigt <https://github.com/octo-sniffle> +// Chigozirim C. <https://github.com/smac89> +// Flarna <https://github.com/Flarna> +// Mariusz Wiktorczyk <https://github.com/mwiktorczyk> +// wwwy3y3 <https://github.com/wwwy3y3> +// Deividas Bakanas <https://github.com/DeividasBakanas> +// Kelvin Jin <https://github.com/kjin> +// Alvis HT Tang <https://github.com/alvis> +// Sebastian Silbermann <https://github.com/eps1lon> +// Hannes Magnusson <https://github.com/Hannes-Magnusson-CK> +// Alberto Schiabel <https://github.com/jkomyno> +// Huw <https://github.com/hoo29> +// Nicolas Even <https://github.com/n-e> +// Bruno Scheufler <https://github.com/brunoscheufler> +// Hoàng Văn Khải <https://github.com/KSXGitHub> +// Lishude <https://github.com/islishude> +// Andrew Makarov <https://github.com/r3nya> // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.1 -/************************************************ -* * -* Node.js v7.x API * -* * -************************************************/ +// ### BEGIN VSCODE MODIFICATION ### +// /** inspector module types */ +// /// <reference path="./inspector.d.ts" /> +// ### BEGIN VSCODE MODIFICATION ### // This needs to be global to avoid TS2403 in case lib.dom.d.ts is present in the same build interface Console { - Console: NodeJS.ConsoleConstructor; - assert(value: any, message?: string, ...optionalParams: any[]): void; - dir(obj: any, options?: NodeJS.InspectOptions): void; - error(message?: any, ...optionalParams: any[]): void; - info(message?: any, ...optionalParams: any[]): void; - log(message?: any, ...optionalParams: any[]): void; - time(label: string): void; - timeEnd(label: string): void; - trace(message?: any, ...optionalParams: any[]): void; - warn(message?: any, ...optionalParams: any[]): void; + Console: NodeJS.ConsoleConstructor; + assert(value: any, message?: string, ...optionalParams: any[]): void; + dir(obj: any, options?: NodeJS.InspectOptions): void; + debug(message?: any, ...optionalParams: any[]): void; + error(message?: any, ...optionalParams: any[]): void; + info(message?: any, ...optionalParams: any[]): void; + log(message?: any, ...optionalParams: any[]): void; + time(label: string): void; + timeEnd(label: string): void; + trace(message?: any, ...optionalParams: any[]): void; + warn(message?: any, ...optionalParams: any[]): void; } interface Error { - stack?: string; + stack?: string; } +// Declare "static" methods in Error interface ErrorConstructor { - captureStackTrace(targetObject: Object, constructorOpt?: Function): void; - stackTraceLimit: number; + /** Create .stack property on a target object */ + captureStackTrace(targetObject: Object, constructorOpt?: Function): void; + + /** + * Optional override for formatting stack traces + * + * @see https://github.com/v8/v8/wiki/Stack%20Trace%20API#customizing-stack-traces + */ + prepareStackTrace?: (err: Error, stackTraces: NodeJS.CallSite[]) => any; + + stackTraceLimit: number; } // compat for TypeScript 1.8 @@ -49,55 +75,90 @@ interface WeakSetConstructor { } // Forward-declare needed types from lib.es2015.d.ts (in case users are using `--lib es5`) interface Iterable<T> { } interface Iterator<T> { - next(value?: any): IteratorResult<T>; + next(value?: any): IteratorResult<T>; } interface IteratorResult<T> { } interface SymbolConstructor { - readonly iterator: symbol; + readonly iterator: symbol; } declare var Symbol: SymbolConstructor; +// Node.js ESNEXT support +interface String { + /** Removes whitespace from the left end of a string. */ + trimLeft(): string; + /** Removes whitespace from the right end of a string. */ + trimRight(): string; +} + /************************************************ * * * GLOBAL * * * ************************************************/ declare var process: NodeJS.Process; -declare var global: any; +declare var global: NodeJS.Global; declare var console: Console; -// Don't use these!! :) +// ### BEGIN VSCODE MODIFICATION ### // declare var __filename: string; // declare var __dirname: string; // declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; +// declare namespace setTimeout { +// export function __promisify__(ms: number): Promise<void>; +// export function __promisify__<T>(ms: number, value: T): Promise<T>; +// } // declare function clearTimeout(timeoutId: NodeJS.Timer): void; // declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; // declare function clearInterval(intervalId: NodeJS.Timer): void; +// ### END VSCODE MODIFICATION ### + declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; +declare namespace setImmediate { + export function __promisify__(): Promise<void>; + export function __promisify__<T>(value: T): Promise<T>; +} declare function clearImmediate(immediateId: any): void; +// TODO: change to `type NodeRequireFunction = (id: string) => any;` in next mayor version. interface NodeRequireFunction { - (id: string): any; + /* tslint:disable-next-line:callable-types */ + (id: string): any; } +// ### BEGIN VSCODE MODIFICATION ### // interface NodeRequire extends NodeRequireFunction { -// resolve(id: string): string; +// resolve: RequireResolve; // cache: any; -// extensions: any; +// extensions: NodeExtensions; // main: NodeModule | undefined; // } +// interface RequireResolve { +// (id: string, options?: { paths?: string[]; }): string; +// paths(request: string): string[] | null; +// } + +// interface NodeExtensions { +// '.js': (m: NodeModule, filename: string) => any; +// '.json': (m: NodeModule, filename: string) => any; +// '.node': (m: NodeModule, filename: string) => any; +// [ext: string]: (m: NodeModule, filename: string) => any; +// } + // declare var require: NodeRequire; +// ### END VSCODE MODIFICATION ### interface NodeModule { - exports: any; - require: NodeRequireFunction; - id: string; - filename: string; - loaded: boolean; - parent: NodeModule | null; - children: NodeModule[]; + exports: any; + require: NodeRequireFunction; + id: string; + filename: string; + loaded: boolean; + parent: NodeModule | null; + children: NodeModule[]; + paths: string[]; } declare var module: NodeModule; @@ -105,17 +166,16 @@ declare var module: NodeModule; // Same as module.exports declare var exports: any; declare var SlowBuffer: { - new(str: string, encoding?: string): Buffer; - new(size: number): Buffer; - new(size: Uint8Array): Buffer; - new(array: any[]): Buffer; - prototype: Buffer; - isBuffer(obj: any): boolean; - byteLength(string: string, encoding?: string): number; - concat(list: Buffer[], totalLength?: number): Buffer; + new(str: string, encoding?: string): Buffer; + new(size: number): Buffer; + new(size: Uint8Array): Buffer; + new(array: any[]): Buffer; + prototype: Buffer; + isBuffer(obj: any): boolean; + byteLength(string: string, encoding?: string): number; + concat(list: Buffer[], totalLength?: number): Buffer; }; - // Buffer class type BufferEncoding = "ascii" | "utf8" | "utf16le" | "ucs2" | "base64" | "latin1" | "binary" | "hex"; interface Buffer extends NodeBuffer { } @@ -132,19 +192,19 @@ declare var Buffer: { * @param str String to store in buffer. * @param encoding encoding to use, optional. Default is 'utf8' */ - new(str: string, encoding?: string): Buffer; + // new(str: string, encoding?: string): Buffer; ### VSCODE CHANGE (new Buffer() is deprecated) /** * Allocates a new buffer of {size} octets. * * @param size count of octets to allocate. */ - new(size: number): Buffer; + // new(size: number): Buffer; ### VSCODE CHANGE (new Buffer() is deprecated) /** * Allocates a new buffer containing the given {array} of octets. * * @param array The octets to store. */ - new(array: Uint8Array): Buffer; + // new(array: Uint8Array): Buffer; ### VSCODE CHANGE (new Buffer() is deprecated) /** * Produces a Buffer backed by the same allocated memory as * the given {ArrayBuffer}. @@ -152,26 +212,20 @@ declare var Buffer: { * * @param arrayBuffer The ArrayBuffer with which to share memory. */ - new(arrayBuffer: ArrayBuffer): Buffer; + // new(arrayBuffer: ArrayBuffer): Buffer; ### VSCODE CHANGE (new Buffer() is deprecated) /** * Allocates a new buffer containing the given {array} of octets. * * @param array The octets to store. */ - new(array: any[]): Buffer; + // new(array: any[]): Buffer; ### VSCODE CHANGE (new Buffer() is deprecated) /** * Copies the passed {buffer} data onto a new {Buffer} instance. * * @param buffer The buffer to copy. */ - new(buffer: Buffer): Buffer; - prototype: Buffer; - /** - * Allocates a new Buffer using an {array} of octets. - * - * @param array - */ - from(array: any[]): Buffer; + // new(buffer: Buffer): Buffer; ### VSCODE CHANGE (new Buffer() is deprecated) + prototype: Buffer; /** * When passed a reference to the .buffer property of a TypedArray instance, * the newly created Buffer will share the same allocated memory as the TypedArray. @@ -179,45 +233,40 @@ declare var Buffer: { * within the {arrayBuffer} that will be shared by the Buffer. * * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer() - * @param byteOffset - * @param length */ - from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer; + from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer; /** - * Copies the passed {buffer} data onto a new Buffer instance. - * - * @param buffer + * Creates a new Buffer using the passed {data} + * @param data data to create a new Buffer */ - from(buffer: Buffer): Buffer; + from(data: any[] | string | Buffer | ArrayBuffer /*| TypedArray*/): Buffer; /** * Creates a new Buffer containing the given JavaScript string {str}. * If provided, the {encoding} parameter identifies the character encoding. * If not provided, {encoding} defaults to 'utf8'. - * - * @param str */ - from(str: string, encoding?: string): Buffer; + from(str: string, encoding?: string): Buffer; /** * Returns true if {obj} is a Buffer * * @param obj object to test. */ - isBuffer(obj: any): obj is Buffer; + isBuffer(obj: any): obj is Buffer; /** * Returns true if {encoding} is a valid encoding argument. * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' * * @param encoding string to test. */ - isEncoding(encoding: string): boolean; + isEncoding(encoding: string): boolean; /** * Gives the actual byte length of a string. encoding defaults to 'utf8'. * This is not the same as String.prototype.length since that returns the number of characters in a string. * - * @param string string to test. + * @param string string to test. (TypedArray is also allowed, but it is only available starting ES2017) * @param encoding encoding used to evaluate (defaults to 'utf8') */ - byteLength(string: string, encoding?: string): number; + byteLength(string: string | Buffer | DataView | ArrayBuffer, encoding?: string): number; /** * Returns a buffer which is the result of concatenating all the buffers in the list together. * @@ -229,11 +278,11 @@ declare var Buffer: { * @param totalLength Total length of the buffers when concatenated. * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly. */ - concat(list: Buffer[], totalLength?: number): Buffer; + concat(list: Buffer[], totalLength?: number): Buffer; /** * The same as buf1.compare(buf2). */ - compare(buf1: Buffer, buf2: Buffer): number; + compare(buf1: Buffer, buf2: Buffer): number; /** * Allocates a new buffer of {size} octets. * @@ -242,21 +291,25 @@ declare var Buffer: { * If parameter is omitted, buffer will be filled with zeros. * @param encoding encoding used for call to buf.fill while initalizing */ - alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer; + alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer; /** * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents * of the newly created Buffer are unknown and may contain sensitive data. * * @param size count of octets to allocate */ - allocUnsafe(size: number): Buffer; + allocUnsafe(size: number): Buffer; /** * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents * of the newly created Buffer are unknown and may contain sensitive data. * * @param size count of octets to allocate */ - allocUnsafeSlow(size: number): Buffer; + allocUnsafeSlow(size: number): Buffer; + /** + * This is the number of bytes used to determine the size of pre-allocated, internal Buffer instances used for pooling. This value may be modified. + */ + poolSize: number; }; /************************************************ @@ -265,273 +318,506 @@ declare var Buffer: { * * ************************************************/ declare namespace NodeJS { - export interface InspectOptions { - showHidden?: boolean; - depth?: number | null; - colors?: boolean; - customInspect?: boolean; - showProxy?: boolean; - maxArrayLength?: number | null; - breakLength?: number; - } + export interface InspectOptions { + showHidden?: boolean; + depth?: number | null; + colors?: boolean; + customInspect?: boolean; + showProxy?: boolean; + maxArrayLength?: number | null; + breakLength?: number; + } - export interface ConsoleConstructor { - prototype: Console; - new(stdout: WritableStream, stderr?: WritableStream): Console; - } + export interface ConsoleConstructor { + prototype: Console; + new(stdout: WritableStream, stderr?: WritableStream): Console; + } - export interface ErrnoException extends Error { - errno?: number; - code?: string; - path?: string; - syscall?: string; - stack?: string; - } + export interface CallSite { + /** + * Value of "this" + */ + getThis(): any; - export class EventEmitter { - addListener(event: string | symbol, listener: Function): this; - on(event: string | symbol, listener: Function): this; - once(event: string | symbol, listener: Function): this; - removeListener(event: string | symbol, listener: Function): this; - removeAllListeners(event?: string | symbol): this; - setMaxListeners(n: number): this; - getMaxListeners(): number; - listeners(event: string | symbol): Function[]; - emit(event: string | symbol, ...args: any[]): boolean; - listenerCount(type: string | symbol): number; - // Added in Node 6... - prependListener(event: string | symbol, listener: Function): this; - prependOnceListener(event: string | symbol, listener: Function): this; - eventNames(): (string | symbol)[]; - } + /** + * Type of "this" as a string. + * This is the name of the function stored in the constructor field of + * "this", if available. Otherwise the object's [[Class]] internal + * property. + */ + getTypeName(): string | null; - export interface ReadableStream extends EventEmitter { - readable: boolean; - read(size?: number): string | Buffer; - setEncoding(encoding: string | null): this; - pause(): this; - resume(): this; - isPaused(): boolean; - pipe<T extends WritableStream>(destination: T, options?: { end?: boolean; }): T; - unpipe<T extends WritableStream>(destination?: T): this; - unshift(chunk: string): void; - unshift(chunk: Buffer): void; - wrap(oldStream: ReadableStream): ReadableStream; - } + /** + * Current function + */ + getFunction(): Function | undefined; - export interface WritableStream extends EventEmitter { - writable: boolean; - write(buffer: Buffer | string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - } + /** + * Name of the current function, typically its name property. + * If a name property is not available an attempt will be made to try + * to infer a name from the function's context. + */ + getFunctionName(): string | null; - export interface ReadWriteStream extends ReadableStream, WritableStream { } + /** + * Name of the property [of "this" or one of its prototypes] that holds + * the current function + */ + getMethodName(): string | null; - export interface Events extends EventEmitter { } + /** + * Name of the script [if this function was defined in a script] + */ + getFileName(): string | null; - export interface Domain extends Events { - run(fn: Function): void; - add(emitter: Events): void; - remove(emitter: Events): void; - bind(cb: (err: Error, data: any) => any): any; - intercept(cb: (data: any) => any): any; - dispose(): void; + /** + * Current line number [if this function was defined in a script] + */ + getLineNumber(): number | null; - addListener(event: string, listener: Function): this; - on(event: string, listener: Function): this; - once(event: string, listener: Function): this; - removeListener(event: string, listener: Function): this; - removeAllListeners(event?: string): this; - } + /** + * Current column number [if this function was defined in a script] + */ + getColumnNumber(): number | null; - export interface MemoryUsage { - rss: number; - heapTotal: number; - heapUsed: number; - } + /** + * A call site object representing the location where eval was called + * [if this function was created using a call to eval] + */ + getEvalOrigin(): string | undefined; - export interface CpuUsage { - user: number; - system: number; - } + /** + * Is this a toplevel invocation, that is, is "this" the global object? + */ + isToplevel(): boolean; - export interface ProcessVersions { - http_parser: string; - node: string; - v8: string; - ares: string; - uv: string; - zlib: string; - modules: string; - openssl: string; - } + /** + * Does this call take place in code defined by a call to eval? + */ + isEval(): boolean; - type Platform = 'aix' - | 'android' - | 'darwin' - | 'freebsd' - | 'linux' - | 'openbsd' - | 'sunos' - | 'win32'; + /** + * Is this call in native V8 code? + */ + isNative(): boolean; - export interface Socket extends ReadWriteStream { - isTTY?: true; - } + /** + * Is this a constructor call? + */ + isConstructor(): boolean; + } - export interface WriteStream extends Socket { - columns?: number; - rows?: number; - } - export interface ReadStream extends Socket { - isRaw?: boolean; - setRawMode?(mode: boolean): void; - } + export interface ErrnoException extends Error { + errno?: number; + code?: string; + path?: string; + syscall?: string; + stack?: string; + } - export interface Process extends EventEmitter { - stdout: WriteStream; - stderr: WriteStream; - stdin: ReadStream; - openStdin(): Socket; - argv: string[]; - argv0: string; - execArgv: string[]; - execPath: string; - abort(): void; - chdir(directory: string): void; - cwd(): string; - emitWarning(warning: string | Error, name?: string, ctor?: Function): void; - env: any; - exit(code?: number): void; - exitCode: number; - getgid(): number; - setgid(id: number): void; - setgid(id: string): void; - getuid(): number; - setuid(id: number): void; - setuid(id: string): void; - version: string; - versions: ProcessVersions; - config: { - target_defaults: { - cflags: any[]; - default_configuration: string; - defines: string[]; - include_dirs: string[]; - libraries: string[]; - }; - variables: { - clang: number; - host_arch: string; - node_install_npm: boolean; - node_install_waf: boolean; - node_prefix: string; - node_shared_openssl: boolean; - node_shared_v8: boolean; - node_shared_zlib: boolean; - node_use_dtrace: boolean; - node_use_etw: boolean; - node_use_openssl: boolean; - target_arch: string; - v8_no_strict_aliasing: number; - v8_use_snapshot: boolean; - visibility: string; - }; - }; - kill(pid: number, signal?: string | number): void; - pid: number; - title: string; - arch: string; - platform: Platform; - mainModule?: NodeModule; - memoryUsage(): MemoryUsage; - cpuUsage(previousValue?: CpuUsage): CpuUsage; - nextTick(callback: Function, ...args: any[]): void; - umask(mask?: number): number; - uptime(): number; - hrtime(time?: [number, number]): [number, number]; - domain: Domain; + export class EventEmitter { + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + removeListener(event: string | symbol, listener: (...args: any[]) => void): this; + removeAllListeners(event?: string | symbol): this; + setMaxListeners(n: number): this; + getMaxListeners(): number; + listeners(event: string | symbol): Function[]; + emit(event: string | symbol, ...args: any[]): boolean; + listenerCount(type: string | symbol): number; + // Added in Node 6... + prependListener(event: string | symbol, listener: (...args: any[]) => void): this; + prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; + eventNames(): Array<string | symbol>; + } - // Worker - send?(message: any, sendHandle?: any): void; - disconnect(): void; - connected: boolean; - } + export interface ReadableStream extends EventEmitter { + readable: boolean; + read(size?: number): string | Buffer; + setEncoding(encoding: string): this; + pause(): this; + resume(): this; + isPaused(): boolean; + pipe<T extends WritableStream>(destination: T, options?: { end?: boolean; }): T; + unpipe<T extends WritableStream>(destination?: T): this; + unshift(chunk: string): void; + unshift(chunk: Buffer): void; + wrap(oldStream: ReadableStream): this; + } - export interface Global { - Array: typeof Array; - ArrayBuffer: typeof ArrayBuffer; - Boolean: typeof Boolean; - Buffer: typeof Buffer; - DataView: typeof DataView; - Date: typeof Date; - Error: typeof Error; - EvalError: typeof EvalError; - Float32Array: typeof Float32Array; - Float64Array: typeof Float64Array; - Function: typeof Function; - GLOBAL: Global; - Infinity: typeof Infinity; - Int16Array: typeof Int16Array; - Int32Array: typeof Int32Array; - Int8Array: typeof Int8Array; - Intl: typeof Intl; - JSON: typeof JSON; - Map: MapConstructor; - Math: typeof Math; - NaN: typeof NaN; - Number: typeof Number; - Object: typeof Object; - Promise: Function; - RangeError: typeof RangeError; - ReferenceError: typeof ReferenceError; - RegExp: typeof RegExp; - Set: SetConstructor; - String: typeof String; - Symbol: Function; - SyntaxError: typeof SyntaxError; - TypeError: typeof TypeError; - URIError: typeof URIError; - Uint16Array: typeof Uint16Array; - Uint32Array: typeof Uint32Array; - Uint8Array: typeof Uint8Array; - Uint8ClampedArray: Function; - WeakMap: WeakMapConstructor; - WeakSet: WeakSetConstructor; - clearImmediate: (immediateId: any) => void; - clearInterval: (intervalId: NodeJS.Timer) => void; - clearTimeout: (timeoutId: NodeJS.Timer) => void; - console: typeof console; - decodeURI: typeof decodeURI; - decodeURIComponent: typeof decodeURIComponent; - encodeURI: typeof encodeURI; - encodeURIComponent: typeof encodeURIComponent; - escape: (str: string) => string; - eval: typeof eval; - global: Global; - isFinite: typeof isFinite; - isNaN: typeof isNaN; - parseFloat: typeof parseFloat; - parseInt: typeof parseInt; - process: Process; - root: Global; - setImmediate: (callback: (...args: any[]) => void, ...args: any[]) => any; - setInterval: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; - setTimeout: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; - undefined: typeof undefined; - unescape: (str: string) => string; - gc: () => void; - v8debug?: any; - } + export interface WritableStream extends EventEmitter { + writable: boolean; + write(buffer: Buffer | string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + end(cb?: Function): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + } - export interface Timer { - ref(): void; - unref(): void; - } + export interface ReadWriteStream extends ReadableStream, WritableStream { } + + export interface Events extends EventEmitter { } + + export interface Domain extends Events { + run(fn: Function): void; + add(emitter: Events): void; + remove(emitter: Events): void; + bind(cb: (err: Error, data: any) => any): any; + intercept(cb: (data: any) => any): any; + dispose(): void; + + addListener(event: string, listener: (...args: any[]) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + removeListener(event: string, listener: (...args: any[]) => void): this; + removeAllListeners(event?: string): this; + } + + export interface MemoryUsage { + rss: number; + heapTotal: number; + heapUsed: number; + external: number; + } + + export interface CpuUsage { + user: number; + system: number; + } + + export interface ProcessVersions { + http_parser: string; + node: string; + v8: string; + ares: string; + uv: string; + zlib: string; + modules: string; + openssl: string; + } + + type Platform = 'aix' + | 'android' + | 'darwin' + | 'freebsd' + | 'linux' + | 'openbsd' + | 'sunos' + | 'win32' + | 'cygwin'; + + type Signals = + "SIGABRT" | "SIGALRM" | "SIGBUS" | "SIGCHLD" | "SIGCONT" | "SIGFPE" | "SIGHUP" | "SIGILL" | "SIGINT" | "SIGIO" | + "SIGIOT" | "SIGKILL" | "SIGPIPE" | "SIGPOLL" | "SIGPROF" | "SIGPWR" | "SIGQUIT" | "SIGSEGV" | "SIGSTKFLT" | + "SIGSTOP" | "SIGSYS" | "SIGTERM" | "SIGTRAP" | "SIGTSTP" | "SIGTTIN" | "SIGTTOU" | "SIGUNUSED" | "SIGURG" | + "SIGUSR1" | "SIGUSR2" | "SIGVTALRM" | "SIGWINCH" | "SIGXCPU" | "SIGXFSZ" | "SIGBREAK" | "SIGLOST" | "SIGINFO"; + + type BeforeExitListener = (code: number) => void; + type DisconnectListener = () => void; + type ExitListener = (code: number) => void; + type RejectionHandledListener = (promise: Promise<any>) => void; + type UncaughtExceptionListener = (error: Error) => void; + type UnhandledRejectionListener = (reason: any, promise: Promise<any>) => void; + type WarningListener = (warning: Error) => void; + type MessageListener = (message: any, sendHandle: any) => void; + type SignalsListener = () => void; + type NewListenerListener = (type: string | symbol, listener: (...args: any[]) => void) => void; + type RemoveListenerListener = (type: string | symbol, listener: (...args: any[]) => void) => void; + + export interface Socket extends ReadWriteStream { + isTTY?: true; + } + + export interface ProcessEnv { + [key: string]: string | undefined; + } + + export interface WriteStream extends Socket { + readonly writableHighWaterMark: number; + columns?: number; + rows?: number; + _write(chunk: any, encoding: string, callback: Function): void; + _destroy(err: Error, callback: Function): void; + _final(callback: Function): void; + setDefaultEncoding(encoding: string): this; + cork(): void; + uncork(): void; + destroy(error?: Error): void; + } + export interface ReadStream extends Socket { + readonly readableHighWaterMark: number; + isRaw?: boolean; + setRawMode?(mode: boolean): void; + _read(size: number): void; + _destroy(err: Error, callback: Function): void; + push(chunk: any, encoding?: string): boolean; + destroy(error?: Error): void; + } + + export interface Process extends EventEmitter { + stdout: WriteStream; + stderr: WriteStream; + stdin: ReadStream; + openStdin(): Socket; + argv: string[]; + argv0: string; + execArgv: string[]; + execPath: string; + abort(): void; + chdir(directory: string): void; + cwd(): string; + debugPort: number; + emitWarning(warning: string | Error, name?: string, ctor?: Function): void; + env: ProcessEnv; + exit(code?: number): never; + exitCode: number; + getgid(): number; + setgid(id: number | string): void; + getuid(): number; + setuid(id: number | string): void; + geteuid(): number; + seteuid(id: number | string): void; + getegid(): number; + setegid(id: number | string): void; + getgroups(): number[]; + setgroups(groups: Array<string | number>): void; + version: string; + versions: ProcessVersions; + config: { + target_defaults: { + cflags: any[]; + default_configuration: string; + defines: string[]; + include_dirs: string[]; + libraries: string[]; + }; + variables: { + clang: number; + host_arch: string; + node_install_npm: boolean; + node_install_waf: boolean; + node_prefix: string; + node_shared_openssl: boolean; + node_shared_v8: boolean; + node_shared_zlib: boolean; + node_use_dtrace: boolean; + node_use_etw: boolean; + node_use_openssl: boolean; + target_arch: string; + v8_no_strict_aliasing: number; + v8_use_snapshot: boolean; + visibility: string; + }; + }; + kill(pid: number, signal?: string | number): void; + pid: number; + title: string; + arch: string; + platform: Platform; + mainModule?: NodeModule; + memoryUsage(): MemoryUsage; + cpuUsage(previousValue?: CpuUsage): CpuUsage; + nextTick(callback: Function, ...args: any[]): void; + umask(mask?: number): number; + uptime(): number; + hrtime(time?: [number, number]): [number, number]; + domain: Domain; + + // Worker + send?(message: any, sendHandle?: any): void; + disconnect(): void; + connected: boolean; + + /** + * EventEmitter + * 1. beforeExit + * 2. disconnect + * 3. exit + * 4. message + * 5. rejectionHandled + * 6. uncaughtException + * 7. unhandledRejection + * 8. warning + * 9. message + * 10. <All OS Signals> + * 11. newListener/removeListener inherited from EventEmitter + */ + addListener(event: "beforeExit", listener: BeforeExitListener): this; + addListener(event: "disconnect", listener: DisconnectListener): this; + addListener(event: "exit", listener: ExitListener): this; + addListener(event: "rejectionHandled", listener: RejectionHandledListener): this; + addListener(event: "uncaughtException", listener: UncaughtExceptionListener): this; + addListener(event: "unhandledRejection", listener: UnhandledRejectionListener): this; + addListener(event: "warning", listener: WarningListener): this; + addListener(event: "message", listener: MessageListener): this; + addListener(event: Signals, listener: SignalsListener): this; + addListener(event: "newListener", listener: NewListenerListener): this; + addListener(event: "removeListener", listener: RemoveListenerListener): this; + + emit(event: "beforeExit", code: number): boolean; + emit(event: "disconnect"): boolean; + emit(event: "exit", code: number): boolean; + emit(event: "rejectionHandled", promise: Promise<any>): boolean; + emit(event: "uncaughtException", error: Error): boolean; + emit(event: "unhandledRejection", reason: any, promise: Promise<any>): boolean; + emit(event: "warning", warning: Error): boolean; + emit(event: "message", message: any, sendHandle: any): this; + emit(event: Signals): boolean; + emit(event: "newListener", eventName: string | symbol, listener: (...args: any[]) => void): this; + emit(event: "removeListener", eventName: string, listener: (...args: any[]) => void): this; + + on(event: "beforeExit", listener: BeforeExitListener): this; + on(event: "disconnect", listener: DisconnectListener): this; + on(event: "exit", listener: ExitListener): this; + on(event: "rejectionHandled", listener: RejectionHandledListener): this; + on(event: "uncaughtException", listener: UncaughtExceptionListener): this; + on(event: "unhandledRejection", listener: UnhandledRejectionListener): this; + on(event: "warning", listener: WarningListener): this; + on(event: "message", listener: MessageListener): this; + on(event: Signals, listener: SignalsListener): this; + on(event: "newListener", listener: NewListenerListener): this; + on(event: "removeListener", listener: RemoveListenerListener): this; + + once(event: "beforeExit", listener: BeforeExitListener): this; + once(event: "disconnect", listener: DisconnectListener): this; + once(event: "exit", listener: ExitListener): this; + once(event: "rejectionHandled", listener: RejectionHandledListener): this; + once(event: "uncaughtException", listener: UncaughtExceptionListener): this; + once(event: "unhandledRejection", listener: UnhandledRejectionListener): this; + once(event: "warning", listener: WarningListener): this; + once(event: "message", listener: MessageListener): this; + once(event: Signals, listener: SignalsListener): this; + once(event: "newListener", listener: NewListenerListener): this; + once(event: "removeListener", listener: RemoveListenerListener): this; + + prependListener(event: "beforeExit", listener: BeforeExitListener): this; + prependListener(event: "disconnect", listener: DisconnectListener): this; + prependListener(event: "exit", listener: ExitListener): this; + prependListener(event: "rejectionHandled", listener: RejectionHandledListener): this; + prependListener(event: "uncaughtException", listener: UncaughtExceptionListener): this; + prependListener(event: "unhandledRejection", listener: UnhandledRejectionListener): this; + prependListener(event: "warning", listener: WarningListener): this; + prependListener(event: "message", listener: MessageListener): this; + prependListener(event: Signals, listener: SignalsListener): this; + prependListener(event: "newListener", listener: NewListenerListener): this; + prependListener(event: "removeListener", listener: RemoveListenerListener): this; + + prependOnceListener(event: "beforeExit", listener: BeforeExitListener): this; + prependOnceListener(event: "disconnect", listener: DisconnectListener): this; + prependOnceListener(event: "exit", listener: ExitListener): this; + prependOnceListener(event: "rejectionHandled", listener: RejectionHandledListener): this; + prependOnceListener(event: "uncaughtException", listener: UncaughtExceptionListener): this; + prependOnceListener(event: "unhandledRejection", listener: UnhandledRejectionListener): this; + prependOnceListener(event: "warning", listener: WarningListener): this; + prependOnceListener(event: "message", listener: MessageListener): this; + prependOnceListener(event: Signals, listener: SignalsListener): this; + prependOnceListener(event: "newListener", listener: NewListenerListener): this; + prependOnceListener(event: "removeListener", listener: RemoveListenerListener): this; + + listeners(event: "beforeExit"): BeforeExitListener[]; + listeners(event: "disconnect"): DisconnectListener[]; + listeners(event: "exit"): ExitListener[]; + listeners(event: "rejectionHandled"): RejectionHandledListener[]; + listeners(event: "uncaughtException"): UncaughtExceptionListener[]; + listeners(event: "unhandledRejection"): UnhandledRejectionListener[]; + listeners(event: "warning"): WarningListener[]; + listeners(event: "message"): MessageListener[]; + listeners(event: Signals): SignalsListener[]; + listeners(event: "newListener"): NewListenerListener[]; + listeners(event: "removeListener"): RemoveListenerListener[]; + } + + export interface Global { + Array: typeof Array; + ArrayBuffer: typeof ArrayBuffer; + Boolean: typeof Boolean; + Buffer: typeof Buffer; + DataView: typeof DataView; + Date: typeof Date; + Error: typeof Error; + EvalError: typeof EvalError; + Float32Array: typeof Float32Array; + Float64Array: typeof Float64Array; + Function: typeof Function; + GLOBAL: Global; + Infinity: typeof Infinity; + Int16Array: typeof Int16Array; + Int32Array: typeof Int32Array; + Int8Array: typeof Int8Array; + Intl: typeof Intl; + JSON: typeof JSON; + Map: MapConstructor; + Math: typeof Math; + NaN: typeof NaN; + Number: typeof Number; + Object: typeof Object; + Promise: Function; + RangeError: typeof RangeError; + ReferenceError: typeof ReferenceError; + RegExp: typeof RegExp; + Set: SetConstructor; + String: typeof String; + Symbol: Function; + SyntaxError: typeof SyntaxError; + TypeError: typeof TypeError; + URIError: typeof URIError; + Uint16Array: typeof Uint16Array; + Uint32Array: typeof Uint32Array; + Uint8Array: typeof Uint8Array; + Uint8ClampedArray: Function; + WeakMap: WeakMapConstructor; + WeakSet: WeakSetConstructor; + clearImmediate: (immediateId: any) => void; + clearInterval: (intervalId: NodeJS.Timer) => void; + clearTimeout: (timeoutId: NodeJS.Timer) => void; + console: typeof console; + decodeURI: typeof decodeURI; + decodeURIComponent: typeof decodeURIComponent; + encodeURI: typeof encodeURI; + encodeURIComponent: typeof encodeURIComponent; + escape: (str: string) => string; + eval: typeof eval; + global: Global; + isFinite: typeof isFinite; + isNaN: typeof isNaN; + parseFloat: typeof parseFloat; + parseInt: typeof parseInt; + process: Process; + root: Global; + setImmediate: (callback: (...args: any[]) => void, ...args: any[]) => any; + setInterval: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; + setTimeout: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; + undefined: typeof undefined; + unescape: (str: string) => string; + gc: () => void; + v8debug?: any; + } + + export interface Timer { + ref(): void; + unref(): void; + } + + class Module { + static runMain(): void; + static wrap(code: string): string; + static builtinModules: string[]; + + static Module: typeof Module; + + exports: any; + require: NodeRequireFunction; + id: string; + filename: string; + loaded: boolean; + parent: Module | null; + children: Module[]; + paths: string[]; + + constructor(id: string, parent?: Module); + } } interface IterableIterator<T> { } @@ -540,59 +826,59 @@ interface IterableIterator<T> { } * @deprecated */ interface NodeBuffer extends Uint8Array { - write(string: string, offset?: number, length?: number, encoding?: string): number; - toString(encoding?: string, start?: number, end?: number): string; - toJSON(): { type: 'Buffer', data: any[] }; - equals(otherBuffer: Buffer): boolean; - compare(otherBuffer: Buffer, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number; - copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; - slice(start?: number, end?: number): Buffer; - writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; - readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number; - readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number; - readIntLE(offset: number, byteLength: number, noAssert?: boolean): number; - readIntBE(offset: number, byteLength: number, noAssert?: boolean): number; - readUInt8(offset: number, noAssert?: boolean): number; - readUInt16LE(offset: number, noAssert?: boolean): number; - readUInt16BE(offset: number, noAssert?: boolean): number; - readUInt32LE(offset: number, noAssert?: boolean): number; - readUInt32BE(offset: number, noAssert?: boolean): number; - readInt8(offset: number, noAssert?: boolean): number; - readInt16LE(offset: number, noAssert?: boolean): number; - readInt16BE(offset: number, noAssert?: boolean): number; - readInt32LE(offset: number, noAssert?: boolean): number; - readInt32BE(offset: number, noAssert?: boolean): number; - readFloatLE(offset: number, noAssert?: boolean): number; - readFloatBE(offset: number, noAssert?: boolean): number; - readDoubleLE(offset: number, noAssert?: boolean): number; - readDoubleBE(offset: number, noAssert?: boolean): number; - swap16(): Buffer; - swap32(): Buffer; - swap64(): Buffer; - writeUInt8(value: number, offset: number, noAssert?: boolean): number; - writeUInt16LE(value: number, offset: number, noAssert?: boolean): number; - writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; - writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; - writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; - writeInt8(value: number, offset: number, noAssert?: boolean): number; - writeInt16LE(value: number, offset: number, noAssert?: boolean): number; - writeInt16BE(value: number, offset: number, noAssert?: boolean): number; - writeInt32LE(value: number, offset: number, noAssert?: boolean): number; - writeInt32BE(value: number, offset: number, noAssert?: boolean): number; - writeFloatLE(value: number, offset: number, noAssert?: boolean): number; - writeFloatBE(value: number, offset: number, noAssert?: boolean): number; - writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; - writeDoubleBE(value: number, offset: number, noAssert?: boolean): number; - fill(value: any, offset?: number, end?: number): this; - indexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; - lastIndexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; - entries(): IterableIterator<[number, number]>; - includes(value: string | number | Buffer, byteOffset?: number, encoding?: string): boolean; - keys(): IterableIterator<number>; - values(): IterableIterator<number>; + write(string: string, offset?: number, length?: number, encoding?: string): number; + toString(encoding?: string, start?: number, end?: number): string; + toJSON(): { type: 'Buffer', data: any[] }; + equals(otherBuffer: Buffer): boolean; + compare(otherBuffer: Buffer, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number; + copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; + slice(start?: number, end?: number): Buffer; + writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readUInt8(offset: number, noAssert?: boolean): number; + readUInt16LE(offset: number, noAssert?: boolean): number; + readUInt16BE(offset: number, noAssert?: boolean): number; + readUInt32LE(offset: number, noAssert?: boolean): number; + readUInt32BE(offset: number, noAssert?: boolean): number; + readInt8(offset: number, noAssert?: boolean): number; + readInt16LE(offset: number, noAssert?: boolean): number; + readInt16BE(offset: number, noAssert?: boolean): number; + readInt32LE(offset: number, noAssert?: boolean): number; + readInt32BE(offset: number, noAssert?: boolean): number; + readFloatLE(offset: number, noAssert?: boolean): number; + readFloatBE(offset: number, noAssert?: boolean): number; + readDoubleLE(offset: number, noAssert?: boolean): number; + readDoubleBE(offset: number, noAssert?: boolean): number; + swap16(): Buffer; + swap32(): Buffer; + swap64(): Buffer; + writeUInt8(value: number, offset: number, noAssert?: boolean): number; + writeUInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeInt8(value: number, offset: number, noAssert?: boolean): number; + writeInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeFloatLE(value: number, offset: number, noAssert?: boolean): number; + writeFloatBE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleBE(value: number, offset: number, noAssert?: boolean): number; + fill(value: any, offset?: number, end?: number): this; + indexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; + lastIndexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; + entries(): IterableIterator<[number, number]>; + includes(value: string | number | Buffer, byteOffset?: number, encoding?: string): boolean; + keys(): IterableIterator<number>; + values(): IterableIterator<number>; } /************************************************ @@ -601,205 +887,271 @@ interface NodeBuffer extends Uint8Array { * * ************************************************/ declare module "buffer" { - export var INSPECT_MAX_BYTES: number; - var BuffType: typeof Buffer; - var SlowBuffType: typeof SlowBuffer; - export { BuffType as Buffer, SlowBuffType as SlowBuffer }; + export var INSPECT_MAX_BYTES: number; + var BuffType: typeof Buffer; + var SlowBuffType: typeof SlowBuffer; + export { BuffType as Buffer, SlowBuffType as SlowBuffer }; } declare module "querystring" { - export interface StringifyOptions { - encodeURIComponent?: Function; - } + export interface StringifyOptions { + encodeURIComponent?: Function; + } - export interface ParseOptions { - maxKeys?: number; - decodeURIComponent?: Function; - } + export interface ParseOptions { + maxKeys?: number; + decodeURIComponent?: Function; + } - export function stringify<T>(obj: T, sep?: string, eq?: string, options?: StringifyOptions): string; - export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): any; - export function parse<T extends {}>(str: string, sep?: string, eq?: string, options?: ParseOptions): T; - export function escape(str: string): string; - export function unescape(str: string): string; + interface ParsedUrlQuery { [key: string]: string | string[] | undefined; } + + export function stringify<T>(obj: T, sep?: string, eq?: string, options?: StringifyOptions): string; + export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): ParsedUrlQuery; + export function parse<T extends {}>(str: string, sep?: string, eq?: string, options?: ParseOptions): T; + export function escape(str: string): string; + export function unescape(str: string): string; } declare module "events" { - class internal extends NodeJS.EventEmitter { } + class internal extends NodeJS.EventEmitter { } - namespace internal { - export class EventEmitter extends internal { - static listenerCount(emitter: EventEmitter, event: string | symbol): number; // deprecated - static defaultMaxListeners: number; + namespace internal { + export class EventEmitter extends internal { + static listenerCount(emitter: EventEmitter, event: string | symbol): number; // deprecated + static defaultMaxListeners: number; - addListener(event: string | symbol, listener: Function): this; - on(event: string | symbol, listener: Function): this; - once(event: string | symbol, listener: Function): this; - prependListener(event: string | symbol, listener: Function): this; - prependOnceListener(event: string | symbol, listener: Function): this; - removeListener(event: string | symbol, listener: Function): this; - removeAllListeners(event?: string | symbol): this; - setMaxListeners(n: number): this; - getMaxListeners(): number; - listeners(event: string | symbol): Function[]; - emit(event: string | symbol, ...args: any[]): boolean; - eventNames(): (string | symbol)[]; - listenerCount(type: string | symbol): number; - } - } + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + prependListener(event: string | symbol, listener: (...args: any[]) => void): this; + prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; + removeListener(event: string | symbol, listener: (...args: any[]) => void): this; + removeAllListeners(event?: string | symbol): this; + setMaxListeners(n: number): this; + getMaxListeners(): number; + listeners(event: string | symbol): Function[]; + emit(event: string | symbol, ...args: any[]): boolean; + eventNames(): Array<string | symbol>; + listenerCount(type: string | symbol): number; + } + } - export = internal; + export = internal; } declare module "http" { - import * as events from "events"; - import * as net from "net"; - import * as stream from "stream"; + import * as events from "events"; + import * as net from "net"; + import * as stream from "stream"; + import { URL } from "url"; - export interface RequestOptions { - protocol?: string; - host?: string; - hostname?: string; - family?: number; - port?: number; - localAddress?: string; - socketPath?: string; - method?: string; - path?: string; - headers?: { [key: string]: any }; - auth?: string; - agent?: Agent | boolean; - timeout?: number; - } + // incoming headers will never contain number + export interface IncomingHttpHeaders { + 'accept'?: string; + 'access-control-allow-origin'?: string; + 'access-control-allow-credentials'?: string; + 'access-control-expose-headers'?: string; + 'access-control-max-age'?: string; + 'access-control-allow-methods'?: string; + 'access-control-allow-headers'?: string; + 'accept-patch'?: string; + 'accept-ranges'?: string; + 'authorization'?: string; + 'age'?: string; + 'allow'?: string; + 'alt-svc'?: string; + 'cache-control'?: string; + 'connection'?: string; + 'content-disposition'?: string; + 'content-encoding'?: string; + 'content-language'?: string; + 'content-length'?: string; + 'content-location'?: string; + 'content-range'?: string; + 'content-type'?: string; + 'date'?: string; + 'expires'?: string; + 'host'?: string; + 'last-modified'?: string; + 'location'?: string; + 'pragma'?: string; + 'proxy-authenticate'?: string; + 'public-key-pins'?: string; + 'retry-after'?: string; + 'set-cookie'?: string[]; + 'strict-transport-security'?: string; + 'trailer'?: string; + 'transfer-encoding'?: string; + 'tk'?: string; + 'upgrade'?: string; + 'vary'?: string; + 'via'?: string; + 'warning'?: string; + 'www-authenticate'?: string; + [header: string]: string | string[] | undefined; + } - export interface Server extends net.Server { - setTimeout(msecs: number, callback: Function): void; - maxHeadersCount: number; - timeout: number; - listening: boolean; - } + // outgoing headers allows numbers (as they are converted internally to strings) + export interface OutgoingHttpHeaders { + [header: string]: number | string | string[] | undefined; + } + + export interface ClientRequestArgs { + protocol?: string; + host?: string; + hostname?: string; + family?: number; + port?: number | string; + defaultPort?: number | string; + localAddress?: string; + socketPath?: string; + method?: string; + path?: string; + headers?: OutgoingHttpHeaders; + auth?: string; + agent?: Agent | boolean; + _defaultAgent?: Agent; + timeout?: number; + // https://github.com/nodejs/node/blob/master/lib/_http_client.js#L278 + createConnection?: (options: ClientRequestArgs, oncreate: (err: Error, socket: net.Socket) => void) => net.Socket; + } + + export class Server extends net.Server { + constructor(requestListener?: (req: IncomingMessage, res: ServerResponse) => void); + + setTimeout(msecs?: number, callback?: () => void): this; + setTimeout(callback: () => void): this; + maxHeadersCount: number; + timeout: number; + keepAliveTimeout: number; + } /** * @deprecated Use IncomingMessage */ - export interface ServerRequest extends IncomingMessage { - connection: net.Socket; - } - export interface ServerResponse extends stream.Writable { - // Extended base methods - write(buffer: Buffer): boolean; - write(buffer: Buffer, cb?: Function): boolean; - write(str: string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - write(str: string, encoding?: string, fd?: string): boolean; + export class ServerRequest extends IncomingMessage { + connection: net.Socket; + } - writeContinue(): void; - writeHead(statusCode: number, reasonPhrase?: string, headers?: any): void; - writeHead(statusCode: number, headers?: any): void; - statusCode: number; - statusMessage: string; - headersSent: boolean; - setHeader(name: string, value: string | string[]): void; - setTimeout(msecs: number, callback: Function): ServerResponse; - sendDate: boolean; - getHeader(name: string): string; - removeHeader(name: string): void; - write(chunk: any, encoding?: string): any; - addTrailers(headers: any): void; - finished: boolean; + // https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js + export class OutgoingMessage extends stream.Writable { + upgrading: boolean; + chunkedEncoding: boolean; + shouldKeepAlive: boolean; + useChunkedEncodingByDefault: boolean; + sendDate: boolean; + finished: boolean; + headersSent: boolean; + connection: net.Socket; - // Extended base methods - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - end(data?: any, encoding?: string): void; - } - export interface ClientRequest extends stream.Writable { - // Extended base methods - write(buffer: Buffer): boolean; - write(buffer: Buffer, cb?: Function): boolean; - write(str: string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - write(str: string, encoding?: string, fd?: string): boolean; + constructor(); - write(chunk: any, encoding?: string): void; - abort(): void; - setTimeout(timeout: number, callback?: Function): void; - setNoDelay(noDelay?: boolean): void; - setSocketKeepAlive(enable?: boolean, initialDelay?: number): void; + setTimeout(msecs: number, callback?: () => void): this; + destroy(error: Error): void; + setHeader(name: string, value: number | string | string[]): void; + getHeader(name: string): number | string | string[] | undefined; + getHeaders(): OutgoingHttpHeaders; + getHeaderNames(): string[]; + hasHeader(name: string): boolean; + removeHeader(name: string): void; + addTrailers(headers: OutgoingHttpHeaders | Array<[string, string]>): void; + flushHeaders(): void; + } - setHeader(name: string, value: string | string[]): void; - getHeader(name: string): string; - removeHeader(name: string): void; - addTrailers(headers: any): void; + // https://github.com/nodejs/node/blob/master/lib/_http_server.js#L108-L256 + export class ServerResponse extends OutgoingMessage { + statusCode: number; + statusMessage: string; - // Extended base methods - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - end(data?: any, encoding?: string): void; - } - export interface IncomingMessage extends stream.Readable { - httpVersion: string; - httpVersionMajor: number; - httpVersionMinor: number; - connection: net.Socket; - headers: any; - rawHeaders: string[]; - trailers: any; - rawTrailers: any; - setTimeout(msecs: number, callback: Function): NodeJS.Timer; + constructor(req: IncomingMessage); + + assignSocket(socket: net.Socket): void; + detachSocket(socket: net.Socket): void; + // https://github.com/nodejs/node/blob/master/test/parallel/test-http-write-callbacks.js#L53 + // no args in writeContinue callback + writeContinue(callback?: () => void): void; + writeHead(statusCode: number, reasonPhrase?: string, headers?: OutgoingHttpHeaders): void; + writeHead(statusCode: number, headers?: OutgoingHttpHeaders): void; + } + + // https://github.com/nodejs/node/blob/master/lib/_http_client.js#L77 + export class ClientRequest extends OutgoingMessage { + connection: net.Socket; + socket: net.Socket; + aborted: number; + + constructor(url: string | URL | ClientRequestArgs, cb?: (res: IncomingMessage) => void); + + abort(): void; + onSocket(socket: net.Socket): void; + setTimeout(timeout: number, callback?: () => void): this; + setNoDelay(noDelay?: boolean): void; + setSocketKeepAlive(enable?: boolean, initialDelay?: number): void; + } + + export class IncomingMessage extends stream.Readable { + constructor(socket: net.Socket); + + httpVersion: string; + httpVersionMajor: number; + httpVersionMinor: number; + connection: net.Socket; + headers: IncomingHttpHeaders; + rawHeaders: string[]; + trailers: { [key: string]: string | undefined }; + rawTrailers: string[]; + setTimeout(msecs: number, callback: () => void): this; /** * Only valid for request obtained from http.Server. */ - method?: string; + method?: string; /** * Only valid for request obtained from http.Server. */ - url?: string; + url?: string; /** * Only valid for response obtained from http.ClientRequest. */ - statusCode?: number; + statusCode?: number; /** * Only valid for response obtained from http.ClientRequest. */ - statusMessage?: string; - socket: net.Socket; - destroy(error?: Error): void; - } + statusMessage?: string; + socket: net.Socket; + destroy(error?: Error): void; + } + /** * @deprecated Use IncomingMessage */ - export interface ClientResponse extends IncomingMessage { } + export class ClientResponse extends IncomingMessage { } - export interface AgentOptions { + export interface AgentOptions { /** * Keep sockets around in a pool to be used by other requests in the future. Default = false */ - keepAlive?: boolean; + keepAlive?: boolean; /** * When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive. Default = 1000. * Only relevant if keepAlive is set to true. */ - keepAliveMsecs?: number; + keepAliveMsecs?: number; /** * Maximum number of sockets to allow per host. Default for Node 0.10 is 5, default for Node 0.12 is Infinity */ - maxSockets?: number; + maxSockets?: number; /** * Maximum number of sockets to leave open in a free state. Only relevant if keepAlive is set to true. Default = 256. */ - maxFreeSockets?: number; - } + maxFreeSockets?: number; + } - export class Agent { - maxSockets: number; - sockets: any; - requests: any; + export class Agent { + maxFreeSockets: number; + maxSockets: number; + sockets: any; + requests: any; - constructor(opts?: AgentOptions); + constructor(opts?: AgentOptions); /** * Destroy any sockets that are currently in use by the agent. @@ -807,62 +1159,61 @@ declare module "http" { * then it is best to explicitly shut down the agent when you know that it will no longer be used. Otherwise, * sockets may hang open for quite a long time before the server terminates them. */ - destroy(): void; - } + destroy(): void; + } - export var METHODS: string[]; + export var METHODS: string[]; - export var STATUS_CODES: { - [errorCode: number]: string; - [errorCode: string]: string; - }; - export function createServer(requestListener?: (request: IncomingMessage, response: ServerResponse) => void): Server; - export function createClient(port?: number, host?: string): any; - export function request(options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest; - export function get(options: any, callback?: (res: IncomingMessage) => void): ClientRequest; - export var globalAgent: Agent; + export var STATUS_CODES: { + [errorCode: number]: string | undefined; + [errorCode: string]: string | undefined; + }; + + export function createServer(requestListener?: (request: IncomingMessage, response: ServerResponse) => void): Server; + export function createClient(port?: number, host?: string): any; + + // although RequestOptions are passed as ClientRequestArgs to ClientRequest directly, + // create interface RequestOptions would make the naming more clear to developers + export interface RequestOptions extends ClientRequestArgs { } + export function request(options: RequestOptions | string | URL, callback?: (res: IncomingMessage) => void): ClientRequest; + export function get(options: RequestOptions | string | URL, callback?: (res: IncomingMessage) => void): ClientRequest; + export var globalAgent: Agent; } declare module "cluster" { - import * as child from "child_process"; - import * as events from "events"; - import * as net from "net"; + import * as child from "child_process"; + import * as events from "events"; + import * as net from "net"; - // interfaces - export interface ClusterSettings { - execArgv?: string[]; // default: process.execArgv - exec?: string; - args?: string[]; - silent?: boolean; - stdio?: any[]; - uid?: number; - gid?: number; - } + // interfaces + export interface ClusterSettings { + execArgv?: string[]; // default: process.execArgv + exec?: string; + args?: string[]; + silent?: boolean; + stdio?: any[]; + uid?: number; + gid?: number; + inspectPort?: number | (() => number); + } - export interface ClusterSetupMasterSettings { - exec?: string; // default: process.argv[1] - args?: string[]; // default: process.argv.slice(2) - silent?: boolean; // default: false - stdio?: any[]; - } + export interface Address { + address: string; + port: number; + addressType: number | "udp4" | "udp6"; // 4, 6, -1, "udp4", "udp6" + } - export interface Address { - address: string; - port: number; - addressType: number | "udp4" | "udp6"; // 4, 6, -1, "udp4", "udp6" - } - - export class Worker extends events.EventEmitter { - id: string; - process: child.ChildProcess; - suicide: boolean; - send(message: any, sendHandle?: any, callback?: (error: Error) => void): boolean; - kill(signal?: string): void; - destroy(signal?: string): void; - disconnect(): void; - isConnected(): boolean; - isDead(): boolean; - exitedAfterDisconnect: boolean; + export class Worker extends events.EventEmitter { + id: number; + process: child.ChildProcess; + suicide: boolean; + send(message: any, sendHandle?: any, callback?: (error: Error) => void): boolean; + kill(signal?: string): void; + destroy(signal?: string): void; + disconnect(): void; + isConnected(): boolean; + isDead(): boolean; + exitedAfterDisconnect: boolean; /** * events.EventEmitter @@ -873,68 +1224,68 @@ declare module "cluster" { * 5. message * 6. online */ - addListener(event: string, listener: Function): this; - addListener(event: "disconnect", listener: () => void): this; - addListener(event: "error", listener: (error: Error) => void): this; - addListener(event: "exit", listener: (code: number, signal: string) => void): this; - addListener(event: "listening", listener: (address: Address) => void): this; - addListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - addListener(event: "online", listener: () => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "disconnect", listener: () => void): this; + addListener(event: "error", listener: (error: Error) => void): this; + addListener(event: "exit", listener: (code: number, signal: string) => void): this; + addListener(event: "listening", listener: (address: Address) => void): this; + addListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + addListener(event: "online", listener: () => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "disconnect", listener: () => void): boolean - emit(event: "error", listener: (error: Error) => void): boolean - emit(event: "exit", listener: (code: number, signal: string) => void): boolean - emit(event: "listening", listener: (address: Address) => void): boolean - emit(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): boolean - emit(event: "online", listener: () => void): boolean + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "disconnect"): boolean; + emit(event: "error", error: Error): boolean; + emit(event: "exit", code: number, signal: string): boolean; + emit(event: "listening", address: Address): boolean; + emit(event: "message", message: any, handle: net.Socket | net.Server): boolean; + emit(event: "online"): boolean; - on(event: string, listener: Function): this; - on(event: "disconnect", listener: () => void): this; - on(event: "error", listener: (error: Error) => void): this; - on(event: "exit", listener: (code: number, signal: string) => void): this; - on(event: "listening", listener: (address: Address) => void): this; - on(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - on(event: "online", listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "disconnect", listener: () => void): this; + on(event: "error", listener: (error: Error) => void): this; + on(event: "exit", listener: (code: number, signal: string) => void): this; + on(event: "listening", listener: (address: Address) => void): this; + on(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + on(event: "online", listener: () => void): this; - once(event: string, listener: Function): this; - once(event: "disconnect", listener: () => void): this; - once(event: "error", listener: (error: Error) => void): this; - once(event: "exit", listener: (code: number, signal: string) => void): this; - once(event: "listening", listener: (address: Address) => void): this; - once(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - once(event: "online", listener: () => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "disconnect", listener: () => void): this; + once(event: "error", listener: (error: Error) => void): this; + once(event: "exit", listener: (code: number, signal: string) => void): this; + once(event: "listening", listener: (address: Address) => void): this; + once(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + once(event: "online", listener: () => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "disconnect", listener: () => void): this; - prependListener(event: "error", listener: (error: Error) => void): this; - prependListener(event: "exit", listener: (code: number, signal: string) => void): this; - prependListener(event: "listening", listener: (address: Address) => void): this; - prependListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - prependListener(event: "online", listener: () => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "disconnect", listener: () => void): this; + prependListener(event: "error", listener: (error: Error) => void): this; + prependListener(event: "exit", listener: (code: number, signal: string) => void): this; + prependListener(event: "listening", listener: (address: Address) => void): this; + prependListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + prependListener(event: "online", listener: () => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "disconnect", listener: () => void): this; - prependOnceListener(event: "error", listener: (error: Error) => void): this; - prependOnceListener(event: "exit", listener: (code: number, signal: string) => void): this; - prependOnceListener(event: "listening", listener: (address: Address) => void): this; - prependOnceListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - prependOnceListener(event: "online", listener: () => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "disconnect", listener: () => void): this; + prependOnceListener(event: "error", listener: (error: Error) => void): this; + prependOnceListener(event: "exit", listener: (code: number, signal: string) => void): this; + prependOnceListener(event: "listening", listener: (address: Address) => void): this; + prependOnceListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + prependOnceListener(event: "online", listener: () => void): this; + } - export interface Cluster extends events.EventEmitter { - Worker: Worker; - disconnect(callback?: Function): void; - fork(env?: any): Worker; - isMaster: boolean; - isWorker: boolean; - // TODO: cluster.schedulingPolicy - settings: ClusterSettings; - setupMaster(settings?: ClusterSetupMasterSettings): void; - worker: Worker; - workers: { - [index: string]: Worker - }; + export interface Cluster extends events.EventEmitter { + Worker: Worker; + disconnect(callback?: Function): void; + fork(env?: any): Worker; + isMaster: boolean; + isWorker: boolean; + // TODO: cluster.schedulingPolicy + settings: ClusterSettings; + setupMaster(settings?: ClusterSettings): void; + worker?: Worker; + workers?: { + [index: string]: Worker | undefined + }; /** * events.EventEmitter @@ -946,73 +1297,72 @@ declare module "cluster" { * 6. online * 7. setup */ - addListener(event: string, listener: Function): this; - addListener(event: "disconnect", listener: (worker: Worker) => void): this; - addListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; - addListener(event: "fork", listener: (worker: Worker) => void): this; - addListener(event: "listening", listener: (worker: Worker, address: Address) => void): this; - addListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - addListener(event: "online", listener: (worker: Worker) => void): this; - addListener(event: "setup", listener: (settings: any) => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "disconnect", listener: (worker: Worker) => void): this; + addListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; + addListener(event: "fork", listener: (worker: Worker) => void): this; + addListener(event: "listening", listener: (worker: Worker, address: Address) => void): this; + addListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + addListener(event: "online", listener: (worker: Worker) => void): this; + addListener(event: "setup", listener: (settings: any) => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "disconnect", listener: (worker: Worker) => void): boolean; - emit(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): boolean; - emit(event: "fork", listener: (worker: Worker) => void): boolean; - emit(event: "listening", listener: (worker: Worker, address: Address) => void): boolean; - emit(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): boolean; - emit(event: "online", listener: (worker: Worker) => void): boolean; - emit(event: "setup", listener: (settings: any) => void): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "disconnect", worker: Worker): boolean; + emit(event: "exit", worker: Worker, code: number, signal: string): boolean; + emit(event: "fork", worker: Worker): boolean; + emit(event: "listening", worker: Worker, address: Address): boolean; + emit(event: "message", worker: Worker, message: any, handle: net.Socket | net.Server): boolean; + emit(event: "online", worker: Worker): boolean; + emit(event: "setup", settings: any): boolean; - on(event: string, listener: Function): this; - on(event: "disconnect", listener: (worker: Worker) => void): this; - on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; - on(event: "fork", listener: (worker: Worker) => void): this; - on(event: "listening", listener: (worker: Worker, address: Address) => void): this; - on(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - on(event: "online", listener: (worker: Worker) => void): this; - on(event: "setup", listener: (settings: any) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "disconnect", listener: (worker: Worker) => void): this; + on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; + on(event: "fork", listener: (worker: Worker) => void): this; + on(event: "listening", listener: (worker: Worker, address: Address) => void): this; + on(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + on(event: "online", listener: (worker: Worker) => void): this; + on(event: "setup", listener: (settings: any) => void): this; - once(event: string, listener: Function): this; - once(event: "disconnect", listener: (worker: Worker) => void): this; - once(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; - once(event: "fork", listener: (worker: Worker) => void): this; - once(event: "listening", listener: (worker: Worker, address: Address) => void): this; - once(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - once(event: "online", listener: (worker: Worker) => void): this; - once(event: "setup", listener: (settings: any) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "disconnect", listener: (worker: Worker) => void): this; + once(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; + once(event: "fork", listener: (worker: Worker) => void): this; + once(event: "listening", listener: (worker: Worker, address: Address) => void): this; + once(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + once(event: "online", listener: (worker: Worker) => void): this; + once(event: "setup", listener: (settings: any) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "disconnect", listener: (worker: Worker) => void): this; - prependListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; - prependListener(event: "fork", listener: (worker: Worker) => void): this; - prependListener(event: "listening", listener: (worker: Worker, address: Address) => void): this; - prependListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - prependListener(event: "online", listener: (worker: Worker) => void): this; - prependListener(event: "setup", listener: (settings: any) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "disconnect", listener: (worker: Worker) => void): this; + prependListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; + prependListener(event: "fork", listener: (worker: Worker) => void): this; + prependListener(event: "listening", listener: (worker: Worker, address: Address) => void): this; + prependListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + prependListener(event: "online", listener: (worker: Worker) => void): this; + prependListener(event: "setup", listener: (settings: any) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "disconnect", listener: (worker: Worker) => void): this; - prependOnceListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; - prependOnceListener(event: "fork", listener: (worker: Worker) => void): this; - prependOnceListener(event: "listening", listener: (worker: Worker, address: Address) => void): this; - prependOnceListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. - prependOnceListener(event: "online", listener: (worker: Worker) => void): this; - prependOnceListener(event: "setup", listener: (settings: any) => void): this; + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "disconnect", listener: (worker: Worker) => void): this; + prependOnceListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this; + prependOnceListener(event: "fork", listener: (worker: Worker) => void): this; + prependOnceListener(event: "listening", listener: (worker: Worker, address: Address) => void): this; + prependOnceListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined. + prependOnceListener(event: "online", listener: (worker: Worker) => void): this; + prependOnceListener(event: "setup", listener: (settings: any) => void): this; + } - } - - export function disconnect(callback?: Function): void; - export function fork(env?: any): Worker; - export var isMaster: boolean; - export var isWorker: boolean; - // TODO: cluster.schedulingPolicy - export var settings: ClusterSettings; - export function setupMaster(settings?: ClusterSetupMasterSettings): void; - export var worker: Worker; - export var workers: { - [index: string]: Worker - }; + export function disconnect(callback?: Function): void; + export function fork(env?: any): Worker; + export var isMaster: boolean; + export var isWorker: boolean; + // TODO: cluster.schedulingPolicy + export var settings: ClusterSettings; + export function setupMaster(settings?: ClusterSettings): void; + export var worker: Worker; + export var workers: { + [index: string]: Worker | undefined + }; /** * events.EventEmitter @@ -1024,499 +1374,522 @@ declare module "cluster" { * 6. online * 7. setup */ - export function addListener(event: string, listener: Function): Cluster; - export function addListener(event: "disconnect", listener: (worker: Worker) => void): Cluster; - export function addListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; - export function addListener(event: "fork", listener: (worker: Worker) => void): Cluster; - export function addListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; - export function addListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. - export function addListener(event: "online", listener: (worker: Worker) => void): Cluster; - export function addListener(event: "setup", listener: (settings: any) => void): Cluster; + export function addListener(event: string, listener: (...args: any[]) => void): Cluster; + export function addListener(event: "disconnect", listener: (worker: Worker) => void): Cluster; + export function addListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; + export function addListener(event: "fork", listener: (worker: Worker) => void): Cluster; + export function addListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; + export function addListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. + export function addListener(event: "online", listener: (worker: Worker) => void): Cluster; + export function addListener(event: "setup", listener: (settings: any) => void): Cluster; - export function emit(event: string | symbol, ...args: any[]): boolean; - export function emit(event: "disconnect", listener: (worker: Worker) => void): boolean; - export function emit(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): boolean; - export function emit(event: "fork", listener: (worker: Worker) => void): boolean; - export function emit(event: "listening", listener: (worker: Worker, address: Address) => void): boolean; - export function emit(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): boolean; - export function emit(event: "online", listener: (worker: Worker) => void): boolean; - export function emit(event: "setup", listener: (settings: any) => void): boolean; + export function emit(event: string | symbol, ...args: any[]): boolean; + export function emit(event: "disconnect", worker: Worker): boolean; + export function emit(event: "exit", worker: Worker, code: number, signal: string): boolean; + export function emit(event: "fork", worker: Worker): boolean; + export function emit(event: "listening", worker: Worker, address: Address): boolean; + export function emit(event: "message", worker: Worker, message: any, handle: net.Socket | net.Server): boolean; + export function emit(event: "online", worker: Worker): boolean; + export function emit(event: "setup", settings: any): boolean; - export function on(event: string, listener: Function): Cluster; - export function on(event: "disconnect", listener: (worker: Worker) => void): Cluster; - export function on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; - export function on(event: "fork", listener: (worker: Worker) => void): Cluster; - export function on(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; - export function on(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. - export function on(event: "online", listener: (worker: Worker) => void): Cluster; - export function on(event: "setup", listener: (settings: any) => void): Cluster; + export function on(event: string, listener: (...args: any[]) => void): Cluster; + export function on(event: "disconnect", listener: (worker: Worker) => void): Cluster; + export function on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; + export function on(event: "fork", listener: (worker: Worker) => void): Cluster; + export function on(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; + export function on(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. + export function on(event: "online", listener: (worker: Worker) => void): Cluster; + export function on(event: "setup", listener: (settings: any) => void): Cluster; - export function once(event: string, listener: Function): Cluster; - export function once(event: "disconnect", listener: (worker: Worker) => void): Cluster; - export function once(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; - export function once(event: "fork", listener: (worker: Worker) => void): Cluster; - export function once(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; - export function once(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. - export function once(event: "online", listener: (worker: Worker) => void): Cluster; - export function once(event: "setup", listener: (settings: any) => void): Cluster; + export function once(event: string, listener: (...args: any[]) => void): Cluster; + export function once(event: "disconnect", listener: (worker: Worker) => void): Cluster; + export function once(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; + export function once(event: "fork", listener: (worker: Worker) => void): Cluster; + export function once(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; + export function once(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. + export function once(event: "online", listener: (worker: Worker) => void): Cluster; + export function once(event: "setup", listener: (settings: any) => void): Cluster; - export function removeListener(event: string, listener: Function): Cluster; - export function removeAllListeners(event?: string): Cluster; - export function setMaxListeners(n: number): Cluster; - export function getMaxListeners(): number; - export function listeners(event: string): Function[]; - export function listenerCount(type: string): number; + export function removeListener(event: string, listener: (...args: any[]) => void): Cluster; + export function removeAllListeners(event?: string): Cluster; + export function setMaxListeners(n: number): Cluster; + export function getMaxListeners(): number; + export function listeners(event: string): Function[]; + export function listenerCount(type: string): number; - export function prependListener(event: string, listener: Function): Cluster; - export function prependListener(event: "disconnect", listener: (worker: Worker) => void): Cluster; - export function prependListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; - export function prependListener(event: "fork", listener: (worker: Worker) => void): Cluster; - export function prependListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; - export function prependListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. - export function prependListener(event: "online", listener: (worker: Worker) => void): Cluster; - export function prependListener(event: "setup", listener: (settings: any) => void): Cluster; + export function prependListener(event: string, listener: (...args: any[]) => void): Cluster; + export function prependListener(event: "disconnect", listener: (worker: Worker) => void): Cluster; + export function prependListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; + export function prependListener(event: "fork", listener: (worker: Worker) => void): Cluster; + export function prependListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; + export function prependListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. + export function prependListener(event: "online", listener: (worker: Worker) => void): Cluster; + export function prependListener(event: "setup", listener: (settings: any) => void): Cluster; - export function prependOnceListener(event: string, listener: Function): Cluster; - export function prependOnceListener(event: "disconnect", listener: (worker: Worker) => void): Cluster; - export function prependOnceListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; - export function prependOnceListener(event: "fork", listener: (worker: Worker) => void): Cluster; - export function prependOnceListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; - export function prependOnceListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. - export function prependOnceListener(event: "online", listener: (worker: Worker) => void): Cluster; - export function prependOnceListener(event: "setup", listener: (settings: any) => void): Cluster; + export function prependOnceListener(event: string, listener: (...args: any[]) => void): Cluster; + export function prependOnceListener(event: "disconnect", listener: (worker: Worker) => void): Cluster; + export function prependOnceListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster; + export function prependOnceListener(event: "fork", listener: (worker: Worker) => void): Cluster; + export function prependOnceListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster; + export function prependOnceListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined. + export function prependOnceListener(event: "online", listener: (worker: Worker) => void): Cluster; + export function prependOnceListener(event: "setup", listener: (settings: any) => void): Cluster; - export function eventNames(): string[]; + export function eventNames(): string[]; } declare module "zlib" { - import * as stream from "stream"; + import * as stream from "stream"; - export interface ZlibOptions { - flush?: number; // default: zlib.constants.Z_NO_FLUSH - finishFlush?: number; // default: zlib.constants.Z_FINISH - chunkSize?: number; // default: 16*1024 - windowBits?: number; - level?: number; // compression only - memLevel?: number; // compression only - strategy?: number; // compression only - dictionary?: any; // deflate/inflate only, empty dictionary by default - } + export interface ZlibOptions { + flush?: number; // default: zlib.constants.Z_NO_FLUSH + finishFlush?: number; // default: zlib.constants.Z_FINISH + chunkSize?: number; // default: 16*1024 + windowBits?: number; + level?: number; // compression only + memLevel?: number; // compression only + strategy?: number; // compression only + dictionary?: any; // deflate/inflate only, empty dictionary by default + } - export interface Gzip extends stream.Transform { } - export interface Gunzip extends stream.Transform { } - export interface Deflate extends stream.Transform { } - export interface Inflate extends stream.Transform { } - export interface DeflateRaw extends stream.Transform { } - export interface InflateRaw extends stream.Transform { } - export interface Unzip extends stream.Transform { } + export interface Zlib { + readonly bytesRead: number; + close(callback?: () => void): void; + flush(kind?: number | (() => void), callback?: () => void): void; + } - export function createGzip(options?: ZlibOptions): Gzip; - export function createGunzip(options?: ZlibOptions): Gunzip; - export function createDeflate(options?: ZlibOptions): Deflate; - export function createInflate(options?: ZlibOptions): Inflate; - export function createDeflateRaw(options?: ZlibOptions): DeflateRaw; - export function createInflateRaw(options?: ZlibOptions): InflateRaw; - export function createUnzip(options?: ZlibOptions): Unzip; + export interface ZlibParams { + params(level: number, strategy: number, callback: () => void): void; + } - export function deflate(buf: Buffer | string, callback: (error: Error, result: Buffer) => void): void; - export function deflate(buf: Buffer | string, options: ZlibOptions, callback: (error: Error, result: Buffer) => void): void; - export function deflateSync(buf: Buffer | string, options?: ZlibOptions): Buffer; - export function deflateRaw(buf: Buffer | string, callback: (error: Error, result: Buffer) => void): void; - export function deflateRaw(buf: Buffer | string, options: ZlibOptions, callback: (error: Error, result: Buffer) => void): void; - export function deflateRawSync(buf: Buffer | string, options?: ZlibOptions): Buffer; - export function gzip(buf: Buffer | string, callback: (error: Error, result: Buffer) => void): void; - export function gzip(buf: Buffer | string, options: ZlibOptions, callback: (error: Error, result: Buffer) => void): void; - export function gzipSync(buf: Buffer | string, options?: ZlibOptions): Buffer; - export function gunzip(buf: Buffer | string, callback: (error: Error, result: Buffer) => void): void; - export function gunzip(buf: Buffer | string, options: ZlibOptions, callback: (error: Error, result: Buffer) => void): void; - export function gunzipSync(buf: Buffer | string, options?: ZlibOptions): Buffer; - export function inflate(buf: Buffer | string, callback: (error: Error, result: Buffer) => void): void; - export function inflate(buf: Buffer | string, options: ZlibOptions, callback: (error: Error, result: Buffer) => void): void; - export function inflateSync(buf: Buffer | string, options?: ZlibOptions): Buffer; - export function inflateRaw(buf: Buffer | string, callback: (error: Error, result: Buffer) => void): void; - export function inflateRaw(buf: Buffer | string, options: ZlibOptions, callback: (error: Error, result: Buffer) => void): void; - export function inflateRawSync(buf: Buffer | string, options?: ZlibOptions): Buffer; - export function unzip(buf: Buffer | string, callback: (error: Error, result: Buffer) => void): void; - export function unzip(buf: Buffer | string, options: ZlibOptions, callback: (error: Error, result: Buffer) => void): void; - export function unzipSync(buf: Buffer | string, options?: ZlibOptions): Buffer; + export interface ZlibReset { + reset(): void; + } - export namespace constants { - // Allowed flush values. + export interface Gzip extends stream.Transform, Zlib { } + export interface Gunzip extends stream.Transform, Zlib { } + export interface Deflate extends stream.Transform, Zlib, ZlibReset, ZlibParams { } + export interface Inflate extends stream.Transform, Zlib, ZlibReset { } + export interface DeflateRaw extends stream.Transform, Zlib, ZlibReset, ZlibParams { } + export interface InflateRaw extends stream.Transform, Zlib, ZlibReset { } + export interface Unzip extends stream.Transform, Zlib { } - export const Z_NO_FLUSH: number; - export const Z_PARTIAL_FLUSH: number; - export const Z_SYNC_FLUSH: number; - export const Z_FULL_FLUSH: number; - export const Z_FINISH: number; - export const Z_BLOCK: number; - export const Z_TREES: number; + export function createGzip(options?: ZlibOptions): Gzip; + export function createGunzip(options?: ZlibOptions): Gunzip; + export function createDeflate(options?: ZlibOptions): Deflate; + export function createInflate(options?: ZlibOptions): Inflate; + export function createDeflateRaw(options?: ZlibOptions): DeflateRaw; + export function createInflateRaw(options?: ZlibOptions): InflateRaw; + export function createUnzip(options?: ZlibOptions): Unzip; - // Return codes for the compression/decompression functions. Negative values are errors, positive values are used for special but normal events. + type InputType = string | Buffer | DataView /* | TypedArray */; + export function deflate(buf: InputType, callback: (error: Error | null, result: Buffer) => void): void; + export function deflate(buf: InputType, options: ZlibOptions, callback: (error: Error | null, result: Buffer) => void): void; + export function deflateSync(buf: InputType, options?: ZlibOptions): Buffer; + export function deflateRaw(buf: InputType, callback: (error: Error | null, result: Buffer) => void): void; + export function deflateRaw(buf: InputType, options: ZlibOptions, callback: (error: Error | null, result: Buffer) => void): void; + export function deflateRawSync(buf: InputType, options?: ZlibOptions): Buffer; + export function gzip(buf: InputType, callback: (error: Error | null, result: Buffer) => void): void; + export function gzip(buf: InputType, options: ZlibOptions, callback: (error: Error | null, result: Buffer) => void): void; + export function gzipSync(buf: InputType, options?: ZlibOptions): Buffer; + export function gunzip(buf: InputType, callback: (error: Error | null, result: Buffer) => void): void; + export function gunzip(buf: InputType, options: ZlibOptions, callback: (error: Error | null, result: Buffer) => void): void; + export function gunzipSync(buf: InputType, options?: ZlibOptions): Buffer; + export function inflate(buf: InputType, callback: (error: Error | null, result: Buffer) => void): void; + export function inflate(buf: InputType, options: ZlibOptions, callback: (error: Error | null, result: Buffer) => void): void; + export function inflateSync(buf: InputType, options?: ZlibOptions): Buffer; + export function inflateRaw(buf: InputType, callback: (error: Error | null, result: Buffer) => void): void; + export function inflateRaw(buf: InputType, options: ZlibOptions, callback: (error: Error | null, result: Buffer) => void): void; + export function inflateRawSync(buf: InputType, options?: ZlibOptions): Buffer; + export function unzip(buf: InputType, callback: (error: Error | null, result: Buffer) => void): void; + export function unzip(buf: InputType, options: ZlibOptions, callback: (error: Error | null, result: Buffer) => void): void; + export function unzipSync(buf: InputType, options?: ZlibOptions): Buffer; - export const Z_OK: number; - export const Z_STREAM_END: number; - export const Z_NEED_DICT: number; - export const Z_ERRNO: number; - export const Z_STREAM_ERROR: number; - export const Z_DATA_ERROR: number; - export const Z_MEM_ERROR: number; - export const Z_BUF_ERROR: number; - export const Z_VERSION_ERROR: number; + export namespace constants { + // Allowed flush values. - // Compression levels. + export const Z_NO_FLUSH: number; + export const Z_PARTIAL_FLUSH: number; + export const Z_SYNC_FLUSH: number; + export const Z_FULL_FLUSH: number; + export const Z_FINISH: number; + export const Z_BLOCK: number; + export const Z_TREES: number; - export const Z_NO_COMPRESSION: number; - export const Z_BEST_SPEED: number; - export const Z_BEST_COMPRESSION: number; - export const Z_DEFAULT_COMPRESSION: number; + // Return codes for the compression/decompression functions. Negative values are errors, positive values are used for special but normal events. - // Compression strategy. + export const Z_OK: number; + export const Z_STREAM_END: number; + export const Z_NEED_DICT: number; + export const Z_ERRNO: number; + export const Z_STREAM_ERROR: number; + export const Z_DATA_ERROR: number; + export const Z_MEM_ERROR: number; + export const Z_BUF_ERROR: number; + export const Z_VERSION_ERROR: number; - export const Z_FILTERED: number; - export const Z_HUFFMAN_ONLY: number; - export const Z_RLE: number; - export const Z_FIXED: number; - export const Z_DEFAULT_STRATEGY: number; - } + // Compression levels. - // Constants - export var Z_NO_FLUSH: number; - export var Z_PARTIAL_FLUSH: number; - export var Z_SYNC_FLUSH: number; - export var Z_FULL_FLUSH: number; - export var Z_FINISH: number; - export var Z_BLOCK: number; - export var Z_TREES: number; - export var Z_OK: number; - export var Z_STREAM_END: number; - export var Z_NEED_DICT: number; - export var Z_ERRNO: number; - export var Z_STREAM_ERROR: number; - export var Z_DATA_ERROR: number; - export var Z_MEM_ERROR: number; - export var Z_BUF_ERROR: number; - export var Z_VERSION_ERROR: number; - export var Z_NO_COMPRESSION: number; - export var Z_BEST_SPEED: number; - export var Z_BEST_COMPRESSION: number; - export var Z_DEFAULT_COMPRESSION: number; - export var Z_FILTERED: number; - export var Z_HUFFMAN_ONLY: number; - export var Z_RLE: number; - export var Z_FIXED: number; - export var Z_DEFAULT_STRATEGY: number; - export var Z_BINARY: number; - export var Z_TEXT: number; - export var Z_ASCII: number; - export var Z_UNKNOWN: number; - export var Z_DEFLATED: number; + export const Z_NO_COMPRESSION: number; + export const Z_BEST_SPEED: number; + export const Z_BEST_COMPRESSION: number; + export const Z_DEFAULT_COMPRESSION: number; + + // Compression strategy. + + export const Z_FILTERED: number; + export const Z_HUFFMAN_ONLY: number; + export const Z_RLE: number; + export const Z_FIXED: number; + export const Z_DEFAULT_STRATEGY: number; + } + + // Constants + export var Z_NO_FLUSH: number; + export var Z_PARTIAL_FLUSH: number; + export var Z_SYNC_FLUSH: number; + export var Z_FULL_FLUSH: number; + export var Z_FINISH: number; + export var Z_BLOCK: number; + export var Z_TREES: number; + export var Z_OK: number; + export var Z_STREAM_END: number; + export var Z_NEED_DICT: number; + export var Z_ERRNO: number; + export var Z_STREAM_ERROR: number; + export var Z_DATA_ERROR: number; + export var Z_MEM_ERROR: number; + export var Z_BUF_ERROR: number; + export var Z_VERSION_ERROR: number; + export var Z_NO_COMPRESSION: number; + export var Z_BEST_SPEED: number; + export var Z_BEST_COMPRESSION: number; + export var Z_DEFAULT_COMPRESSION: number; + export var Z_FILTERED: number; + export var Z_HUFFMAN_ONLY: number; + export var Z_RLE: number; + export var Z_FIXED: number; + export var Z_DEFAULT_STRATEGY: number; + export var Z_BINARY: number; + export var Z_TEXT: number; + export var Z_ASCII: number; + export var Z_UNKNOWN: number; + export var Z_DEFLATED: number; } declare module "os" { - export interface CpuInfo { - model: string; - speed: number; - times: { - user: number; - nice: number; - sys: number; - idle: number; - irq: number; - }; - } + export interface CpuInfo { + model: string; + speed: number; + times: { + user: number; + nice: number; + sys: number; + idle: number; + irq: number; + }; + } - export interface NetworkInterfaceInfo { - address: string; - netmask: string; - family: string; - mac: string; - internal: boolean; - } + export interface NetworkInterfaceBase { + address: string; + netmask: string; + mac: string; + internal: boolean; + } - export function hostname(): string; - export function loadavg(): number[]; - export function uptime(): number; - export function freemem(): number; - export function totalmem(): number; - export function cpus(): CpuInfo[]; - export function type(): string; - export function release(): string; - export function networkInterfaces(): { [index: string]: NetworkInterfaceInfo[] }; - export function homedir(): string; - export function userInfo(options?: { encoding: string }): { username: string, uid: number, gid: number, shell: any, homedir: string } - export var constants: { - UV_UDP_REUSEADDR: number, - signals: { - SIGHUP: number; - SIGINT: number; - SIGQUIT: number; - SIGILL: number; - SIGTRAP: number; - SIGABRT: number; - SIGIOT: number; - SIGBUS: number; - SIGFPE: number; - SIGKILL: number; - SIGUSR1: number; - SIGSEGV: number; - SIGUSR2: number; - SIGPIPE: number; - SIGALRM: number; - SIGTERM: number; - SIGCHLD: number; - SIGSTKFLT: number; - SIGCONT: number; - SIGSTOP: number; - SIGTSTP: number; - SIGTTIN: number; - SIGTTOU: number; - SIGURG: number; - SIGXCPU: number; - SIGXFSZ: number; - SIGVTALRM: number; - SIGPROF: number; - SIGWINCH: number; - SIGIO: number; - SIGPOLL: number; - SIGPWR: number; - SIGSYS: number; - SIGUNUSED: number; - }, - errno: { - E2BIG: number; - EACCES: number; - EADDRINUSE: number; - EADDRNOTAVAIL: number; - EAFNOSUPPORT: number; - EAGAIN: number; - EALREADY: number; - EBADF: number; - EBADMSG: number; - EBUSY: number; - ECANCELED: number; - ECHILD: number; - ECONNABORTED: number; - ECONNREFUSED: number; - ECONNRESET: number; - EDEADLK: number; - EDESTADDRREQ: number; - EDOM: number; - EDQUOT: number; - EEXIST: number; - EFAULT: number; - EFBIG: number; - EHOSTUNREACH: number; - EIDRM: number; - EILSEQ: number; - EINPROGRESS: number; - EINTR: number; - EINVAL: number; - EIO: number; - EISCONN: number; - EISDIR: number; - ELOOP: number; - EMFILE: number; - EMLINK: number; - EMSGSIZE: number; - EMULTIHOP: number; - ENAMETOOLONG: number; - ENETDOWN: number; - ENETRESET: number; - ENETUNREACH: number; - ENFILE: number; - ENOBUFS: number; - ENODATA: number; - ENODEV: number; - ENOENT: number; - ENOEXEC: number; - ENOLCK: number; - ENOLINK: number; - ENOMEM: number; - ENOMSG: number; - ENOPROTOOPT: number; - ENOSPC: number; - ENOSR: number; - ENOSTR: number; - ENOSYS: number; - ENOTCONN: number; - ENOTDIR: number; - ENOTEMPTY: number; - ENOTSOCK: number; - ENOTSUP: number; - ENOTTY: number; - ENXIO: number; - EOPNOTSUPP: number; - EOVERFLOW: number; - EPERM: number; - EPIPE: number; - EPROTO: number; - EPROTONOSUPPORT: number; - EPROTOTYPE: number; - ERANGE: number; - EROFS: number; - ESPIPE: number; - ESRCH: number; - ESTALE: number; - ETIME: number; - ETIMEDOUT: number; - ETXTBSY: number; - EWOULDBLOCK: number; - EXDEV: number; - }, - }; - export function arch(): string; - export function platform(): NodeJS.Platform; - export function tmpdir(): string; - export var EOL: string; - export function endianness(): "BE" | "LE"; + export interface NetworkInterfaceInfoIPv4 extends NetworkInterfaceBase { + family: "IPv4"; + } + + export interface NetworkInterfaceInfoIPv6 extends NetworkInterfaceBase { + family: "IPv6"; + scopeid: number; + } + + export type NetworkInterfaceInfo = NetworkInterfaceInfoIPv4 | NetworkInterfaceInfoIPv6; + + export function hostname(): string; + export function loadavg(): number[]; + export function uptime(): number; + export function freemem(): number; + export function totalmem(): number; + export function cpus(): CpuInfo[]; + export function type(): string; + export function release(): string; + export function networkInterfaces(): { [index: string]: NetworkInterfaceInfo[] }; + export function homedir(): string; + export function userInfo(options?: { encoding: string }): { username: string, uid: number, gid: number, shell: any, homedir: string }; + export var constants: { + UV_UDP_REUSEADDR: number, + signals: { + SIGHUP: number; + SIGINT: number; + SIGQUIT: number; + SIGILL: number; + SIGTRAP: number; + SIGABRT: number; + SIGIOT: number; + SIGBUS: number; + SIGFPE: number; + SIGKILL: number; + SIGUSR1: number; + SIGSEGV: number; + SIGUSR2: number; + SIGPIPE: number; + SIGALRM: number; + SIGTERM: number; + SIGCHLD: number; + SIGSTKFLT: number; + SIGCONT: number; + SIGSTOP: number; + SIGTSTP: number; + SIGTTIN: number; + SIGTTOU: number; + SIGURG: number; + SIGXCPU: number; + SIGXFSZ: number; + SIGVTALRM: number; + SIGPROF: number; + SIGWINCH: number; + SIGIO: number; + SIGPOLL: number; + SIGPWR: number; + SIGSYS: number; + SIGUNUSED: number; + }, + errno: { + E2BIG: number; + EACCES: number; + EADDRINUSE: number; + EADDRNOTAVAIL: number; + EAFNOSUPPORT: number; + EAGAIN: number; + EALREADY: number; + EBADF: number; + EBADMSG: number; + EBUSY: number; + ECANCELED: number; + ECHILD: number; + ECONNABORTED: number; + ECONNREFUSED: number; + ECONNRESET: number; + EDEADLK: number; + EDESTADDRREQ: number; + EDOM: number; + EDQUOT: number; + EEXIST: number; + EFAULT: number; + EFBIG: number; + EHOSTUNREACH: number; + EIDRM: number; + EILSEQ: number; + EINPROGRESS: number; + EINTR: number; + EINVAL: number; + EIO: number; + EISCONN: number; + EISDIR: number; + ELOOP: number; + EMFILE: number; + EMLINK: number; + EMSGSIZE: number; + EMULTIHOP: number; + ENAMETOOLONG: number; + ENETDOWN: number; + ENETRESET: number; + ENETUNREACH: number; + ENFILE: number; + ENOBUFS: number; + ENODATA: number; + ENODEV: number; + ENOENT: number; + ENOEXEC: number; + ENOLCK: number; + ENOLINK: number; + ENOMEM: number; + ENOMSG: number; + ENOPROTOOPT: number; + ENOSPC: number; + ENOSR: number; + ENOSTR: number; + ENOSYS: number; + ENOTCONN: number; + ENOTDIR: number; + ENOTEMPTY: number; + ENOTSOCK: number; + ENOTSUP: number; + ENOTTY: number; + ENXIO: number; + EOPNOTSUPP: number; + EOVERFLOW: number; + EPERM: number; + EPIPE: number; + EPROTO: number; + EPROTONOSUPPORT: number; + EPROTOTYPE: number; + ERANGE: number; + EROFS: number; + ESPIPE: number; + ESRCH: number; + ESTALE: number; + ETIME: number; + ETIMEDOUT: number; + ETXTBSY: number; + EWOULDBLOCK: number; + EXDEV: number; + }, + }; + export function arch(): string; + export function platform(): NodeJS.Platform; + export function tmpdir(): string; + export const EOL: string; + export function endianness(): "BE" | "LE"; } declare module "https" { - import * as tls from "tls"; - import * as events from "events"; - import * as http from "http"; + import * as tls from "tls"; + import * as events from "events"; + import * as http from "http"; + import { URL } from "url"; - export interface ServerOptions { - pfx?: any; - key?: any; - passphrase?: string; - cert?: any; - ca?: any; - crl?: any; - ciphers?: string; - honorCipherOrder?: boolean; - requestCert?: boolean; - rejectUnauthorized?: boolean; - NPNProtocols?: any; - SNICallback?: (servername: string, cb: (err: Error, ctx: tls.SecureContext) => any) => any; - } + export type ServerOptions = tls.SecureContextOptions & tls.TlsOptions; - export interface RequestOptions extends http.RequestOptions { - pfx?: any; - key?: any; - passphrase?: string; - cert?: any; - ca?: any; - ciphers?: string; - rejectUnauthorized?: boolean; - secureProtocol?: string; - } + // see https://nodejs.org/docs/latest-v8.x/api/https.html#https_https_request_options_callback + type extendedRequestKeys = "pfx" | + "key" | + "passphrase" | + "cert" | + "ca" | + "ciphers" | + "rejectUnauthorized" | + "secureProtocol" | + "servername"; - export interface Agent extends http.Agent { } + export type RequestOptions = http.RequestOptions & Pick<tls.ConnectionOptions, extendedRequestKeys>; - export interface AgentOptions extends http.AgentOptions { - pfx?: any; - key?: any; - passphrase?: string; - cert?: any; - ca?: any; - ciphers?: string; - rejectUnauthorized?: boolean; - secureProtocol?: string; - maxCachedSessions?: number; - } + export interface AgentOptions extends http.AgentOptions, tls.ConnectionOptions { + rejectUnauthorized?: boolean; + maxCachedSessions?: number; + } - export var Agent: { - new(options?: AgentOptions): Agent; - }; - export interface Server extends tls.Server { } - export function createServer(options: ServerOptions, requestListener?: Function): Server; - export function request(options: RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest; - export function get(options: RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest; - export var globalAgent: Agent; + export class Agent extends http.Agent { + constructor(options?: AgentOptions); + options: AgentOptions; + } + + export class Server extends tls.Server { + setTimeout(callback: () => void): this; + setTimeout(msecs?: number, callback?: () => void): this; + timeout: number; + keepAliveTimeout: number; + } + + export function createServer(options: ServerOptions, requestListener?: (req: http.IncomingMessage, res: http.ServerResponse) => void): Server; + export function request(options: RequestOptions | string | URL, callback?: (res: http.IncomingMessage) => void): http.ClientRequest; + export function get(options: RequestOptions | string | URL, callback?: (res: http.IncomingMessage) => void): http.ClientRequest; + export var globalAgent: Agent; } declare module "punycode" { - export function decode(string: string): string; - export function encode(string: string): string; - export function toUnicode(domain: string): string; - export function toASCII(domain: string): string; - export var ucs2: ucs2; - interface ucs2 { - decode(string: string): number[]; - encode(codePoints: number[]): string; - } - export var version: any; + export function decode(string: string): string; + export function encode(string: string): string; + export function toUnicode(domain: string): string; + export function toASCII(domain: string): string; + export var ucs2: ucs2; + interface ucs2 { + decode(string: string): number[]; + encode(codePoints: number[]): string; + } + export var version: any; } declare module "repl" { - import * as stream from "stream"; - import * as readline from "readline"; + import * as stream from "stream"; + import * as readline from "readline"; - export interface ReplOptions { - prompt?: string; - input?: NodeJS.ReadableStream; - output?: NodeJS.WritableStream; - terminal?: boolean; - eval?: Function; - useColors?: boolean; - useGlobal?: boolean; - ignoreUndefined?: boolean; - writer?: Function; - completer?: Function; - replMode?: any; - breakEvalOnSigint?: any; - } + export interface ReplOptions { + prompt?: string; + input?: NodeJS.ReadableStream; + output?: NodeJS.WritableStream; + terminal?: boolean; + eval?: Function; + useColors?: boolean; + useGlobal?: boolean; + ignoreUndefined?: boolean; + writer?: Function; + completer?: Function; + replMode?: any; + breakEvalOnSigint?: any; + } - export interface REPLServer extends readline.ReadLine { - context: any; - defineCommand(keyword: string, cmd: Function | { help: string, action: Function }): void; - displayPrompt(preserveCursor?: boolean): void; + export interface REPLServer extends readline.ReadLine { + context: any; + inputStream: NodeJS.ReadableStream; + outputStream: NodeJS.WritableStream; + + defineCommand(keyword: string, cmd: Function | { help: string, action: Function }): void; + displayPrompt(preserveCursor?: boolean): void; /** * events.EventEmitter * 1. exit * 2. reset - **/ + */ - addListener(event: string, listener: Function): this; - addListener(event: "exit", listener: () => void): this; - addListener(event: "reset", listener: Function): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "exit", listener: () => void): this; + addListener(event: "reset", listener: (...args: any[]) => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "exit"): boolean; - emit(event: "reset", context: any): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "exit"): boolean; + emit(event: "reset", context: any): boolean; - on(event: string, listener: Function): this; - on(event: "exit", listener: () => void): this; - on(event: "reset", listener: Function): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "exit", listener: () => void): this; + on(event: "reset", listener: (...args: any[]) => void): this; - once(event: string, listener: Function): this; - once(event: "exit", listener: () => void): this; - once(event: "reset", listener: Function): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "exit", listener: () => void): this; + once(event: "reset", listener: (...args: any[]) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "exit", listener: () => void): this; - prependListener(event: "reset", listener: Function): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "exit", listener: () => void): this; + prependListener(event: "reset", listener: (...args: any[]) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "exit", listener: () => void): this; - prependOnceListener(event: "reset", listener: Function): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "exit", listener: () => void): this; + prependOnceListener(event: "reset", listener: (...args: any[]) => void): this; + } - export function start(options?: string | ReplOptions): REPLServer; + export function start(options?: string | ReplOptions): REPLServer; + + export class Recoverable extends SyntaxError { + err: Error; + + constructor(err: Error); + } } declare module "readline" { - import * as events from "events"; - import * as stream from "stream"; + import * as events from "events"; + import * as stream from "stream"; - export interface Key { - sequence?: string; - name?: string; - ctrl?: boolean; - meta?: boolean; - shift?: boolean; - } + export interface Key { + sequence?: string; + name?: string; + ctrl?: boolean; + meta?: boolean; + shift?: boolean; + } - export interface ReadLine extends events.EventEmitter { - setPrompt(prompt: string): void; - prompt(preserveCursor?: boolean): void; - question(query: string, callback: (answer: string) => void): void; - pause(): ReadLine; - resume(): ReadLine; - close(): void; - write(data: string | Buffer, key?: Key): void; + export interface ReadLine extends events.EventEmitter { + setPrompt(prompt: string): void; + prompt(preserveCursor?: boolean): void; + question(query: string, callback: (answer: string) => void): void; + pause(): ReadLine; + resume(): ReadLine; + close(): void; + write(data: string | Buffer, key?: Key): void; /** * events.EventEmitter @@ -1527,138 +1900,141 @@ declare module "readline" { * 5. SIGCONT * 6. SIGINT * 7. SIGTSTP - **/ + */ - addListener(event: string, listener: Function): this; - addListener(event: "close", listener: () => void): this; - addListener(event: "line", listener: (input: any) => void): this; - addListener(event: "pause", listener: () => void): this; - addListener(event: "resume", listener: () => void): this; - addListener(event: "SIGCONT", listener: () => void): this; - addListener(event: "SIGINT", listener: () => void): this; - addListener(event: "SIGTSTP", listener: () => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "line", listener: (input: any) => void): this; + addListener(event: "pause", listener: () => void): this; + addListener(event: "resume", listener: () => void): this; + addListener(event: "SIGCONT", listener: () => void): this; + addListener(event: "SIGINT", listener: () => void): this; + addListener(event: "SIGTSTP", listener: () => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "close"): boolean; - emit(event: "line", input: any): boolean; - emit(event: "pause"): boolean; - emit(event: "resume"): boolean; - emit(event: "SIGCONT"): boolean; - emit(event: "SIGINT"): boolean; - emit(event: "SIGTSTP"): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close"): boolean; + emit(event: "line", input: any): boolean; + emit(event: "pause"): boolean; + emit(event: "resume"): boolean; + emit(event: "SIGCONT"): boolean; + emit(event: "SIGINT"): boolean; + emit(event: "SIGTSTP"): boolean; - on(event: string, listener: Function): this; - on(event: "close", listener: () => void): this; - on(event: "line", listener: (input: any) => void): this; - on(event: "pause", listener: () => void): this; - on(event: "resume", listener: () => void): this; - on(event: "SIGCONT", listener: () => void): this; - on(event: "SIGINT", listener: () => void): this; - on(event: "SIGTSTP", listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "line", listener: (input: any) => void): this; + on(event: "pause", listener: () => void): this; + on(event: "resume", listener: () => void): this; + on(event: "SIGCONT", listener: () => void): this; + on(event: "SIGINT", listener: () => void): this; + on(event: "SIGTSTP", listener: () => void): this; - once(event: string, listener: Function): this; - once(event: "close", listener: () => void): this; - once(event: "line", listener: (input: any) => void): this; - once(event: "pause", listener: () => void): this; - once(event: "resume", listener: () => void): this; - once(event: "SIGCONT", listener: () => void): this; - once(event: "SIGINT", listener: () => void): this; - once(event: "SIGTSTP", listener: () => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "line", listener: (input: any) => void): this; + once(event: "pause", listener: () => void): this; + once(event: "resume", listener: () => void): this; + once(event: "SIGCONT", listener: () => void): this; + once(event: "SIGINT", listener: () => void): this; + once(event: "SIGTSTP", listener: () => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "close", listener: () => void): this; - prependListener(event: "line", listener: (input: any) => void): this; - prependListener(event: "pause", listener: () => void): this; - prependListener(event: "resume", listener: () => void): this; - prependListener(event: "SIGCONT", listener: () => void): this; - prependListener(event: "SIGINT", listener: () => void): this; - prependListener(event: "SIGTSTP", listener: () => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "line", listener: (input: any) => void): this; + prependListener(event: "pause", listener: () => void): this; + prependListener(event: "resume", listener: () => void): this; + prependListener(event: "SIGCONT", listener: () => void): this; + prependListener(event: "SIGINT", listener: () => void): this; + prependListener(event: "SIGTSTP", listener: () => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "close", listener: () => void): this; - prependOnceListener(event: "line", listener: (input: any) => void): this; - prependOnceListener(event: "pause", listener: () => void): this; - prependOnceListener(event: "resume", listener: () => void): this; - prependOnceListener(event: "SIGCONT", listener: () => void): this; - prependOnceListener(event: "SIGINT", listener: () => void): this; - prependOnceListener(event: "SIGTSTP", listener: () => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "line", listener: (input: any) => void): this; + prependOnceListener(event: "pause", listener: () => void): this; + prependOnceListener(event: "resume", listener: () => void): this; + prependOnceListener(event: "SIGCONT", listener: () => void): this; + prependOnceListener(event: "SIGINT", listener: () => void): this; + prependOnceListener(event: "SIGTSTP", listener: () => void): this; + } - type Completer = (line: string) => CompleterResult; - type AsyncCompleter = (line: string, callback: (err: any, result: CompleterResult) => void) => any; + type Completer = (line: string) => CompleterResult; + type AsyncCompleter = (line: string, callback: (err: any, result: CompleterResult) => void) => any; - export type CompleterResult = [string[], string]; + export type CompleterResult = [string[], string]; - export interface ReadLineOptions { - input: NodeJS.ReadableStream; - output?: NodeJS.WritableStream; - completer?: Completer | AsyncCompleter; - terminal?: boolean; - historySize?: number; - prompt?: string; - crlfDelay?: number; - removeHistoryDuplicates?: boolean; - } + export interface ReadLineOptions { + input: NodeJS.ReadableStream; + output?: NodeJS.WritableStream; + completer?: Completer | AsyncCompleter; + terminal?: boolean; + historySize?: number; + prompt?: string; + crlfDelay?: number; + removeHistoryDuplicates?: boolean; + } - export function createInterface(input: NodeJS.ReadableStream, output?: NodeJS.WritableStream, completer?: Completer | AsyncCompleter, terminal?: boolean): ReadLine; - export function createInterface(options: ReadLineOptions): ReadLine; + export function createInterface(input: NodeJS.ReadableStream, output?: NodeJS.WritableStream, completer?: Completer | AsyncCompleter, terminal?: boolean): ReadLine; + export function createInterface(options: ReadLineOptions): ReadLine; - export function cursorTo(stream: NodeJS.WritableStream, x: number, y: number): void; - export function moveCursor(stream: NodeJS.WritableStream, dx: number | string, dy: number | string): void; - export function clearLine(stream: NodeJS.WritableStream, dir: number): void; - export function clearScreenDown(stream: NodeJS.WritableStream): void; + export function cursorTo(stream: NodeJS.WritableStream, x: number, y?: number): void; + export function emitKeypressEvents(stream: NodeJS.ReadableStream, interface?: ReadLine): void; + export function moveCursor(stream: NodeJS.WritableStream, dx: number | string, dy: number | string): void; + export function clearLine(stream: NodeJS.WritableStream, dir: number): void; + export function clearScreenDown(stream: NodeJS.WritableStream): void; } declare module "vm" { - export interface Context { } - export interface ScriptOptions { - filename?: string; - lineOffset?: number; - columnOffset?: number; - displayErrors?: boolean; - timeout?: number; - cachedData?: Buffer; - produceCachedData?: boolean; - } - export interface RunningScriptOptions { - filename?: string; - lineOffset?: number; - columnOffset?: number; - displayErrors?: boolean; - timeout?: number; - } - export class Script { - constructor(code: string, options?: ScriptOptions); - runInContext(contextifiedSandbox: Context, options?: RunningScriptOptions): any; - runInNewContext(sandbox?: Context, options?: RunningScriptOptions): any; - runInThisContext(options?: RunningScriptOptions): any; - } - export function createContext(sandbox?: Context): Context; - export function isContext(sandbox: Context): boolean; - export function runInContext(code: string, contextifiedSandbox: Context, options?: RunningScriptOptions): any; - export function runInDebugContext(code: string): any; - export function runInNewContext(code: string, sandbox?: Context, options?: RunningScriptOptions): any; - export function runInThisContext(code: string, options?: RunningScriptOptions): any; + export interface Context { } + export interface ScriptOptions { + filename?: string; + lineOffset?: number; + columnOffset?: number; + displayErrors?: boolean; + timeout?: number; + cachedData?: Buffer; + produceCachedData?: boolean; + } + export interface RunningScriptOptions { + filename?: string; + lineOffset?: number; + columnOffset?: number; + displayErrors?: boolean; + timeout?: number; + } + export class Script { + constructor(code: string, options?: ScriptOptions); + runInContext(contextifiedSandbox: Context, options?: RunningScriptOptions): any; + runInNewContext(sandbox?: Context, options?: RunningScriptOptions): any; + runInThisContext(options?: RunningScriptOptions): any; + } + export function createContext(sandbox?: Context): Context; + export function isContext(sandbox: Context): boolean; + export function runInContext(code: string, contextifiedSandbox: Context, options?: RunningScriptOptions | string): any; + export function runInDebugContext(code: string): any; + export function runInNewContext(code: string, sandbox?: Context, options?: RunningScriptOptions | string): any; + export function runInThisContext(code: string, options?: RunningScriptOptions | string): any; } declare module "child_process" { - import * as events from "events"; - import * as stream from "stream"; - import * as net from "net"; + import * as events from "events"; + import * as stream from "stream"; + import * as net from "net"; - export interface ChildProcess extends events.EventEmitter { - stdin: stream.Writable; - stdout: stream.Readable; - stderr: stream.Readable; - stdio: [stream.Writable, stream.Readable, stream.Readable]; - killed: boolean; - pid: number; - kill(signal?: string): void; - send(message: any, sendHandle?: any): boolean; - connected: boolean; - disconnect(): void; - unref(): void; - ref(): void; + export interface ChildProcess extends events.EventEmitter { + stdin: stream.Writable; + stdout: stream.Readable; + stderr: stream.Readable; + stdio: [stream.Writable, stream.Readable, stream.Readable]; + killed: boolean; + pid: number; + kill(signal?: string): void; + send(message: any, callback?: (error: Error) => void): boolean; + send(message: any, sendHandle?: net.Socket | net.Server, callback?: (error: Error) => void): boolean; + send(message: any, sendHandle?: net.Socket | net.Server, options?: MessageOptions, callback?: (error: Error) => void): boolean; + connected: boolean; + disconnect(): void; + unref(): void; + ref(): void; /** * events.EventEmitter @@ -1667,464 +2043,625 @@ declare module "child_process" { * 3. error * 4. exit * 5. message - **/ + */ - addListener(event: string, listener: Function): this; - addListener(event: "close", listener: (code: number, signal: string) => void): this; - addListener(event: "disconnect", listener: () => void): this; - addListener(event: "error", listener: (err: Error) => void): this; - addListener(event: "exit", listener: (code: number, signal: string) => void): this; - addListener(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: (code: number, signal: string) => void): this; + addListener(event: "disconnect", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "exit", listener: (code: number, signal: string) => void): this; + addListener(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "close", code: number, signal: string): boolean; - emit(event: "disconnect"): boolean; - emit(event: "error", err: Error): boolean; - emit(event: "exit", code: number, signal: string): boolean; - emit(event: "message", message: any, sendHandle: net.Socket | net.Server): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close", code: number, signal: string): boolean; + emit(event: "disconnect"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "exit", code: number, signal: string): boolean; + emit(event: "message", message: any, sendHandle: net.Socket | net.Server): boolean; - on(event: string, listener: Function): this; - on(event: "close", listener: (code: number, signal: string) => void): this; - on(event: "disconnect", listener: () => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "exit", listener: (code: number, signal: string) => void): this; - on(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: (code: number, signal: string) => void): this; + on(event: "disconnect", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "exit", listener: (code: number, signal: string) => void): this; + on(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; - once(event: string, listener: Function): this; - once(event: "close", listener: (code: number, signal: string) => void): this; - once(event: "disconnect", listener: () => void): this; - once(event: "error", listener: (err: Error) => void): this; - once(event: "exit", listener: (code: number, signal: string) => void): this; - once(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: (code: number, signal: string) => void): this; + once(event: "disconnect", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "exit", listener: (code: number, signal: string) => void): this; + once(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "close", listener: (code: number, signal: string) => void): this; - prependListener(event: "disconnect", listener: () => void): this; - prependListener(event: "error", listener: (err: Error) => void): this; - prependListener(event: "exit", listener: (code: number, signal: string) => void): this; - prependListener(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: (code: number, signal: string) => void): this; + prependListener(event: "disconnect", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "exit", listener: (code: number, signal: string) => void): this; + prependListener(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "close", listener: (code: number, signal: string) => void): this; - prependOnceListener(event: "disconnect", listener: () => void): this; - prependOnceListener(event: "error", listener: (err: Error) => void): this; - prependOnceListener(event: "exit", listener: (code: number, signal: string) => void): this; - prependOnceListener(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: (code: number, signal: string) => void): this; + prependOnceListener(event: "disconnect", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "exit", listener: (code: number, signal: string) => void): this; + prependOnceListener(event: "message", listener: (message: any, sendHandle: net.Socket | net.Server) => void): this; + } - export interface SpawnOptions { - cwd?: string; - env?: any; - stdio?: any; - detached?: boolean; - uid?: number; - gid?: number; - shell?: boolean | string; - windowsVerbatimArguments?: boolean; - } - export function spawn(command: string, args?: string[], options?: SpawnOptions): ChildProcess; + export interface MessageOptions { + keepOpen?: boolean; + } - export interface ExecOptions { - cwd?: string; - env?: any; - shell?: string; - timeout?: number; - maxBuffer?: number; - killSignal?: string; - uid?: number; - gid?: number; - } - export interface ExecOptionsWithStringEncoding extends ExecOptions { - encoding: BufferEncoding; - } - export interface ExecOptionsWithBufferEncoding extends ExecOptions { - encoding: string; // specify `null`. - } - export function exec(command: string, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; - export function exec(command: string, options: ExecOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; - // usage. child_process.exec("tsc", {encoding: null as string}, (err, stdout, stderr) => {}); - export function exec(command: string, options: ExecOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; - export function exec(command: string, options: ExecOptions, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; + export interface SpawnOptions { + cwd?: string; + env?: any; + stdio?: any; + detached?: boolean; + uid?: number; + gid?: number; + shell?: boolean | string; + windowsVerbatimArguments?: boolean; + windowsHide?: boolean; + } - export interface ExecFileOptions { - cwd?: string; - env?: any; - timeout?: number; - maxBuffer?: number; - killSignal?: string; - uid?: number; - gid?: number; - } - export interface ExecFileOptionsWithStringEncoding extends ExecFileOptions { - encoding: BufferEncoding; - } - export interface ExecFileOptionsWithBufferEncoding extends ExecFileOptions { - encoding: string; // specify `null`. - } - export function execFile(file: string, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; - export function execFile(file: string, options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; - // usage. child_process.execFile("file.sh", {encoding: null as string}, (err, stdout, stderr) => {}); - export function execFile(file: string, options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; - export function execFile(file: string, options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; - export function execFile(file: string, args?: string[], callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; - export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; - // usage. child_process.execFile("file.sh", ["foo"], {encoding: null as string}, (err, stdout, stderr) => {}); - export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; - export function execFile(file: string, args?: string[], options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess; + export function spawn(command: string, args?: ReadonlyArray<string>, options?: SpawnOptions): ChildProcess; - export interface ForkOptions { - cwd?: string; - env?: any; - execPath?: string; - execArgv?: string[]; - silent?: boolean; - stdio?: any[]; - uid?: number; - gid?: number; - } - export function fork(modulePath: string, args?: string[], options?: ForkOptions): ChildProcess; + export interface ExecOptions { + cwd?: string; + env?: any; + shell?: string; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + uid?: number; + gid?: number; + windowsHide?: boolean; + } - export interface SpawnSyncOptions { - cwd?: string; - input?: string | Buffer; - stdio?: any; - env?: any; - uid?: number; - gid?: number; - timeout?: number; - killSignal?: string; - maxBuffer?: number; - encoding?: string; - shell?: boolean | string; - } - export interface SpawnSyncOptionsWithStringEncoding extends SpawnSyncOptions { - encoding: BufferEncoding; - } - export interface SpawnSyncOptionsWithBufferEncoding extends SpawnSyncOptions { - encoding: string; // specify `null`. - } - export interface SpawnSyncReturns<T> { - pid: number; - output: string[]; - stdout: T; - stderr: T; - status: number; - signal: string; - error: Error; - } - export function spawnSync(command: string): SpawnSyncReturns<Buffer>; - export function spawnSync(command: string, options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>; - export function spawnSync(command: string, options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns<Buffer>; - export function spawnSync(command: string, options?: SpawnSyncOptions): SpawnSyncReturns<Buffer>; - export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>; - export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns<Buffer>; - export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptions): SpawnSyncReturns<Buffer>; + export interface ExecOptionsWithStringEncoding extends ExecOptions { + encoding: BufferEncoding; + } - export interface ExecSyncOptions { - cwd?: string; - input?: string | Buffer; - stdio?: any; - env?: any; - shell?: string; - uid?: number; - gid?: number; - timeout?: number; - killSignal?: string; - maxBuffer?: number; - encoding?: string; - } - export interface ExecSyncOptionsWithStringEncoding extends ExecSyncOptions { - encoding: BufferEncoding; - } - export interface ExecSyncOptionsWithBufferEncoding extends ExecSyncOptions { - encoding: string; // specify `null`. - } - export function execSync(command: string): Buffer; - export function execSync(command: string, options?: ExecSyncOptionsWithStringEncoding): string; - export function execSync(command: string, options?: ExecSyncOptionsWithBufferEncoding): Buffer; - export function execSync(command: string, options?: ExecSyncOptions): Buffer; + export interface ExecOptionsWithBufferEncoding extends ExecOptions { + encoding: string | null; // specify `null`. + } - export interface ExecFileSyncOptions { - cwd?: string; - input?: string | Buffer; - stdio?: any; - env?: any; - uid?: number; - gid?: number; - timeout?: number; - killSignal?: string; - maxBuffer?: number; - encoding?: string; - } - export interface ExecFileSyncOptionsWithStringEncoding extends ExecFileSyncOptions { - encoding: BufferEncoding; - } - export interface ExecFileSyncOptionsWithBufferEncoding extends ExecFileSyncOptions { - encoding: string; // specify `null`. - } - export function execFileSync(command: string): Buffer; - export function execFileSync(command: string, options?: ExecFileSyncOptionsWithStringEncoding): string; - export function execFileSync(command: string, options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; - export function execFileSync(command: string, options?: ExecFileSyncOptions): Buffer; - export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithStringEncoding): string; - export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; - export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptions): Buffer; + // no `options` definitely means stdout/stderr are `string`. + export function exec(command: string, callback?: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + + // `options` with `"buffer"` or `null` for `encoding` means stdout/stderr are definitely `Buffer`. + export function exec(command: string, options: { encoding: "buffer" | null } & ExecOptions, callback?: (error: Error | null, stdout: Buffer, stderr: Buffer) => void): ChildProcess; + + // `options` with well known `encoding` means stdout/stderr are definitely `string`. + export function exec(command: string, options: { encoding: BufferEncoding } & ExecOptions, callback?: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + + // `options` with an `encoding` whose type is `string` means stdout/stderr could either be `Buffer` or `string`. + // There is no guarantee the `encoding` is unknown as `string` is a superset of `BufferEncoding`. + export function exec(command: string, options: { encoding: string } & ExecOptions, callback?: (error: Error | null, stdout: string | Buffer, stderr: string | Buffer) => void): ChildProcess; + + // `options` without an `encoding` means stdout/stderr are definitely `string`. + export function exec(command: string, options: ExecOptions, callback?: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + + // fallback if nothing else matches. Worst case is always `string | Buffer`. + export function exec(command: string, options: ({ encoding?: string | null } & ExecOptions) | undefined | null, callback?: (error: Error | null, stdout: string | Buffer, stderr: string | Buffer) => void): ChildProcess; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace exec { + export function __promisify__(command: string): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(command: string, options: { encoding: "buffer" | null } & ExecOptions): Promise<{ stdout: Buffer, stderr: Buffer }>; + export function __promisify__(command: string, options: { encoding: BufferEncoding } & ExecOptions): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(command: string, options: ExecOptions): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(command: string, options?: ({ encoding?: string | null } & ExecOptions) | null): Promise<{ stdout: string | Buffer, stderr: string | Buffer }>; + } + + export interface ExecFileOptions { + cwd?: string; + env?: any; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + uid?: number; + gid?: number; + windowsHide?: boolean; + windowsVerbatimArguments?: boolean; + } + export interface ExecFileOptionsWithStringEncoding extends ExecFileOptions { + encoding: BufferEncoding; + } + export interface ExecFileOptionsWithBufferEncoding extends ExecFileOptions { + encoding: 'buffer' | null; + } + export interface ExecFileOptionsWithOtherEncoding extends ExecFileOptions { + encoding: string; + } + + export function execFile(file: string): ChildProcess; + export function execFile(file: string, options: ({ encoding?: string | null } & ExecFileOptions) | undefined | null): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null, options: ({ encoding?: string | null } & ExecFileOptions) | undefined | null): ChildProcess; + + // no `options` definitely means stdout/stderr are `string`. + export function execFile(file: string, callback: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null, callback: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + + // `options` with `"buffer"` or `null` for `encoding` means stdout/stderr are definitely `Buffer`. + export function execFile(file: string, options: ExecFileOptionsWithBufferEncoding, callback: (error: Error | null, stdout: Buffer, stderr: Buffer) => void): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null, options: ExecFileOptionsWithBufferEncoding, callback: (error: Error | null, stdout: Buffer, stderr: Buffer) => void): ChildProcess; + + // `options` with well known `encoding` means stdout/stderr are definitely `string`. + export function execFile(file: string, options: ExecFileOptionsWithStringEncoding, callback: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null, options: ExecFileOptionsWithStringEncoding, callback: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + + // `options` with an `encoding` whose type is `string` means stdout/stderr could either be `Buffer` or `string`. + // There is no guarantee the `encoding` is unknown as `string` is a superset of `BufferEncoding`. + export function execFile(file: string, options: ExecFileOptionsWithOtherEncoding, callback: (error: Error | null, stdout: string | Buffer, stderr: string | Buffer) => void): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null, options: ExecFileOptionsWithOtherEncoding, callback: (error: Error | null, stdout: string | Buffer, stderr: string | Buffer) => void): ChildProcess; + + // `options` without an `encoding` means stdout/stderr are definitely `string`. + export function execFile(file: string, options: ExecFileOptions, callback: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null, options: ExecFileOptions, callback: (error: Error | null, stdout: string, stderr: string) => void): ChildProcess; + + // fallback if nothing else matches. Worst case is always `string | Buffer`. + export function execFile(file: string, options: ({ encoding?: string | null } & ExecFileOptions) | undefined | null, callback: ((error: Error | null, stdout: string | Buffer, stderr: string | Buffer) => void) | undefined | null): ChildProcess; + export function execFile(file: string, args: string[] | undefined | null, options: ({ encoding?: string | null } & ExecFileOptions) | undefined | null, callback: ((error: Error | null, stdout: string | Buffer, stderr: string | Buffer) => void) | undefined | null): ChildProcess; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace execFile { + export function __promisify__(file: string): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(file: string, args: string[] | undefined | null): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(file: string, options: ExecFileOptionsWithBufferEncoding): Promise<{ stdout: Buffer, stderr: Buffer }>; + export function __promisify__(file: string, args: string[] | undefined | null, options: ExecFileOptionsWithBufferEncoding): Promise<{ stdout: Buffer, stderr: Buffer }>; + export function __promisify__(file: string, options: ExecFileOptionsWithStringEncoding): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(file: string, args: string[] | undefined | null, options: ExecFileOptionsWithStringEncoding): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(file: string, options: ExecFileOptionsWithOtherEncoding): Promise<{ stdout: string | Buffer, stderr: string | Buffer }>; + export function __promisify__(file: string, args: string[] | undefined | null, options: ExecFileOptionsWithOtherEncoding): Promise<{ stdout: string | Buffer, stderr: string | Buffer }>; + export function __promisify__(file: string, options: ExecFileOptions): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(file: string, args: string[] | undefined | null, options: ExecFileOptions): Promise<{ stdout: string, stderr: string }>; + export function __promisify__(file: string, options: ({ encoding?: string | null } & ExecFileOptions) | undefined | null): Promise<{ stdout: string | Buffer, stderr: string | Buffer }>; + export function __promisify__(file: string, args: string[] | undefined | null, options: ({ encoding?: string | null } & ExecFileOptions) | undefined | null): Promise<{ stdout: string | Buffer, stderr: string | Buffer }>; + } + + export interface ForkOptions { + cwd?: string; + env?: any; + execPath?: string; + execArgv?: string[]; + silent?: boolean; + stdio?: any[]; + uid?: number; + gid?: number; + windowsVerbatimArguments?: boolean; + } + export function fork(modulePath: string, args?: string[], options?: ForkOptions): ChildProcess; + + export interface SpawnSyncOptions { + cwd?: string; + input?: string | Buffer; + stdio?: any; + env?: any; + uid?: number; + gid?: number; + timeout?: number; + killSignal?: string; + maxBuffer?: number; + encoding?: string; + shell?: boolean | string; + windowsHide?: boolean; + windowsVerbatimArguments?: boolean; + } + export interface SpawnSyncOptionsWithStringEncoding extends SpawnSyncOptions { + encoding: BufferEncoding; + } + export interface SpawnSyncOptionsWithBufferEncoding extends SpawnSyncOptions { + encoding: string; // specify `null`. + } + export interface SpawnSyncReturns<T> { + pid: number; + output: string[]; + stdout: T; + stderr: T; + status: number; + signal: string; + error: Error; + } + export function spawnSync(command: string): SpawnSyncReturns<Buffer>; + export function spawnSync(command: string, options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>; + export function spawnSync(command: string, options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns<Buffer>; + export function spawnSync(command: string, options?: SpawnSyncOptions): SpawnSyncReturns<Buffer>; + export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>; + export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns<Buffer>; + export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptions): SpawnSyncReturns<Buffer>; + + export interface ExecSyncOptions { + cwd?: string; + input?: string | Buffer; + stdio?: any; + env?: any; + shell?: string; + uid?: number; + gid?: number; + timeout?: number; + killSignal?: string; + maxBuffer?: number; + encoding?: string; + windowsHide?: boolean; + } + export interface ExecSyncOptionsWithStringEncoding extends ExecSyncOptions { + encoding: BufferEncoding; + } + export interface ExecSyncOptionsWithBufferEncoding extends ExecSyncOptions { + encoding: string; // specify `null`. + } + export function execSync(command: string): Buffer; + export function execSync(command: string, options?: ExecSyncOptionsWithStringEncoding): string; + export function execSync(command: string, options?: ExecSyncOptionsWithBufferEncoding): Buffer; + export function execSync(command: string, options?: ExecSyncOptions): Buffer; + + export interface ExecFileSyncOptions { + cwd?: string; + input?: string | Buffer; + stdio?: any; + env?: any; + uid?: number; + gid?: number; + timeout?: number; + killSignal?: string; + maxBuffer?: number; + encoding?: string; + windowsHide?: boolean; + } + export interface ExecFileSyncOptionsWithStringEncoding extends ExecFileSyncOptions { + encoding: BufferEncoding; + } + export interface ExecFileSyncOptionsWithBufferEncoding extends ExecFileSyncOptions { + encoding: string; // specify `null`. + } + export function execFileSync(command: string): Buffer; + export function execFileSync(command: string, options?: ExecFileSyncOptionsWithStringEncoding): string; + export function execFileSync(command: string, options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; + export function execFileSync(command: string, options?: ExecFileSyncOptions): Buffer; + export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithStringEncoding): string; + export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithBufferEncoding): Buffer; + export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptions): Buffer; } declare module "url" { - export interface Url { - href?: string; - protocol?: string; - auth?: string; - hostname?: string; - port?: string; - host?: string; - pathname?: string; - search?: string; - query?: string | any; - slashes?: boolean; - hash?: string; - path?: string; - } + import { ParsedUrlQuery } from 'querystring'; - export interface UrlObject { - protocol?: string; - slashes?: boolean; - auth?: string; - host?: string; - hostname?: string; - port?: string | number; - pathname?: string; - search?: string; - query?: { [key: string]: any; }; - hash?: string; - } + export interface UrlObjectCommon { + auth?: string; + hash?: string; + host?: string; + hostname?: string; + href?: string; + path?: string; + pathname?: string; + protocol?: string; + search?: string; + slashes?: boolean; + } - export function parse(urlStr: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url; - export function format(URL: URL, options?: URLFormatOptions): string; - export function format(urlObject: UrlObject): string; - export function resolve(from: string, to: string): string; + // Input to `url.format` + export interface UrlObject extends UrlObjectCommon { + port?: string | number; + query?: string | null | { [key: string]: any }; + } - export interface URLFormatOptions { - auth?: boolean; - fragment?: boolean; - search?: boolean; - unicode?: boolean; - } + // Output of `url.parse` + export interface Url extends UrlObjectCommon { + port?: string; + query?: string | null | ParsedUrlQuery; + } - export class URLSearchParams implements Iterable<string[]> { - constructor(init?: URLSearchParams | string | { [key: string]: string | string[] } | Iterable<string[]>); - append(name: string, value: string): void; - delete(name: string): void; - entries(): Iterator<string[]>; - forEach(callback: (value: string, name: string) => void): void; - get(name: string): string | null; - getAll(name: string): string[]; - has(name: string): boolean; - keys(): Iterator<string>; - set(name: string, value: string): void; - sort(): void; - toString(): string; - values(): Iterator<string>; - [Symbol.iterator](): Iterator<string[]>; - } + export interface UrlWithParsedQuery extends Url { + query: ParsedUrlQuery; + } - export class URL { - constructor(input: string, base?: string | URL); - hash: string; - host: string; - hostname: string; - href: string; - readonly origin: string; - password: string; - pathname: string; - port: string; - protocol: string; - search: string; - readonly searchParams: URLSearchParams; - username: string; - toString(): string; - toJSON(): string; - } + export interface UrlWithStringQuery extends Url { + query: string | null; + } + + export function parse(urlStr: string): UrlWithStringQuery; + export function parse(urlStr: string, parseQueryString: false | undefined, slashesDenoteHost?: boolean): UrlWithStringQuery; + export function parse(urlStr: string, parseQueryString: true, slashesDenoteHost?: boolean): UrlWithParsedQuery; + export function parse(urlStr: string, parseQueryString: boolean, slashesDenoteHost?: boolean): Url; + + export function format(URL: URL, options?: URLFormatOptions): string; + export function format(urlObject: UrlObject | string): string; + export function resolve(from: string, to: string): string; + + export function domainToASCII(domain: string): string; + export function domainToUnicode(domain: string): string; + + export interface URLFormatOptions { + auth?: boolean; + fragment?: boolean; + search?: boolean; + unicode?: boolean; + } + + export class URLSearchParams implements Iterable<[string, string]> { + constructor(init?: URLSearchParams | string | { [key: string]: string | string[] | undefined } | Iterable<[string, string]> | Array<[string, string]>); + append(name: string, value: string): void; + delete(name: string): void; + entries(): IterableIterator<[string, string]>; + forEach(callback: (value: string, name: string) => void): void; + get(name: string): string | null; + getAll(name: string): string[]; + has(name: string): boolean; + keys(): IterableIterator<string>; + set(name: string, value: string): void; + sort(): void; + toString(): string; + values(): IterableIterator<string>; + [Symbol.iterator](): IterableIterator<[string, string]>; + } + + export class URL { + constructor(input: string, base?: string | URL); + hash: string; + host: string; + hostname: string; + href: string; + readonly origin: string; + password: string; + pathname: string; + port: string; + protocol: string; + search: string; + readonly searchParams: URLSearchParams; + username: string; + toString(): string; + toJSON(): string; + } } declare module "dns" { - // Supported getaddrinfo flags. - export const ADDRCONFIG: number; - export const V4MAPPED: number; + // Supported getaddrinfo flags. + export const ADDRCONFIG: number; + export const V4MAPPED: number; - export interface LookupOptions { - family?: number; - hints?: number; - all?: boolean; - } + export interface LookupOptions { + family?: number; + hints?: number; + all?: boolean; + } - export interface LookupOneOptions extends LookupOptions { - all?: false; - } + export interface LookupOneOptions extends LookupOptions { + all?: false; + } - export interface LookupAllOptions extends LookupOptions { - all: true; - } + export interface LookupAllOptions extends LookupOptions { + all: true; + } - export interface LookupAddress { - address: string; - family: number; - } + export interface LookupAddress { + address: string; + family: number; + } - export function lookup(hostname: string, family: number, callback: (err: NodeJS.ErrnoException, address: string, family: number) => void): void; - export function lookup(hostname: string, options: LookupOneOptions, callback: (err: NodeJS.ErrnoException, address: string, family: number) => void): void; - export function lookup(hostname: string, options: LookupAllOptions, callback: (err: NodeJS.ErrnoException, addresses: LookupAddress[]) => void): void; - export function lookup(hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException, address: string | LookupAddress[], family: number) => void): void; - export function lookup(hostname: string, callback: (err: NodeJS.ErrnoException, address: string, family: number) => void): void; + export function lookup(hostname: string, family: number, callback: (err: NodeJS.ErrnoException, address: string, family: number) => void): void; + export function lookup(hostname: string, options: LookupOneOptions, callback: (err: NodeJS.ErrnoException, address: string, family: number) => void): void; + export function lookup(hostname: string, options: LookupAllOptions, callback: (err: NodeJS.ErrnoException, addresses: LookupAddress[]) => void): void; + export function lookup(hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException, address: string | LookupAddress[], family: number) => void): void; + export function lookup(hostname: string, callback: (err: NodeJS.ErrnoException, address: string, family: number) => void): void; - export interface ResolveOptions { - ttl: boolean; - } + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace lookup { + export function __promisify__(hostname: string, options: LookupAllOptions): Promise<{ address: LookupAddress[] }>; + export function __promisify__(hostname: string, options?: LookupOneOptions | number): Promise<{ address: string, family: number }>; + export function __promisify__(hostname: string, options?: LookupOptions | number): Promise<{ address: string | LookupAddress[], family?: number }>; + } - export interface ResolveWithTtlOptions extends ResolveOptions { - ttl: true; - } + export function lookupService(address: string, port: number, callback: (err: NodeJS.ErrnoException, hostname: string, service: string) => void): void; - export interface RecordWithTtl { - address: string; - ttl: number; - } + export namespace lookupService { + export function __promisify__(address: string, port: number): Promise<{ hostname: string, service: string }>; + } - export interface MxRecord { - priority: number; - exchange: string; - } + export interface ResolveOptions { + ttl: boolean; + } - export interface NaptrRecord { - flags: string; - service: string; - regexp: string; - replacement: string; - order: number; - preference: number; - } + export interface ResolveWithTtlOptions extends ResolveOptions { + ttl: true; + } - export interface SoaRecord { - nsname: string; - hostmaster: string; - serial: number; - refresh: number; - retry: number; - expire: number; - minttl: number; - } + export interface RecordWithTtl { + address: string; + ttl: number; + } - export interface SrvRecord { - priority: number; - weight: number; - port: number; - name: string; - } + export interface MxRecord { + priority: number; + exchange: string; + } - export function resolve(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve(hostname: string, rrtype: "A", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve(hostname: string, rrtype: "AAAA", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve(hostname: string, rrtype: "CNAME", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve(hostname: string, rrtype: "MX", callback: (err: NodeJS.ErrnoException, addresses: MxRecord[]) => void): void; - export function resolve(hostname: string, rrtype: "NAPTR", callback: (err: NodeJS.ErrnoException, addresses: NaptrRecord[]) => void): void; - export function resolve(hostname: string, rrtype: "NS", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve(hostname: string, rrtype: "PTR", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve(hostname: string, rrtype: "SOA", callback: (err: NodeJS.ErrnoException, addresses: SoaRecord) => void): void; - export function resolve(hostname: string, rrtype: "SRV", callback: (err: NodeJS.ErrnoException, addresses: SrvRecord[]) => void): void; - export function resolve(hostname: string, rrtype: "TXT", callback: (err: NodeJS.ErrnoException, addresses: string[][]) => void): void; - export function resolve(hostname: string, rrtype: string, callback: (err: NodeJS.ErrnoException, addresses: string[] | MxRecord[] | NaptrRecord[] | SoaRecord | SrvRecord[] | string[][]) => void): void; + export interface NaptrRecord { + flags: string; + service: string; + regexp: string; + replacement: string; + order: number; + preference: number; + } - export function resolve4(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve4(hostname: string, options: ResolveWithTtlOptions, callback: (err: NodeJS.ErrnoException, addresses: RecordWithTtl[]) => void): void; - export function resolve4(hostname: string, options: ResolveOptions, callback: (err: NodeJS.ErrnoException, addresses: string[] | RecordWithTtl[]) => void): void; + export interface SoaRecord { + nsname: string; + hostmaster: string; + serial: number; + refresh: number; + retry: number; + expire: number; + minttl: number; + } - export function resolve6(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolve6(hostname: string, options: ResolveWithTtlOptions, callback: (err: NodeJS.ErrnoException, addresses: RecordWithTtl[]) => void): void; - export function resolve6(hostname: string, options: ResolveOptions, callback: (err: NodeJS.ErrnoException, addresses: string[] | RecordWithTtl[]) => void): void; + export interface SrvRecord { + priority: number; + weight: number; + port: number; + name: string; + } - export function resolveCname(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolveMx(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: MxRecord[]) => void): void; - export function resolveNaptr(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: NaptrRecord[]) => void): void; - export function resolveNs(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolvePtr(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; - export function resolveSoa(hostname: string, callback: (err: NodeJS.ErrnoException, address: SoaRecord) => void): void; - export function resolveSrv(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: SrvRecord[]) => void): void; - export function resolveTxt(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[][]) => void): void; + export function resolve(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve(hostname: string, rrtype: "A", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve(hostname: string, rrtype: "AAAA", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve(hostname: string, rrtype: "CNAME", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve(hostname: string, rrtype: "MX", callback: (err: NodeJS.ErrnoException, addresses: MxRecord[]) => void): void; + export function resolve(hostname: string, rrtype: "NAPTR", callback: (err: NodeJS.ErrnoException, addresses: NaptrRecord[]) => void): void; + export function resolve(hostname: string, rrtype: "NS", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve(hostname: string, rrtype: "PTR", callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve(hostname: string, rrtype: "SOA", callback: (err: NodeJS.ErrnoException, addresses: SoaRecord) => void): void; + export function resolve(hostname: string, rrtype: "SRV", callback: (err: NodeJS.ErrnoException, addresses: SrvRecord[]) => void): void; + export function resolve(hostname: string, rrtype: "TXT", callback: (err: NodeJS.ErrnoException, addresses: string[][]) => void): void; + export function resolve(hostname: string, rrtype: string, callback: (err: NodeJS.ErrnoException, addresses: string[] | MxRecord[] | NaptrRecord[] | SoaRecord | SrvRecord[] | string[][]) => void): void; - export function reverse(ip: string, callback: (err: NodeJS.ErrnoException, hostnames: string[]) => void): void; - export function setServers(servers: string[]): void; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace resolve { + export function __promisify__(hostname: string, rrtype?: "A" | "AAAA" | "CNAME" | "NS" | "PTR"): Promise<string[]>; + export function __promisify__(hostname: string, rrtype: "MX"): Promise<MxRecord[]>; + export function __promisify__(hostname: string, rrtype: "NAPTR"): Promise<NaptrRecord[]>; + export function __promisify__(hostname: string, rrtype: "SOA"): Promise<SoaRecord>; + export function __promisify__(hostname: string, rrtype: "SRV"): Promise<SrvRecord[]>; + export function __promisify__(hostname: string, rrtype: "TXT"): Promise<string[][]>; + export function __promisify__(hostname: string, rrtype?: string): Promise<string[] | MxRecord[] | NaptrRecord[] | SoaRecord | SrvRecord[] | string[][]>; + } - //Error codes - export var NODATA: string; - export var FORMERR: string; - export var SERVFAIL: string; - export var NOTFOUND: string; - export var NOTIMP: string; - export var REFUSED: string; - export var BADQUERY: string; - export var BADNAME: string; - export var BADFAMILY: string; - export var BADRESP: string; - export var CONNREFUSED: string; - export var TIMEOUT: string; - export var EOF: string; - export var FILE: string; - export var NOMEM: string; - export var DESTRUCTION: string; - export var BADSTR: string; - export var BADFLAGS: string; - export var NONAME: string; - export var BADHINTS: string; - export var NOTINITIALIZED: string; - export var LOADIPHLPAPI: string; - export var ADDRGETNETWORKPARAMS: string; - export var CANCELLED: string; + export function resolve4(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve4(hostname: string, options: ResolveWithTtlOptions, callback: (err: NodeJS.ErrnoException, addresses: RecordWithTtl[]) => void): void; + export function resolve4(hostname: string, options: ResolveOptions, callback: (err: NodeJS.ErrnoException, addresses: string[] | RecordWithTtl[]) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace resolve4 { + export function __promisify__(hostname: string): Promise<string[]>; + export function __promisify__(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>; + export function __promisify__(hostname: string, options?: ResolveOptions): Promise<string[] | RecordWithTtl[]>; + } + + export function resolve6(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolve6(hostname: string, options: ResolveWithTtlOptions, callback: (err: NodeJS.ErrnoException, addresses: RecordWithTtl[]) => void): void; + export function resolve6(hostname: string, options: ResolveOptions, callback: (err: NodeJS.ErrnoException, addresses: string[] | RecordWithTtl[]) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace resolve6 { + export function __promisify__(hostname: string): Promise<string[]>; + export function __promisify__(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>; + export function __promisify__(hostname: string, options?: ResolveOptions): Promise<string[] | RecordWithTtl[]>; + } + + export function resolveCname(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolveMx(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: MxRecord[]) => void): void; + export function resolveNaptr(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: NaptrRecord[]) => void): void; + export function resolveNs(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolvePtr(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[]) => void): void; + export function resolveSoa(hostname: string, callback: (err: NodeJS.ErrnoException, address: SoaRecord) => void): void; + export function resolveSrv(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: SrvRecord[]) => void): void; + export function resolveTxt(hostname: string, callback: (err: NodeJS.ErrnoException, addresses: string[][]) => void): void; + + export function reverse(ip: string, callback: (err: NodeJS.ErrnoException, hostnames: string[]) => void): void; + export function setServers(servers: string[]): void; + + // Error codes + export var NODATA: string; + export var FORMERR: string; + export var SERVFAIL: string; + export var NOTFOUND: string; + export var NOTIMP: string; + export var REFUSED: string; + export var BADQUERY: string; + export var BADNAME: string; + export var BADFAMILY: string; + export var BADRESP: string; + export var CONNREFUSED: string; + export var TIMEOUT: string; + export var EOF: string; + export var FILE: string; + export var NOMEM: string; + export var DESTRUCTION: string; + export var BADSTR: string; + export var BADFLAGS: string; + export var NONAME: string; + export var BADHINTS: string; + export var NOTINITIALIZED: string; + export var LOADIPHLPAPI: string; + export var ADDRGETNETWORKPARAMS: string; + export var CANCELLED: string; } declare module "net" { - import * as stream from "stream"; - import * as events from "events"; + import * as stream from "stream"; + import * as events from "events"; + import * as dns from "dns"; - export interface Socket extends stream.Duplex { - // Extended base methods - write(buffer: Buffer): boolean; - write(buffer: Buffer, cb?: Function): boolean; - write(str: string, cb?: Function): boolean; - write(str: string, encoding?: string, cb?: Function): boolean; - write(str: string, encoding?: string, fd?: string): boolean; + type LookupFunction = (hostname: string, options: dns.LookupOneOptions, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void) => void; - connect(port: number, host?: string, connectionListener?: Function): void; - connect(path: string, connectionListener?: Function): void; - bufferSize: number; - setEncoding(encoding?: string): this; - write(data: any, encoding?: string, callback?: Function): void; - destroy(err?: any): void; - pause(): this; - resume(): this; - setTimeout(timeout: number, callback?: Function): void; - setNoDelay(noDelay?: boolean): void; - setKeepAlive(enable?: boolean, initialDelay?: number): void; - address(): { port: number; family: string; address: string; }; - unref(): void; - ref(): void; + export interface SocketConstructorOpts { + fd?: number; + allowHalfOpen?: boolean; + readable?: boolean; + writable?: boolean; + } - remoteAddress: string; - remoteFamily: string; - remotePort: number; - localAddress: string; - localPort: number; - bytesRead: number; - bytesWritten: number; - connecting: boolean; - destroyed: boolean; + export interface TcpSocketConnectOpts { + port: number; + host?: string; + localAddress?: string; + localPort?: number; + hints?: number; + family?: number; + lookup?: LookupFunction; + } - // Extended base methods - end(): void; - end(buffer: Buffer, cb?: Function): void; - end(str: string, cb?: Function): void; - end(str: string, encoding?: string, cb?: Function): void; - end(data?: any, encoding?: string): void; + export interface IpcSocketConnectOpts { + path: string; + } + + export type SocketConnectOpts = TcpSocketConnectOpts | IpcSocketConnectOpts; + + export class Socket extends stream.Duplex { + constructor(options?: SocketConstructorOpts); + + // Extended base methods + write(buffer: Buffer): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + write(str: string, encoding?: string, fd?: string): boolean; + write(data: any, encoding?: string, callback?: Function): void; + + connect(options: SocketConnectOpts, connectionListener?: Function): this; + connect(port: number, host: string, connectionListener?: Function): this; + connect(port: number, connectionListener?: Function): this; + connect(path: string, connectionListener?: Function): this; + + bufferSize: number; + setEncoding(encoding?: string): this; + destroy(err?: any): void; + pause(): this; + resume(): this; + setTimeout(timeout: number, callback?: Function): this; + setNoDelay(noDelay?: boolean): this; + setKeepAlive(enable?: boolean, initialDelay?: number): this; + address(): { port: number; family: string; address: string; }; + unref(): void; + ref(): void; + + remoteAddress?: string; + remoteFamily?: string; + remotePort?: number; + localAddress: string; + localPort: number; + bytesRead: number; + bytesWritten: number; + connecting: boolean; + destroyed: boolean; + + // Extended base methods + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + end(data?: any, encoding?: string): void; /** * events.EventEmitter @@ -2137,97 +2674,97 @@ declare module "net" { * 7. lookup * 8. timeout */ - addListener(event: string, listener: Function): this; - addListener(event: "close", listener: (had_error: boolean) => void): this; - addListener(event: "connect", listener: () => void): this; - addListener(event: "data", listener: (data: Buffer) => void): this; - addListener(event: "drain", listener: () => void): this; - addListener(event: "end", listener: () => void): this; - addListener(event: "error", listener: (err: Error) => void): this; - addListener(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; - addListener(event: "timeout", listener: () => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: (had_error: boolean) => void): this; + addListener(event: "connect", listener: () => void): this; + addListener(event: "data", listener: (data: Buffer) => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; + addListener(event: "timeout", listener: () => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "close", had_error: boolean): boolean; - emit(event: "connect"): boolean; - emit(event: "data", data: Buffer): boolean; - emit(event: "drain"): boolean; - emit(event: "end"): boolean; - emit(event: "error", err: Error): boolean; - emit(event: "lookup", err: Error, address: string, family: string | number, host: string): boolean; - emit(event: "timeout"): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close", had_error: boolean): boolean; + emit(event: "connect"): boolean; + emit(event: "data", data: Buffer): boolean; + emit(event: "drain"): boolean; + emit(event: "end"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "lookup", err: Error, address: string, family: string | number, host: string): boolean; + emit(event: "timeout"): boolean; - on(event: string, listener: Function): this; - on(event: "close", listener: (had_error: boolean) => void): this; - on(event: "connect", listener: () => void): this; - on(event: "data", listener: (data: Buffer) => void): this; - on(event: "drain", listener: () => void): this; - on(event: "end", listener: () => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; - on(event: "timeout", listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: (had_error: boolean) => void): this; + on(event: "connect", listener: () => void): this; + on(event: "data", listener: (data: Buffer) => void): this; + on(event: "drain", listener: () => void): this; + on(event: "end", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; + on(event: "timeout", listener: () => void): this; - once(event: string, listener: Function): this; - once(event: "close", listener: (had_error: boolean) => void): this; - once(event: "connect", listener: () => void): this; - once(event: "data", listener: (data: Buffer) => void): this; - once(event: "drain", listener: () => void): this; - once(event: "end", listener: () => void): this; - once(event: "error", listener: (err: Error) => void): this; - once(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; - once(event: "timeout", listener: () => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: (had_error: boolean) => void): this; + once(event: "connect", listener: () => void): this; + once(event: "data", listener: (data: Buffer) => void): this; + once(event: "drain", listener: () => void): this; + once(event: "end", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; + once(event: "timeout", listener: () => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "close", listener: (had_error: boolean) => void): this; - prependListener(event: "connect", listener: () => void): this; - prependListener(event: "data", listener: (data: Buffer) => void): this; - prependListener(event: "drain", listener: () => void): this; - prependListener(event: "end", listener: () => void): this; - prependListener(event: "error", listener: (err: Error) => void): this; - prependListener(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; - prependListener(event: "timeout", listener: () => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: (had_error: boolean) => void): this; + prependListener(event: "connect", listener: () => void): this; + prependListener(event: "data", listener: (data: Buffer) => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; + prependListener(event: "timeout", listener: () => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "close", listener: (had_error: boolean) => void): this; - prependOnceListener(event: "connect", listener: () => void): this; - prependOnceListener(event: "data", listener: (data: Buffer) => void): this; - prependOnceListener(event: "drain", listener: () => void): this; - prependOnceListener(event: "end", listener: () => void): this; - prependOnceListener(event: "error", listener: (err: Error) => void): this; - prependOnceListener(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; - prependOnceListener(event: "timeout", listener: () => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: (had_error: boolean) => void): this; + prependOnceListener(event: "connect", listener: () => void): this; + prependOnceListener(event: "data", listener: (data: Buffer) => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "lookup", listener: (err: Error, address: string, family: string | number, host: string) => void): this; + prependOnceListener(event: "timeout", listener: () => void): this; + } - export var Socket: { - new(options?: { fd?: number; allowHalfOpen?: boolean; readable?: boolean; writable?: boolean; }): Socket; - }; + export interface ListenOptions { + port?: number; + host?: string; + backlog?: number; + path?: string; + exclusive?: boolean; + } - export interface ListenOptions { - port?: number; - host?: string; - backlog?: number; - path?: string; - exclusive?: boolean; - } + // https://github.com/nodejs/node/blob/master/lib/net.js + export class Server extends events.EventEmitter { + constructor(connectionListener?: (socket: Socket) => void); + constructor(options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean }, connectionListener?: (socket: Socket) => void); - export interface Server extends events.EventEmitter { - listen(port: number, hostname?: string, backlog?: number, listeningListener?: Function): Server; - listen(port: number, hostname?: string, listeningListener?: Function): Server; - listen(port: number, backlog?: number, listeningListener?: Function): Server; - listen(port: number, listeningListener?: Function): Server; - listen(path: string, backlog?: number, listeningListener?: Function): Server; - listen(path: string, listeningListener?: Function): Server; - listen(options: ListenOptions, listeningListener?: Function): Server; - listen(handle: any, backlog?: number, listeningListener?: Function): Server; - listen(handle: any, listeningListener?: Function): Server; - close(callback?: Function): Server; - address(): { port: number; family: string; address: string; }; - getConnections(cb: (error: Error, count: number) => void): void; - ref(): Server; - unref(): Server; - maxConnections: number; - connections: number; - listening: boolean; + listen(port?: number, hostname?: string, backlog?: number, listeningListener?: Function): this; + listen(port?: number, hostname?: string, listeningListener?: Function): this; + listen(port?: number, backlog?: number, listeningListener?: Function): this; + listen(port?: number, listeningListener?: Function): this; + listen(path: string, backlog?: number, listeningListener?: Function): this; + listen(path: string, listeningListener?: Function): this; + listen(options: ListenOptions, listeningListener?: Function): this; + listen(handle: any, backlog?: number, listeningListener?: Function): this; + listen(handle: any, listeningListener?: Function): this; + close(callback?: Function): this; + address(): { port: number; family: string; address: string; }; + getConnections(cb: (error: Error | null, count: number) => void): void; + ref(): this; + unref(): this; + maxConnections: number; + connections: number; + listening: boolean; /** * events.EventEmitter @@ -2236,101 +2773,123 @@ declare module "net" { * 3. error * 4. listening */ - addListener(event: string, listener: Function): this; - addListener(event: "close", listener: () => void): this; - addListener(event: "connection", listener: (socket: Socket) => void): this; - addListener(event: "error", listener: (err: Error) => void): this; - addListener(event: "listening", listener: () => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "connection", listener: (socket: Socket) => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "listening", listener: () => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "close"): boolean; - emit(event: "connection", socket: Socket): boolean; - emit(event: "error", err: Error): boolean; - emit(event: "listening"): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close"): boolean; + emit(event: "connection", socket: Socket): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "listening"): boolean; - on(event: string, listener: Function): this; - on(event: "close", listener: () => void): this; - on(event: "connection", listener: (socket: Socket) => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "listening", listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "connection", listener: (socket: Socket) => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "listening", listener: () => void): this; - once(event: string, listener: Function): this; - once(event: "close", listener: () => void): this; - once(event: "connection", listener: (socket: Socket) => void): this; - once(event: "error", listener: (err: Error) => void): this; - once(event: "listening", listener: () => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "connection", listener: (socket: Socket) => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "listening", listener: () => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "close", listener: () => void): this; - prependListener(event: "connection", listener: (socket: Socket) => void): this; - prependListener(event: "error", listener: (err: Error) => void): this; - prependListener(event: "listening", listener: () => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "connection", listener: (socket: Socket) => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "listening", listener: () => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "close", listener: () => void): this; - prependOnceListener(event: "connection", listener: (socket: Socket) => void): this; - prependOnceListener(event: "error", listener: (err: Error) => void): this; - prependOnceListener(event: "listening", listener: () => void): this; - } - export function createServer(connectionListener?: (socket: Socket) => void): Server; - export function createServer(options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean }, connectionListener?: (socket: Socket) => void): Server; - export function connect(options: { port: number, host?: string, localAddress?: string, localPort?: string, family?: number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; - export function connect(port: number, host?: string, connectionListener?: Function): Socket; - export function connect(path: string, connectionListener?: Function): Socket; - export function createConnection(options: { port: number, host?: string, localAddress?: string, localPort?: string, family?: number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; - export function createConnection(port: number, host?: string, connectionListener?: Function): Socket; - export function createConnection(path: string, connectionListener?: Function): Socket; - export function isIP(input: string): number; - export function isIPv4(input: string): boolean; - export function isIPv6(input: string): boolean; + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "connection", listener: (socket: Socket) => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "listening", listener: () => void): this; + } + + export interface TcpNetConnectOpts extends TcpSocketConnectOpts, SocketConstructorOpts { + timeout?: number; + } + + export interface IpcNetConnectOpts extends IpcSocketConnectOpts, SocketConstructorOpts { + timeout?: number; + } + + export type NetConnectOpts = TcpNetConnectOpts | IpcNetConnectOpts; + + export function createServer(connectionListener?: (socket: Socket) => void): Server; + export function createServer(options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean }, connectionListener?: (socket: Socket) => void): Server; + export function connect(options: NetConnectOpts, connectionListener?: Function): Socket; + export function connect(port: number, host?: string, connectionListener?: Function): Socket; + export function connect(path: string, connectionListener?: Function): Socket; + export function createConnection(options: NetConnectOpts, connectionListener?: Function): Socket; + export function createConnection(port: number, host?: string, connectionListener?: Function): Socket; + export function createConnection(path: string, connectionListener?: Function): Socket; + export function isIP(input: string): number; + export function isIPv4(input: string): boolean; + export function isIPv6(input: string): boolean; } declare module "dgram" { - import * as events from "events"; + import * as events from "events"; + import * as dns from "dns"; - interface RemoteInfo { - address: string; - family: string; - port: number; - } + interface RemoteInfo { + address: string; + family: string; + port: number; + } - interface AddressInfo { - address: string; - family: string; - port: number; - } + interface AddressInfo { + address: string; + family: string; + port: number; + } - interface BindOptions { - port: number; - address?: string; - exclusive?: boolean; - } + interface BindOptions { + port: number; + address?: string; + exclusive?: boolean; + } - type SocketType = "udp4" | "udp6"; + type SocketType = "udp4" | "udp6"; - interface SocketOptions { - type: SocketType; - reuseAddr?: boolean; - } + interface SocketOptions { + type: SocketType; + reuseAddr?: boolean; + recvBufferSize?: number; + sendBufferSize?: number; + lookup?: (hostname: string, options: dns.LookupOneOptions, callback: (err: NodeJS.ErrnoException, address: string, family: number) => void) => void; + } - export function createSocket(type: SocketType, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; - export function createSocket(options: SocketOptions, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; + export function createSocket(type: SocketType, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; + export function createSocket(options: SocketOptions, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; - export interface Socket extends events.EventEmitter { - send(msg: Buffer | String | any[], port: number, address: string, callback?: (error: Error, bytes: number) => void): void; - send(msg: Buffer | String | any[], offset: number, length: number, port: number, address: string, callback?: (error: Error, bytes: number) => void): void; - bind(port?: number, address?: string, callback?: () => void): void; - bind(options: BindOptions, callback?: Function): void; - close(callback?: () => void): void; - address(): AddressInfo; - setBroadcast(flag: boolean): void; - setTTL(ttl: number): void; - setMulticastTTL(ttl: number): void; - setMulticastLoopback(flag: boolean): void; - addMembership(multicastAddress: string, multicastInterface?: string): void; - dropMembership(multicastAddress: string, multicastInterface?: string): void; - ref(): this; - unref(): this; + export class Socket extends events.EventEmitter { + send(msg: Buffer | string | Uint8Array | any[], port: number, address?: string, callback?: (error: Error | null, bytes: number) => void): void; + send(msg: Buffer | string | Uint8Array, offset: number, length: number, port: number, address?: string, callback?: (error: Error | null, bytes: number) => void): void; + bind(port?: number, address?: string, callback?: () => void): void; + bind(port?: number, callback?: () => void): void; + bind(callback?: () => void): void; + bind(options: BindOptions, callback?: Function): void; + close(callback?: () => void): void; + address(): AddressInfo; + setBroadcast(flag: boolean): void; + setTTL(ttl: number): void; + setMulticastTTL(ttl: number): void; + setMulticastInterface(multicastInterface: string): void; + setMulticastLoopback(flag: boolean): void; + addMembership(multicastAddress: string, multicastInterface?: string): void; + dropMembership(multicastAddress: string, multicastInterface?: string): void; + ref(): this; + unref(): this; + setRecvBufferSize(size: number): void; + setSendBufferSize(size: number): void; + getRecvBufferSize(): number; + getSendBufferSize(): number; /** * events.EventEmitter @@ -2338,586 +2897,1738 @@ declare module "dgram" { * 2. error * 3. listening * 4. message - **/ - addListener(event: string, listener: Function): this; - addListener(event: "close", listener: () => void): this; - addListener(event: "error", listener: (err: Error) => void): this; - addListener(event: "listening", listener: () => void): this; - addListener(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; + */ + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "listening", listener: () => void): this; + addListener(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "close"): boolean; - emit(event: "error", err: Error): boolean; - emit(event: "listening"): boolean; - emit(event: "message", msg: Buffer, rinfo: AddressInfo): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "listening"): boolean; + emit(event: "message", msg: Buffer, rinfo: AddressInfo): boolean; - on(event: string, listener: Function): this; - on(event: "close", listener: () => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "listening", listener: () => void): this; - on(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "listening", listener: () => void): this; + on(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; - once(event: string, listener: Function): this; - once(event: "close", listener: () => void): this; - once(event: "error", listener: (err: Error) => void): this; - once(event: "listening", listener: () => void): this; - once(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "listening", listener: () => void): this; + once(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "close", listener: () => void): this; - prependListener(event: "error", listener: (err: Error) => void): this; - prependListener(event: "listening", listener: () => void): this; - prependListener(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "listening", listener: () => void): this; + prependListener(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "close", listener: () => void): this; - prependOnceListener(event: "error", listener: (err: Error) => void): this; - prependOnceListener(event: "listening", listener: () => void): this; - prependOnceListener(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "listening", listener: () => void): this; + prependOnceListener(event: "message", listener: (msg: Buffer, rinfo: AddressInfo) => void): this; + } } declare module "fs" { - import * as stream from "stream"; - import * as events from "events"; + import * as stream from "stream"; + import * as events from "events"; + import { URL } from "url"; - interface Stats { - isFile(): boolean; - isDirectory(): boolean; - isBlockDevice(): boolean; - isCharacterDevice(): boolean; - isSymbolicLink(): boolean; - isFIFO(): boolean; - isSocket(): boolean; - dev: number; - ino: number; - mode: number; - nlink: number; - uid: number; - gid: number; - rdev: number; - size: number; - blksize: number; - blocks: number; - atime: Date; - mtime: Date; - ctime: Date; - birthtime: Date; - } + /** + * Valid types for path values in "fs". + */ + export type PathLike = string | Buffer | URL; - interface FSWatcher extends events.EventEmitter { - close(): void; + export class Stats { + isFile(): boolean; + isDirectory(): boolean; + isBlockDevice(): boolean; + isCharacterDevice(): boolean; + isSymbolicLink(): boolean; + isFIFO(): boolean; + isSocket(): boolean; + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blksize: number; + blocks: number; + atimeMs: number; + mtimeMs: number; + ctimeMs: number; + birthtimeMs: number; + atime: Date; + mtime: Date; + ctime: Date; + birthtime: Date; + } + + export interface FSWatcher extends events.EventEmitter { + close(): void; /** * events.EventEmitter * 1. change * 2. error */ - addListener(event: string, listener: Function): this; - addListener(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; - addListener(event: "error", listener: (error: Error) => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; + addListener(event: "error", listener: (error: Error) => void): this; - on(event: string, listener: Function): this; - on(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; - on(event: "error", listener: (error: Error) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; + on(event: "error", listener: (error: Error) => void): this; - once(event: string, listener: Function): this; - once(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; - once(event: "error", listener: (error: Error) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; + once(event: "error", listener: (error: Error) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; - prependListener(event: "error", listener: (error: Error) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; + prependListener(event: "error", listener: (error: Error) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; - prependOnceListener(event: "error", listener: (error: Error) => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "change", listener: (eventType: string, filename: string | Buffer) => void): this; + prependOnceListener(event: "error", listener: (error: Error) => void): this; + } - export interface ReadStream extends stream.Readable { - close(): void; - destroy(): void; - bytesRead: number; - path: string | Buffer; + export class ReadStream extends stream.Readable { + close(): void; + destroy(): void; + bytesRead: number; + path: string | Buffer; /** * events.EventEmitter * 1. open * 2. close */ - addListener(event: string, listener: Function): this; - addListener(event: "open", listener: (fd: number) => void): this; - addListener(event: "close", listener: () => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "open", listener: (fd: number) => void): this; + addListener(event: "close", listener: () => void): this; - on(event: string, listener: Function): this; - on(event: "open", listener: (fd: number) => void): this; - on(event: "close", listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "open", listener: (fd: number) => void): this; + on(event: "close", listener: () => void): this; - once(event: string, listener: Function): this; - once(event: "open", listener: (fd: number) => void): this; - once(event: "close", listener: () => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "open", listener: (fd: number) => void): this; + once(event: "close", listener: () => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "open", listener: (fd: number) => void): this; - prependListener(event: "close", listener: () => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "open", listener: (fd: number) => void): this; + prependListener(event: "close", listener: () => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "open", listener: (fd: number) => void): this; - prependOnceListener(event: "close", listener: () => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "open", listener: (fd: number) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + } - export interface WriteStream extends stream.Writable { - close(): void; - bytesWritten: number; - path: string | Buffer; + export class WriteStream extends stream.Writable { + close(): void; + bytesWritten: number; + path: string | Buffer; /** * events.EventEmitter * 1. open * 2. close */ - addListener(event: string, listener: Function): this; - addListener(event: "open", listener: (fd: number) => void): this; - addListener(event: "close", listener: () => void): this; + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "open", listener: (fd: number) => void): this; + addListener(event: "close", listener: () => void): this; - on(event: string, listener: Function): this; - on(event: "open", listener: (fd: number) => void): this; - on(event: "close", listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "open", listener: (fd: number) => void): this; + on(event: "close", listener: () => void): this; - once(event: string, listener: Function): this; - once(event: "open", listener: (fd: number) => void): this; - once(event: "close", listener: () => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "open", listener: (fd: number) => void): this; + once(event: "close", listener: () => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "open", listener: (fd: number) => void): this; - prependListener(event: "close", listener: () => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "open", listener: (fd: number) => void): this; + prependListener(event: "close", listener: () => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "open", listener: (fd: number) => void): this; - prependOnceListener(event: "close", listener: () => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "open", listener: (fd: number) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + } /** - * Asynchronous rename. - * @param oldPath - * @param newPath - * @param callback No arguments other than a possible exception are given to the completion callback. + * Asynchronous rename(2) - Change the name or location of a file or directory. + * @param oldPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * @param newPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. */ - export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function rename(oldPath: PathLike, newPath: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace rename { + /** + * Asynchronous rename(2) - Change the name or location of a file or directory. + * @param oldPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * @param newPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function __promisify__(oldPath: PathLike, newPath: PathLike): Promise<void>; + } + /** - * Synchronous rename - * @param oldPath - * @param newPath + * Synchronous rename(2) - Change the name or location of a file or directory. + * @param oldPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * @param newPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. */ - export function renameSync(oldPath: string, newPath: string): void; - export function truncate(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function truncate(path: string | Buffer, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function truncateSync(path: string | Buffer, len?: number): void; - export function ftruncate(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function ftruncate(fd: number, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function ftruncateSync(fd: number, len?: number): void; - export function chown(path: string | Buffer, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function chownSync(path: string | Buffer, uid: number, gid: number): void; - export function fchown(fd: number, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fchownSync(fd: number, uid: number, gid: number): void; - export function lchown(path: string | Buffer, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function lchownSync(path: string | Buffer, uid: number, gid: number): void; - export function chmod(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function chmod(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function chmodSync(path: string | Buffer, mode: number): void; - export function chmodSync(path: string | Buffer, mode: string): void; - export function fchmod(fd: number, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fchmod(fd: number, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fchmodSync(fd: number, mode: number): void; - export function fchmodSync(fd: number, mode: string): void; - export function lchmod(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function lchmod(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function lchmodSync(path: string | Buffer, mode: number): void; - export function lchmodSync(path: string | Buffer, mode: string): void; - export function stat(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; - export function lstat(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; - export function fstat(fd: number, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; - export function statSync(path: string | Buffer): Stats; - export function lstatSync(path: string | Buffer): Stats; - export function fstatSync(fd: number): Stats; - export function link(srcpath: string | Buffer, dstpath: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function linkSync(srcpath: string | Buffer, dstpath: string | Buffer): void; - export function symlink(srcpath: string | Buffer, dstpath: string | Buffer, type?: string, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function symlinkSync(srcpath: string | Buffer, dstpath: string | Buffer, type?: string): void; - export function readlink(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, linkString: string) => any): void; - export function readlinkSync(path: string | Buffer): string; - export function realpath(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; - export function realpath(path: string | Buffer, cache: { [path: string]: string }, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; - export function realpathSync(path: string | Buffer, cache?: { [path: string]: string }): string; + export function renameSync(oldPath: PathLike, newPath: PathLike): void; + /** - * Asynchronous unlink - deletes the file specified in {path} - * - * @param path - * @param callback No arguments other than a possible exception are given to the completion callback. + * Asynchronous truncate(2) - Truncate a file to a specified length. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param len If not specified, defaults to `0`. */ - export function unlink(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function truncate(path: PathLike, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void; + /** - * Synchronous unlink - deletes the file specified in {path} - * - * @param path + * Asynchronous truncate(2) - Truncate a file to a specified length. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. */ - export function unlinkSync(path: string | Buffer): void; + export function truncate(path: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace truncate { + /** + * Asynchronous truncate(2) - Truncate a file to a specified length. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param len If not specified, defaults to `0`. + */ + export function __promisify__(path: PathLike, len?: number | null): Promise<void>; + } + /** - * Asynchronous rmdir - removes the directory specified in {path} - * - * @param path - * @param callback No arguments other than a possible exception are given to the completion callback. + * Synchronous truncate(2) - Truncate a file to a specified length. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param len If not specified, defaults to `0`. */ - export function rmdir(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function truncateSync(path: PathLike, len?: number | null): void; + /** - * Synchronous rmdir - removes the directory specified in {path} - * - * @param path + * Asynchronous ftruncate(2) - Truncate a file to a specified length. + * @param fd A file descriptor. + * @param len If not specified, defaults to `0`. */ - export function rmdirSync(path: string | Buffer): void; + export function ftruncate(fd: number, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void; + /** - * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param callback No arguments other than a possible exception are given to the completion callback. + * Asynchronous ftruncate(2) - Truncate a file to a specified length. + * @param fd A file descriptor. */ - export function mkdir(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function ftruncate(fd: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace ftruncate { + /** + * Asynchronous ftruncate(2) - Truncate a file to a specified length. + * @param fd A file descriptor. + * @param len If not specified, defaults to `0`. + */ + export function __promisify__(fd: number, len?: number | null): Promise<void>; + } + /** - * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. + * Synchronous ftruncate(2) - Truncate a file to a specified length. + * @param fd A file descriptor. + * @param len If not specified, defaults to `0`. */ - export function mkdir(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function ftruncateSync(fd: number, len?: number | null): void; + /** - * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. + * Asynchronous chown(2) - Change ownership of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. */ - export function mkdir(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function chown(path: PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace chown { + /** + * Asynchronous chown(2) - Change ownership of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function __promisify__(path: PathLike, uid: number, gid: number): Promise<void>; + } + /** - * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. + * Synchronous chown(2) - Change ownership of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. */ - export function mkdirSync(path: string | Buffer, mode?: number): void; + export function chownSync(path: PathLike, uid: number, gid: number): void; + /** - * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. - * - * @param path - * @param mode - * @param callback No arguments other than a possible exception are given to the completion callback. + * Asynchronous fchown(2) - Change ownership of a file. + * @param fd A file descriptor. */ - export function mkdirSync(path: string | Buffer, mode?: string): void; + export function fchown(fd: number, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace fchown { + /** + * Asynchronous fchown(2) - Change ownership of a file. + * @param fd A file descriptor. + */ + export function __promisify__(fd: number, uid: number, gid: number): Promise<void>; + } + /** - * Asynchronous mkdtemp - Creates a unique temporary directory. Generates six random characters to be appended behind a required prefix to create a unique temporary directory. - * - * @param prefix - * @param callback The created folder path is passed as a string to the callback's second parameter. + * Synchronous fchown(2) - Change ownership of a file. + * @param fd A file descriptor. */ - export function mkdtemp(prefix: string, callback?: (err: NodeJS.ErrnoException, folder: string) => void): void; + export function fchownSync(fd: number, uid: number, gid: number): void; + /** - * Synchronous mkdtemp - Creates a unique temporary directory. Generates six random characters to be appended behind a required prefix to create a unique temporary directory. - * - * @param prefix - * @returns Returns the created folder path. + * Asynchronous lchown(2) - Change ownership of a file. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. */ - export function mkdtempSync(prefix: string): string; - export function readdir(path: string | Buffer, callback: (err: NodeJS.ErrnoException, files: string[]) => void): void; - export function readdir(path: string | Buffer, options: string | {}, callback: (err: NodeJS.ErrnoException, files: string[]) => void): void; - export function readdirSync(path: string | Buffer, options?: string | {}): string[]; - export function close(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function closeSync(fd: number): void; - export function open(path: string | Buffer, flags: string | number, callback: (err: NodeJS.ErrnoException, fd: number) => void): void; - export function open(path: string | Buffer, flags: string | number, mode: number, callback: (err: NodeJS.ErrnoException, fd: number) => void): void; - export function openSync(path: string | Buffer, flags: string | number, mode?: number): number; - export function utimes(path: string | Buffer, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function utimes(path: string | Buffer, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function utimesSync(path: string | Buffer, atime: number, mtime: number): void; - export function utimesSync(path: string | Buffer, atime: Date, mtime: Date): void; - export function futimes(fd: number, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function futimes(fd: number, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function futimesSync(fd: number, atime: number, mtime: number): void; - export function futimesSync(fd: number, atime: Date, mtime: Date): void; - export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; - export function fsyncSync(fd: number): void; - export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number | null, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; - export function write(fd: number, buffer: Buffer, offset: number, length: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; - export function write(fd: number, data: any, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; - export function write(fd: number, data: any, offset: number, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; - export function write(fd: number, data: any, offset: number, encoding: string, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; - export function writeSync(fd: number, buffer: Buffer, offset: number, length: number, position?: number | null): number; - export function writeSync(fd: number, data: any, position?: number | null, enconding?: string): number; - export function read(fd: number, buffer: Buffer, offset: number, length: number, position: number | null, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: Buffer) => void): void; - export function readSync(fd: number, buffer: Buffer, offset: number, length: number, position: number | null): number; + export function lchown(path: PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace lchown { + /** + * Asynchronous lchown(2) - Change ownership of a file. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function __promisify__(path: PathLike, uid: number, gid: number): Promise<void>; + } + /** - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param encoding - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + * Synchronous lchown(2) - Change ownership of a file. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. */ - export function readFile(filename: string, encoding: null, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; - export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; - export function readFile(filename: string, encoding: string | null, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void; + export function lchownSync(path: PathLike, uid: number, gid: number): void; + /** - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + * Asynchronous chmod(2) - Change permissions of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. */ - export function readFile(filename: string, options: { encoding: null; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; - export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void; - export function readFile(filename: string, options: { encoding: string | null; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void; + export function chmod(path: PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace chmod { + /** + * Asynchronous chmod(2) - Change permissions of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. + */ + export function __promisify__(path: PathLike, mode: string | number): Promise<void>; + } + /** - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + * Synchronous chmod(2) - Change permissions of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. */ - export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + export function chmodSync(path: PathLike, mode: string | number): void; + /** - * Asynchronous readFile - Asynchronously reads the entire contents of a file. - * - * @param fileName - * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + * Asynchronous fchmod(2) - Change permissions of a file. + * @param fd A file descriptor. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. */ - export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + export function fchmod(fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace fchmod { + /** + * Asynchronous fchmod(2) - Change permissions of a file. + * @param fd A file descriptor. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. + */ + export function __promisify__(fd: number, mode: string | number): Promise<void>; + } + /** - * Synchronous readFile - Synchronously reads the entire contents of a file. - * - * @param fileName - * @param encoding + * Synchronous fchmod(2) - Change permissions of a file. + * @param fd A file descriptor. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. */ - export function readFileSync(filename: string, encoding: null): Buffer; - export function readFileSync(filename: string, encoding: string): string; - export function readFileSync(filename: string, encoding: string | null): string | Buffer; + export function fchmodSync(fd: number, mode: string | number): void; + /** - * Synchronous readFile - Synchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. + * Asynchronous lchmod(2) - Change permissions of a file. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. */ - export function readFileSync(filename: string, options: { encoding: null; flag?: string; }): Buffer; - export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string; - export function readFileSync(filename: string, options: { encoding: string | null; flag?: string; }): string | Buffer; + export function lchmod(path: PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace lchmod { + /** + * Asynchronous lchmod(2) - Change permissions of a file. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. + */ + export function __promisify__(path: PathLike, mode: string | number): Promise<void>; + } + /** - * Synchronous readFile - Synchronously reads the entire contents of a file. - * - * @param fileName - * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. + * Synchronous lchmod(2) - Change permissions of a file. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. */ - export function readFileSync(filename: string, options?: { flag?: string; }): Buffer; - export function writeFile(filename: string | number, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; - export function writeFile(filename: string | number, data: any, encoding: string, callback: (err: NodeJS.ErrnoException) => void): void; - export function writeFile(filename: string | number, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function writeFile(filename: string | number, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function writeFileSync(filename: string | number, data: any, encoding: string): void; - export function writeFileSync(filename: string | number, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; - export function writeFileSync(filename: string | number, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; - export function appendFile(filename: string, data: any, encoding: string, callback: (err: NodeJS.ErrnoException) => void): void; - export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; - export function appendFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; - export function appendFileSync(filename: string, data: any, encoding: string): void; - export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; - export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; - export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void; - export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: (curr: Stats, prev: Stats) => void): void; - export function unwatchFile(filename: string, listener?: (curr: Stats, prev: Stats) => void): void; - export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher; - export function watch(filename: string, encoding: string, listener?: (event: string, filename: string | Buffer) => any): FSWatcher; - export function watch(filename: string, options: { persistent?: boolean; recursive?: boolean; encoding?: string }, listener?: (event: string, filename: string | Buffer) => any): FSWatcher; - export function exists(path: string | Buffer, callback?: (exists: boolean) => void): void; - export function existsSync(path: string | Buffer): boolean; + export function lchmodSync(path: PathLike, mode: string | number): void; - export namespace constants { - // File Access Constants + /** + * Asynchronous stat(2) - Get file status. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function stat(path: PathLike, callback: (err: NodeJS.ErrnoException, stats: Stats) => void): void; - /** Constant for fs.access(). File is visible to the calling process. */ - export const F_OK: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace stat { + /** + * Asynchronous stat(2) - Get file status. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function __promisify__(path: PathLike): Promise<Stats>; + } - /** Constant for fs.access(). File can be read by the calling process. */ - export const R_OK: number; + /** + * Synchronous stat(2) - Get file status. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function statSync(path: PathLike): Stats; - /** Constant for fs.access(). File can be written by the calling process. */ - export const W_OK: number; + /** + * Asynchronous fstat(2) - Get file status. + * @param fd A file descriptor. + */ + export function fstat(fd: number, callback: (err: NodeJS.ErrnoException, stats: Stats) => void): void; - /** Constant for fs.access(). File can be executed by the calling process. */ - export const X_OK: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace fstat { + /** + * Asynchronous fstat(2) - Get file status. + * @param fd A file descriptor. + */ + export function __promisify__(fd: number): Promise<Stats>; + } - // File Open Constants + /** + * Synchronous fstat(2) - Get file status. + * @param fd A file descriptor. + */ + export function fstatSync(fd: number): Stats; - /** Constant for fs.open(). Flag indicating to open a file for read-only access. */ - export const O_RDONLY: number; + /** + * Asynchronous lstat(2) - Get file status. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function lstat(path: PathLike, callback: (err: NodeJS.ErrnoException, stats: Stats) => void): void; - /** Constant for fs.open(). Flag indicating to open a file for write-only access. */ - export const O_WRONLY: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace lstat { + /** + * Asynchronous lstat(2) - Get file status. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function __promisify__(path: PathLike): Promise<Stats>; + } - /** Constant for fs.open(). Flag indicating to open a file for read-write access. */ - export const O_RDWR: number; + /** + * Synchronous lstat(2) - Get file status. Does not dereference symbolic links. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function lstatSync(path: PathLike): Stats; - /** Constant for fs.open(). Flag indicating to create the file if it does not already exist. */ - export const O_CREAT: number; + /** + * Asynchronous link(2) - Create a new link (also known as a hard link) to an existing file. + * @param existingPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param newPath A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function link(existingPath: PathLike, newPath: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; - /** Constant for fs.open(). Flag indicating that opening a file should fail if the O_CREAT flag is set and the file already exists. */ - export const O_EXCL: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace link { + /** + * Asynchronous link(2) - Create a new link (also known as a hard link) to an existing file. + * @param existingPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param newPath A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function link(existingPath: PathLike, newPath: PathLike): Promise<void>; + } - /** Constant for fs.open(). Flag indicating that if path identifies a terminal device, opening the path shall not cause that terminal to become the controlling terminal for the process (if the process does not already have one). */ - export const O_NOCTTY: number; + /** + * Synchronous link(2) - Create a new link (also known as a hard link) to an existing file. + * @param existingPath A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param newPath A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function linkSync(existingPath: PathLike, newPath: PathLike): void; - /** Constant for fs.open(). Flag indicating that if the file exists and is a regular file, and the file is opened successfully for write access, its length shall be truncated to zero. */ - export const O_TRUNC: number; + /** + * Asynchronous symlink(2) - Create a new symbolic link to an existing file. + * @param target A path to an existing file. If a URL is provided, it must use the `file:` protocol. + * @param path A path to the new symlink. If a URL is provided, it must use the `file:` protocol. + * @param type May be set to `'dir'`, `'file'`, or `'junction'` (default is `'file'`) and is only available on Windows (ignored on other platforms). + * When using `'junction'`, the `target` argument will automatically be normalized to an absolute path. + */ + export function symlink(target: PathLike, path: PathLike, type: symlink.Type | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void; - /** Constant for fs.open(). Flag indicating that data will be appended to the end of the file. */ - export const O_APPEND: number; + /** + * Asynchronous symlink(2) - Create a new symbolic link to an existing file. + * @param target A path to an existing file. If a URL is provided, it must use the `file:` protocol. + * @param path A path to the new symlink. If a URL is provided, it must use the `file:` protocol. + */ + export function symlink(target: PathLike, path: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; - /** Constant for fs.open(). Flag indicating that the open should fail if the path is not a directory. */ - export const O_DIRECTORY: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace symlink { + /** + * Asynchronous symlink(2) - Create a new symbolic link to an existing file. + * @param target A path to an existing file. If a URL is provided, it must use the `file:` protocol. + * @param path A path to the new symlink. If a URL is provided, it must use the `file:` protocol. + * @param type May be set to `'dir'`, `'file'`, or `'junction'` (default is `'file'`) and is only available on Windows (ignored on other platforms). + * When using `'junction'`, the `target` argument will automatically be normalized to an absolute path. + */ + export function __promisify__(target: PathLike, path: PathLike, type?: string | null): Promise<void>; - /** Constant for fs.open(). Flag indicating reading accesses to the file system will no longer result in an update to the atime information associated with the file. This flag is available on Linux operating systems only. */ - export const O_NOATIME: number; + export type Type = "dir" | "file" | "junction"; + } - /** Constant for fs.open(). Flag indicating that the open should fail if the path is a symbolic link. */ - export const O_NOFOLLOW: number; + /** + * Synchronous symlink(2) - Create a new symbolic link to an existing file. + * @param target A path to an existing file. If a URL is provided, it must use the `file:` protocol. + * @param path A path to the new symlink. If a URL is provided, it must use the `file:` protocol. + * @param type May be set to `'dir'`, `'file'`, or `'junction'` (default is `'file'`) and is only available on Windows (ignored on other platforms). + * When using `'junction'`, the `target` argument will automatically be normalized to an absolute path. + */ + export function symlinkSync(target: PathLike, path: PathLike, type?: symlink.Type | null): void; - /** Constant for fs.open(). Flag indicating that the file is opened for synchronous I/O. */ - export const O_SYNC: number; + /** + * Asynchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readlink(path: PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, linkString: string) => void): void; - /** Constant for fs.open(). Flag indicating to open the symbolic link itself rather than the resource it is pointing to. */ - export const O_SYMLINK: number; + /** + * Asynchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readlink(path: PathLike, options: { encoding: "buffer" } | "buffer", callback: (err: NodeJS.ErrnoException, linkString: Buffer) => void): void; - /** Constant for fs.open(). When set, an attempt will be made to minimize caching effects of file I/O. */ - export const O_DIRECT: number; + /** + * Asynchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readlink(path: PathLike, options: { encoding?: string | null } | string | undefined | null, callback: (err: NodeJS.ErrnoException, linkString: string | Buffer) => void): void; - /** Constant for fs.open(). Flag indicating to open the file in nonblocking mode when possible. */ - export const O_NONBLOCK: number; + /** + * Asynchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function readlink(path: PathLike, callback: (err: NodeJS.ErrnoException, linkString: string) => void): void; - // File Type Constants + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace readlink { + /** + * Asynchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options?: { encoding?: BufferEncoding | null } | BufferEncoding | null): Promise<string>; - /** Constant for fs.Stats mode property for determining a file's type. Bit mask used to extract the file type code. */ - export const S_IFMT: number; + /** + * Asynchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options: { encoding: "buffer" } | "buffer"): Promise<Buffer>; - /** Constant for fs.Stats mode property for determining a file's type. File type constant for a regular file. */ - export const S_IFREG: number; + /** + * Asynchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options?: { encoding?: string | null } | string | null): Promise<string | Buffer>; + } - /** Constant for fs.Stats mode property for determining a file's type. File type constant for a directory. */ - export const S_IFDIR: number; + /** + * Synchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readlinkSync(path: PathLike, options?: { encoding?: BufferEncoding | null } | BufferEncoding | null): string; - /** Constant for fs.Stats mode property for determining a file's type. File type constant for a character-oriented device file. */ - export const S_IFCHR: number; + /** + * Synchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readlinkSync(path: PathLike, options: { encoding: "buffer" } | "buffer"): Buffer; - /** Constant for fs.Stats mode property for determining a file's type. File type constant for a block-oriented device file. */ - export const S_IFBLK: number; + /** + * Synchronous readlink(2) - read value of a symbolic link. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readlinkSync(path: PathLike, options?: { encoding?: string | null } | string | null): string | Buffer; - /** Constant for fs.Stats mode property for determining a file's type. File type constant for a FIFO/pipe. */ - export const S_IFIFO: number; + /** + * Asynchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function realpath(path: PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => void): void; - /** Constant for fs.Stats mode property for determining a file's type. File type constant for a symbolic link. */ - export const S_IFLNK: number; + /** + * Asynchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function realpath(path: PathLike, options: { encoding: "buffer" } | "buffer", callback: (err: NodeJS.ErrnoException, resolvedPath: Buffer) => void): void; - /** Constant for fs.Stats mode property for determining a file's type. File type constant for a socket. */ - export const S_IFSOCK: number; + /** + * Asynchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function realpath(path: PathLike, options: { encoding?: string | null } | string | undefined | null, callback: (err: NodeJS.ErrnoException, resolvedPath: string | Buffer) => void): void; - // File Mode Constants + /** + * Asynchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function realpath(path: PathLike, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => void): void; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable, writable and executable by owner. */ - export const S_IRWXU: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace realpath { + /** + * Asynchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options?: { encoding?: BufferEncoding | null } | BufferEncoding | null): Promise<string>; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable by owner. */ - export const S_IRUSR: number; + /** + * Asynchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options: { encoding: "buffer" } | "buffer"): Promise<Buffer>; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating writable by owner. */ - export const S_IWUSR: number; + /** + * Asynchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options?: { encoding?: string | null } | string | null): Promise<string | Buffer>; + } - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating executable by owner. */ - export const S_IXUSR: number; + /** + * Synchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function realpathSync(path: PathLike, options?: { encoding?: BufferEncoding | null } | BufferEncoding | null): string; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable, writable and executable by group. */ - export const S_IRWXG: number; + /** + * Synchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function realpathSync(path: PathLike, options: { encoding: "buffer" } | "buffer"): Buffer; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable by group. */ - export const S_IRGRP: number; + /** + * Synchronous realpath(3) - return the canonicalized absolute pathname. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function realpathSync(path: PathLike, options?: { encoding?: string | null } | string | null): string | Buffer; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating writable by group. */ - export const S_IWGRP: number; + /** + * Asynchronous unlink(2) - delete a name and possibly the file it refers to. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function unlink(path: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating executable by group. */ - export const S_IXGRP: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace unlink { + /** + * Asynchronous unlink(2) - delete a name and possibly the file it refers to. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function __promisify__(path: PathLike): Promise<void>; + } - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable, writable and executable by others. */ - export const S_IRWXO: number; + /** + * Synchronous unlink(2) - delete a name and possibly the file it refers to. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function unlinkSync(path: PathLike): void; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable by others. */ - export const S_IROTH: number; + /** + * Asynchronous rmdir(2) - delete a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function rmdir(path: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating writable by others. */ - export const S_IWOTH: number; + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace rmdir { + /** + * Asynchronous rmdir(2) - delete a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function __promisify__(path: PathLike): Promise<void>; + } - /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating executable by others. */ - export const S_IXOTH: number; - } + /** + * Synchronous rmdir(2) - delete a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function rmdirSync(path: PathLike): void; - /** Tests a user's permissions for the file specified by path. */ - export function access(path: string | Buffer, callback: (err: NodeJS.ErrnoException) => void): void; - export function access(path: string | Buffer, mode: number, callback: (err: NodeJS.ErrnoException) => void): void; - /** Synchronous version of fs.access. This throws if any accessibility checks fail, and does nothing otherwise. */ - export function accessSync(path: string | Buffer, mode?: number): void; - export function createReadStream(path: string | Buffer, options?: { - flags?: string; - encoding?: string; - fd?: number; - mode?: number; - autoClose?: boolean; - start?: number; - end?: number; - }): ReadStream; - export function createWriteStream(path: string | Buffer, options?: { - flags?: string; - encoding?: string; - fd?: number; - mode?: number; - autoClose?: boolean; - start?: number; - }): WriteStream; - export function fdatasync(fd: number, callback: Function): void; - export function fdatasyncSync(fd: number): void; + /** + * Asynchronous mkdir(2) - create a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`. + */ + export function mkdir(path: PathLike, mode: number | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void; + + /** + * Asynchronous mkdir(2) - create a directory with a mode of `0o777`. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function mkdir(path: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace mkdir { + /** + * Asynchronous mkdir(2) - create a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`. + */ + export function __promisify__(path: PathLike, mode?: number | string | null): Promise<void>; + } + + /** + * Synchronous mkdir(2) - create a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`. + */ + export function mkdirSync(path: PathLike, mode?: number | string | null): void; + + /** + * Asynchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function mkdtemp(prefix: string, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, folder: string) => void): void; + + /** + * Asynchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function mkdtemp(prefix: string, options: "buffer" | { encoding: "buffer" }, callback: (err: NodeJS.ErrnoException, folder: Buffer) => void): void; + + /** + * Asynchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function mkdtemp(prefix: string, options: { encoding?: string | null } | string | undefined | null, callback: (err: NodeJS.ErrnoException, folder: string | Buffer) => void): void; + + /** + * Asynchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + */ + export function mkdtemp(prefix: string, callback: (err: NodeJS.ErrnoException, folder: string) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace mkdtemp { + /** + * Asynchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(prefix: string, options?: { encoding?: BufferEncoding | null } | BufferEncoding | null): Promise<string>; + + /** + * Asynchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(prefix: string, options: { encoding: "buffer" } | "buffer"): Promise<Buffer>; + + /** + * Asynchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(prefix: string, options?: { encoding?: string | null } | string | null): Promise<string | Buffer>; + } + + /** + * Synchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function mkdtempSync(prefix: string, options?: { encoding?: BufferEncoding | null } | BufferEncoding | null): string; + + /** + * Synchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function mkdtempSync(prefix: string, options: { encoding: "buffer" } | "buffer"): Buffer; + + /** + * Synchronously creates a unique temporary directory. + * Generates six random characters to be appended behind a required prefix to create a unique temporary directory. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function mkdtempSync(prefix: string, options?: { encoding?: string | null } | string | null): string | Buffer; + + /** + * Asynchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readdir(path: PathLike, options: { encoding: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, files: string[]) => void): void; + + /** + * Asynchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readdir(path: PathLike, options: { encoding: "buffer" } | "buffer", callback: (err: NodeJS.ErrnoException, files: Buffer[]) => void): void; + + /** + * Asynchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readdir(path: PathLike, options: { encoding?: string | null } | string | undefined | null, callback: (err: NodeJS.ErrnoException, files: string[] | Buffer[]) => void): void; + + /** + * Asynchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function readdir(path: PathLike, callback: (err: NodeJS.ErrnoException, files: string[]) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace readdir { + /** + * Asynchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options?: { encoding: BufferEncoding | null } | BufferEncoding | null): Promise<string[]>; + + /** + * Asynchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options: "buffer" | { encoding: "buffer" }): Promise<Buffer[]>; + + /** + * Asynchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function __promisify__(path: PathLike, options?: { encoding?: string | null } | string | null): Promise<string[] | Buffer[]>; + } + + /** + * Synchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readdirSync(path: PathLike, options?: { encoding: BufferEncoding | null } | BufferEncoding | null): string[]; + + /** + * Synchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readdirSync(path: PathLike, options: { encoding: "buffer" } | "buffer"): Buffer[]; + + /** + * Synchronous readdir(3) - read a directory. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used. + */ + export function readdirSync(path: PathLike, options?: { encoding?: string | null } | string | null): string[] | Buffer[]; + + /** + * Asynchronous close(2) - close a file descriptor. + * @param fd A file descriptor. + */ + export function close(fd: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace close { + /** + * Asynchronous close(2) - close a file descriptor. + * @param fd A file descriptor. + */ + export function __promisify__(fd: number): Promise<void>; + } + + /** + * Synchronous close(2) - close a file descriptor. + * @param fd A file descriptor. + */ + export function closeSync(fd: number): void; + + /** + * Asynchronous open(2) - open and possibly create a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. If not supplied, defaults to `0o666`. + */ + export function open(path: PathLike, flags: string | number, mode: string | number | undefined | null, callback: (err: NodeJS.ErrnoException, fd: number) => void): void; + + /** + * Asynchronous open(2) - open and possibly create a file. If the file is created, its mode will be `0o666`. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + */ + export function open(path: PathLike, flags: string | number, callback: (err: NodeJS.ErrnoException, fd: number) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace open { + /** + * Asynchronous open(2) - open and possibly create a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. If not supplied, defaults to `0o666`. + */ + export function __promisify__(path: PathLike, flags: string | number, mode?: string | number | null): Promise<number>; + } + + /** + * Synchronous open(2) - open and possibly create a file, returning a file descriptor.. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param mode A file mode. If a string is passed, it is parsed as an octal integer. If not supplied, defaults to `0o666`. + */ + export function openSync(path: PathLike, flags: string | number, mode?: string | number | null): number; + + /** + * Asynchronously change file timestamps of the file referenced by the supplied path. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param atime The last access time. If a string is provided, it will be coerced to number. + * @param mtime The last modified time. If a string is provided, it will be coerced to number. + */ + export function utimes(path: PathLike, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace utimes { + /** + * Asynchronously change file timestamps of the file referenced by the supplied path. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param atime The last access time. If a string is provided, it will be coerced to number. + * @param mtime The last modified time. If a string is provided, it will be coerced to number. + */ + export function __promisify__(path: PathLike, atime: string | number | Date, mtime: string | number | Date): Promise<void>; + } + + /** + * Synchronously change file timestamps of the file referenced by the supplied path. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * @param atime The last access time. If a string is provided, it will be coerced to number. + * @param mtime The last modified time. If a string is provided, it will be coerced to number. + */ + export function utimesSync(path: PathLike, atime: string | number | Date, mtime: string | number | Date): void; + + /** + * Asynchronously change file timestamps of the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param atime The last access time. If a string is provided, it will be coerced to number. + * @param mtime The last modified time. If a string is provided, it will be coerced to number. + */ + export function futimes(fd: number, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace futimes { + /** + * Asynchronously change file timestamps of the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param atime The last access time. If a string is provided, it will be coerced to number. + * @param mtime The last modified time. If a string is provided, it will be coerced to number. + */ + export function __promisify__(fd: number, atime: string | number | Date, mtime: string | number | Date): Promise<void>; + } + + /** + * Synchronously change file timestamps of the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param atime The last access time. If a string is provided, it will be coerced to number. + * @param mtime The last modified time. If a string is provided, it will be coerced to number. + */ + export function futimesSync(fd: number, atime: string | number | Date, mtime: string | number | Date): void; + + /** + * Asynchronous fsync(2) - synchronize a file's in-core state with the underlying storage device. + * @param fd A file descriptor. + */ + export function fsync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace fsync { + /** + * Asynchronous fsync(2) - synchronize a file's in-core state with the underlying storage device. + * @param fd A file descriptor. + */ + export function __promisify__(fd: number): Promise<void>; + } + + /** + * Synchronous fsync(2) - synchronize a file's in-core state with the underlying storage device. + * @param fd A file descriptor. + */ + export function fsyncSync(fd: number): void; + + /** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + * @param length The number of bytes to write. If not supplied, defaults to `buffer.length - offset`. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + */ + export function write<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined | null, length: number | undefined | null, position: number | undefined | null, callback: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void; + + /** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + * @param length The number of bytes to write. If not supplied, defaults to `buffer.length - offset`. + */ + export function write<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined | null, length: number | undefined | null, callback: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void; + + /** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + */ + export function write<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined | null, callback: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void; + + /** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + */ + export function write<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, callback: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void; + + /** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. If something other than a string is supplied it will be coerced to a string. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + * @param encoding The expected string encoding. + */ + export function write(fd: number, string: any, position: number | undefined | null, encoding: string | undefined | null, callback: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + + /** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. If something other than a string is supplied it will be coerced to a string. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + */ + export function write(fd: number, string: any, position: number | undefined | null, callback: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + + /** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. If something other than a string is supplied it will be coerced to a string. + */ + export function write(fd: number, string: any, callback: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace write { + /** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + * @param length The number of bytes to write. If not supplied, defaults to `buffer.length - offset`. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + */ + export function __promisify__<TBuffer extends Buffer | Uint8Array>(fd: number, buffer?: TBuffer, offset?: number, length?: number, position?: number | null): Promise<{ bytesWritten: number, buffer: TBuffer }>; + + /** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. If something other than a string is supplied it will be coerced to a string. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + * @param encoding The expected string encoding. + */ + export function __promisify__(fd: number, string: any, position?: number | null, encoding?: string | null): Promise<{ bytesWritten: number, buffer: string }>; + } + + /** + * Synchronously writes `buffer` to the file referenced by the supplied file descriptor, returning the number of bytes written. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + * @param length The number of bytes to write. If not supplied, defaults to `buffer.length - offset`. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + */ + export function writeSync(fd: number, buffer: Buffer | Uint8Array, offset?: number | null, length?: number | null, position?: number | null): number; + + /** + * Synchronously writes `string` to the file referenced by the supplied file descriptor, returning the number of bytes written. + * @param fd A file descriptor. + * @param string A string to write. If something other than a string is supplied it will be coerced to a string. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + * @param encoding The expected string encoding. + */ + export function writeSync(fd: number, string: any, position?: number | null, encoding?: string | null): number; + + /** + * Asynchronously reads data from the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param buffer The buffer that the data will be written to. + * @param offset The offset in the buffer at which to start writing. + * @param length The number of bytes to read. + * @param position The offset from the beginning of the file from which data should be read. If `null`, data will be read from the current position. + */ + export function read<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number, length: number, position: number | null, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: TBuffer) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace read { + /** + * @param fd A file descriptor. + * @param buffer The buffer that the data will be written to. + * @param offset The offset in the buffer at which to start writing. + * @param length The number of bytes to read. + * @param position The offset from the beginning of the file from which data should be read. If `null`, data will be read from the current position. + */ + export function __promisify__<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number, length: number, position: number | null): Promise<{ bytesRead: number, buffer: TBuffer }>; + } + + /** + * Synchronously reads data from the file referenced by the supplied file descriptor, returning the number of bytes read. + * @param fd A file descriptor. + * @param buffer The buffer that the data will be written to. + * @param offset The offset in the buffer at which to start writing. + * @param length The number of bytes to read. + * @param position The offset from the beginning of the file from which data should be read. If `null`, data will be read from the current position. + */ + export function readSync(fd: number, buffer: Buffer | Uint8Array, offset: number, length: number, position: number | null): number; + + /** + * Asynchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options An object that may contain an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function readFile(path: PathLike | number, options: { encoding?: null; flag?: string; } | undefined | null, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + + /** + * Asynchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options Either the encoding for the result, or an object that contains the encoding and an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function readFile(path: PathLike | number, options: { encoding: string; flag?: string; } | string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; + + /** + * Asynchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options Either the encoding for the result, or an object that contains the encoding and an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function readFile(path: PathLike | number, options: { encoding?: string | null; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void; + + /** + * Asynchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + */ + export function readFile(path: PathLike | number, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace readFile { + /** + * Asynchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options An object that may contain an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function __promisify__(path: PathLike | number, options?: { encoding?: null; flag?: string; } | null): Promise<Buffer>; + + /** + * Asynchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options Either the encoding for the result, or an object that contains the encoding and an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function __promisify__(path: PathLike | number, options: { encoding: string; flag?: string; } | string): Promise<string>; + + /** + * Asynchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options Either the encoding for the result, or an object that contains the encoding and an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function __promisify__(path: PathLike | number, options?: { encoding?: string | null; flag?: string; } | string | null): Promise<string | Buffer>; + } + + /** + * Synchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options An object that may contain an optional flag. If a flag is not provided, it defaults to `'r'`. + */ + export function readFileSync(path: PathLike | number, options?: { encoding?: null; flag?: string; } | null): Buffer; + + /** + * Synchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options Either the encoding for the result, or an object that contains the encoding and an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function readFileSync(path: PathLike | number, options: { encoding: string; flag?: string; } | string): string; + + /** + * Synchronously reads the entire contents of a file. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param options Either the encoding for the result, or an object that contains the encoding and an optional flag. + * If a flag is not provided, it defaults to `'r'`. + */ + export function readFileSync(path: PathLike | number, options?: { encoding?: string | null; flag?: string; } | string | null): string | Buffer; + + /** + * Asynchronously writes data to a file, replacing the file if it already exists. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `mode` is not supplied, the default of `0o666` is used. + * If `mode` is a string, it is parsed as an octal integer. + * If `flag` is not supplied, the default of `'w'` is used. + */ + export function writeFile(path: PathLike | number, data: any, options: { encoding?: string | null; mode?: number | string; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void; + + /** + * Asynchronously writes data to a file, replacing the file if it already exists. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + */ + export function writeFile(path: PathLike | number, data: any, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace writeFile { + /** + * Asynchronously writes data to a file, replacing the file if it already exists. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `mode` is not supplied, the default of `0o666` is used. + * If `mode` is a string, it is parsed as an octal integer. + * If `flag` is not supplied, the default of `'w'` is used. + */ + export function __promisify__(path: PathLike | number, data: any, options?: { encoding?: string | null; mode?: number | string; flag?: string; } | string | null): Promise<void>; + } + + /** + * Synchronously writes data to a file, replacing the file if it already exists. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `mode` is not supplied, the default of `0o666` is used. + * If `mode` is a string, it is parsed as an octal integer. + * If `flag` is not supplied, the default of `'w'` is used. + */ + export function writeFileSync(path: PathLike | number, data: any, options?: { encoding?: string | null; mode?: number | string; flag?: string; } | string | null): void; + + /** + * Asynchronously append data to a file, creating the file if it does not exist. + * @param file A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `mode` is not supplied, the default of `0o666` is used. + * If `mode` is a string, it is parsed as an octal integer. + * If `flag` is not supplied, the default of `'a'` is used. + */ + export function appendFile(file: PathLike | number, data: any, options: { encoding?: string | null, mode?: string | number, flag?: string } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void; + + /** + * Asynchronously append data to a file, creating the file if it does not exist. + * @param file A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + */ + export function appendFile(file: PathLike | number, data: any, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace appendFile { + /** + * Asynchronously append data to a file, creating the file if it does not exist. + * @param file A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `mode` is not supplied, the default of `0o666` is used. + * If `mode` is a string, it is parsed as an octal integer. + * If `flag` is not supplied, the default of `'a'` is used. + */ + export function __promisify__(file: PathLike | number, data: any, options?: { encoding?: string | null, mode?: string | number, flag?: string } | string | null): Promise<void>; + } + + /** + * Synchronously append data to a file, creating the file if it does not exist. + * @param file A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * If a file descriptor is provided, the underlying file will _not_ be closed automatically. + * @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string. + * @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `mode` is not supplied, the default of `0o666` is used. + * If `mode` is a string, it is parsed as an octal integer. + * If `flag` is not supplied, the default of `'a'` is used. + */ + export function appendFileSync(file: PathLike | number, data: any, options?: { encoding?: string | null; mode?: number | string; flag?: string; } | string | null): void; + + /** + * Watch for changes on `filename`. The callback `listener` will be called each time the file is accessed. + */ + export function watchFile(filename: PathLike, options: { persistent?: boolean; interval?: number; } | undefined, listener: (curr: Stats, prev: Stats) => void): void; + + /** + * Watch for changes on `filename`. The callback `listener` will be called each time the file is accessed. + * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function watchFile(filename: PathLike, listener: (curr: Stats, prev: Stats) => void): void; + + /** + * Stop watching for changes on `filename`. + * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function unwatchFile(filename: PathLike, listener?: (curr: Stats, prev: Stats) => void): void; + + /** + * Watch for changes on `filename`, where `filename` is either a file or a directory, returning an `FSWatcher`. + * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * @param options Either the encoding for the filename provided to the listener, or an object optionally specifying encoding, persistent, and recursive options. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `persistent` is not supplied, the default of `true` is used. + * If `recursive` is not supplied, the default of `false` is used. + */ + export function watch(filename: PathLike, options: { encoding?: BufferEncoding | null, persistent?: boolean, recursive?: boolean } | BufferEncoding | undefined | null, listener?: (event: string, filename: string) => void): FSWatcher; + + /** + * Watch for changes on `filename`, where `filename` is either a file or a directory, returning an `FSWatcher`. + * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * @param options Either the encoding for the filename provided to the listener, or an object optionally specifying encoding, persistent, and recursive options. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `persistent` is not supplied, the default of `true` is used. + * If `recursive` is not supplied, the default of `false` is used. + */ + export function watch(filename: PathLike, options: { encoding: "buffer", persistent?: boolean, recursive?: boolean } | "buffer", listener?: (event: string, filename: Buffer) => void): FSWatcher; + + /** + * Watch for changes on `filename`, where `filename` is either a file or a directory, returning an `FSWatcher`. + * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + * @param options Either the encoding for the filename provided to the listener, or an object optionally specifying encoding, persistent, and recursive options. + * If `encoding` is not supplied, the default of `'utf8'` is used. + * If `persistent` is not supplied, the default of `true` is used. + * If `recursive` is not supplied, the default of `false` is used. + */ + export function watch(filename: PathLike, options: { encoding?: string | null, persistent?: boolean, recursive?: boolean } | string | null, listener?: (event: string, filename: string | Buffer) => void): FSWatcher; + + /** + * Watch for changes on `filename`, where `filename` is either a file or a directory, returning an `FSWatcher`. + * @param filename A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function watch(filename: PathLike, listener?: (event: string, filename: string) => any): FSWatcher; + + /** + * Asynchronously tests whether or not the given path exists by checking with the file system. + * @deprecated + * @param path A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function exists(path: PathLike, callback: (exists: boolean) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace exists { + /** + * @param path A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + function __promisify__(path: PathLike): Promise<boolean>; + } + + /** + * Synchronously tests whether or not the given path exists by checking with the file system. + * @param path A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function existsSync(path: PathLike): boolean; + + export namespace constants { + // File Access Constants + + /** Constant for fs.access(). File is visible to the calling process. */ + export const F_OK: number; + + /** Constant for fs.access(). File can be read by the calling process. */ + export const R_OK: number; + + /** Constant for fs.access(). File can be written by the calling process. */ + export const W_OK: number; + + /** Constant for fs.access(). File can be executed by the calling process. */ + export const X_OK: number; + + // File Open Constants + + /** Constant for fs.open(). Flag indicating to open a file for read-only access. */ + export const O_RDONLY: number; + + /** Constant for fs.open(). Flag indicating to open a file for write-only access. */ + export const O_WRONLY: number; + + /** Constant for fs.open(). Flag indicating to open a file for read-write access. */ + export const O_RDWR: number; + + /** Constant for fs.open(). Flag indicating to create the file if it does not already exist. */ + export const O_CREAT: number; + + /** Constant for fs.open(). Flag indicating that opening a file should fail if the O_CREAT flag is set and the file already exists. */ + export const O_EXCL: number; + + /** Constant for fs.open(). Flag indicating that if path identifies a terminal device, opening the path shall not cause that terminal to become the controlling terminal for the process (if the process does not already have one). */ + export const O_NOCTTY: number; + + /** Constant for fs.open(). Flag indicating that if the file exists and is a regular file, and the file is opened successfully for write access, its length shall be truncated to zero. */ + export const O_TRUNC: number; + + /** Constant for fs.open(). Flag indicating that data will be appended to the end of the file. */ + export const O_APPEND: number; + + /** Constant for fs.open(). Flag indicating that the open should fail if the path is not a directory. */ + export const O_DIRECTORY: number; + + /** Constant for fs.open(). Flag indicating reading accesses to the file system will no longer result in an update to the atime information associated with the file. This flag is available on Linux operating systems only. */ + export const O_NOATIME: number; + + /** Constant for fs.open(). Flag indicating that the open should fail if the path is a symbolic link. */ + export const O_NOFOLLOW: number; + + /** Constant for fs.open(). Flag indicating that the file is opened for synchronous I/O. */ + export const O_SYNC: number; + + /** Constant for fs.open(). Flag indicating that the file is opened for synchronous I/O with write operations waiting for data integrity. */ + export const O_DSYNC: number; + + /** Constant for fs.open(). Flag indicating to open the symbolic link itself rather than the resource it is pointing to. */ + export const O_SYMLINK: number; + + /** Constant for fs.open(). When set, an attempt will be made to minimize caching effects of file I/O. */ + export const O_DIRECT: number; + + /** Constant for fs.open(). Flag indicating to open the file in nonblocking mode when possible. */ + export const O_NONBLOCK: number; + + // File Type Constants + + /** Constant for fs.Stats mode property for determining a file's type. Bit mask used to extract the file type code. */ + export const S_IFMT: number; + + /** Constant for fs.Stats mode property for determining a file's type. File type constant for a regular file. */ + export const S_IFREG: number; + + /** Constant for fs.Stats mode property for determining a file's type. File type constant for a directory. */ + export const S_IFDIR: number; + + /** Constant for fs.Stats mode property for determining a file's type. File type constant for a character-oriented device file. */ + export const S_IFCHR: number; + + /** Constant for fs.Stats mode property for determining a file's type. File type constant for a block-oriented device file. */ + export const S_IFBLK: number; + + /** Constant for fs.Stats mode property for determining a file's type. File type constant for a FIFO/pipe. */ + export const S_IFIFO: number; + + /** Constant for fs.Stats mode property for determining a file's type. File type constant for a symbolic link. */ + export const S_IFLNK: number; + + /** Constant for fs.Stats mode property for determining a file's type. File type constant for a socket. */ + export const S_IFSOCK: number; + + // File Mode Constants + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable, writable and executable by owner. */ + export const S_IRWXU: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable by owner. */ + export const S_IRUSR: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating writable by owner. */ + export const S_IWUSR: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating executable by owner. */ + export const S_IXUSR: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable, writable and executable by group. */ + export const S_IRWXG: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable by group. */ + export const S_IRGRP: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating writable by group. */ + export const S_IWGRP: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating executable by group. */ + export const S_IXGRP: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable, writable and executable by others. */ + export const S_IRWXO: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating readable by others. */ + export const S_IROTH: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating writable by others. */ + export const S_IWOTH: number; + + /** Constant for fs.Stats mode property for determining access permissions for a file. File mode indicating executable by others. */ + export const S_IXOTH: number; + + /** Constant for fs.copyFile. Flag indicating the destination file should not be overwritten if it already exists. */ + export const COPYFILE_EXCL: number; + } + + /** + * Asynchronously tests a user's permissions for the file specified by path. + * @param path A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function access(path: PathLike, mode: number | undefined, callback: (err: NodeJS.ErrnoException) => void): void; + + /** + * Asynchronously tests a user's permissions for the file specified by path. + * @param path A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function access(path: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace access { + /** + * Asynchronously tests a user's permissions for the file specified by path. + * @param path A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function __promisify__(path: PathLike, mode?: number): Promise<void>; + } + + /** + * Synchronously tests a user's permissions for the file specified by path. + * @param path A path to a file or directory. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function accessSync(path: PathLike, mode?: number): void; + + /** + * Returns a new `ReadStream` object. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function createReadStream(path: PathLike, options?: string | { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + autoClose?: boolean; + start?: number; + end?: number; + highWaterMark?: number; + }): ReadStream; + + /** + * Returns a new `WriteStream` object. + * @param path A path to a file. If a URL is provided, it must use the `file:` protocol. + * URL support is _experimental_. + */ + export function createWriteStream(path: PathLike, options?: string | { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + autoClose?: boolean; + start?: number; + }): WriteStream; + + /** + * Asynchronous fdatasync(2) - synchronize a file's in-core state with storage device. + * @param fd A file descriptor. + */ + export function fdatasync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace fdatasync { + /** + * Asynchronous fdatasync(2) - synchronize a file's in-core state with storage device. + * @param fd A file descriptor. + */ + export function __promisify__(fd: number): Promise<void>; + } + + /** + * Synchronous fdatasync(2) - synchronize a file's in-core state with storage device. + * @param fd A file descriptor. + */ + export function fdatasyncSync(fd: number): void; + + /** + * Asynchronously copies src to dest. By default, dest is overwritten if it already exists. + * No arguments other than a possible exception are given to the callback function. + * Node.js makes no guarantees about the atomicity of the copy operation. + * If an error occurs after the destination file has been opened for writing, Node.js will attempt + * to remove the destination. + * @param src A path to the source file. + * @param dest A path to the destination file. + */ + export function copyFile(src: PathLike, dest: PathLike, callback: (err: NodeJS.ErrnoException) => void): void; + /** + * Asynchronously copies src to dest. By default, dest is overwritten if it already exists. + * No arguments other than a possible exception are given to the callback function. + * Node.js makes no guarantees about the atomicity of the copy operation. + * If an error occurs after the destination file has been opened for writing, Node.js will attempt + * to remove the destination. + * @param src A path to the source file. + * @param dest A path to the destination file. + * @param flags An integer that specifies the behavior of the copy operation. The only supported flag is fs.constants.COPYFILE_EXCL, which causes the copy operation to fail if dest already exists. + */ + export function copyFile(src: PathLike, dest: PathLike, flags: number, callback: (err: NodeJS.ErrnoException) => void): void; + + // NOTE: This namespace provides design-time support for util.promisify. Exported members do not exist at runtime. + export namespace copyFile { + /** + * Asynchronously copies src to dest. By default, dest is overwritten if it already exists. + * No arguments other than a possible exception are given to the callback function. + * Node.js makes no guarantees about the atomicity of the copy operation. + * If an error occurs after the destination file has been opened for writing, Node.js will attempt + * to remove the destination. + * @param src A path to the source file. + * @param dest A path to the destination file. + * @param flags An optional integer that specifies the behavior of the copy operation. The only supported flag is fs.constants.COPYFILE_EXCL, which causes the copy operation to fail if dest already exists. + */ + export function __promisify__(src: PathLike, dst: PathLike, flags?: number): Promise<void>; + } + + /** + * Synchronously copies src to dest. By default, dest is overwritten if it already exists. + * Node.js makes no guarantees about the atomicity of the copy operation. + * If an error occurs after the destination file has been opened for writing, Node.js will attempt + * to remove the destination. + * @param src A path to the source file. + * @param dest A path to the destination file. + * @param flags An optional integer that specifies the behavior of the copy operation. The only supported flag is fs.constants.COPYFILE_EXCL, which causes the copy operation to fail if dest already exists. + */ + export function copyFileSync(src: PathLike, dest: PathLike, flags?: number): void; } declare module "path" { - /** * A parsed path object generated by path.parse() or consumed by path.format(). */ - export interface ParsedPath { + export interface ParsedPath { /** * The root of the path such as '/' or 'c:\' */ - root: string; + root: string; /** * The full directory path such as '/home/user/dir' or 'c:\path\dir' */ - dir: string; + dir: string; /** * The file name including extension (if any) such as 'index.html' */ - base: string; + base: string; /** * The file extension (if any) such as '.html' */ - ext: string; + ext: string; /** * The file name without extension (if any) such as 'index' */ - name: string; - } + name: string; + } + export interface FormatInputPathObject { + /** + * The root of the path such as '/' or 'c:\' + */ + root?: string; + /** + * The full directory path such as '/home/user/dir' or 'c:\path\dir' + */ + dir?: string; + /** + * The file name including extension (if any) such as 'index.html' + */ + base?: string; + /** + * The file extension (if any) such as '.html' + */ + ext?: string; + /** + * The file name without extension (if any) such as 'index' + */ + name?: string; + } /** * Normalize a string path, reducing '..' and '.' parts. @@ -2925,14 +4636,14 @@ declare module "path" { * * @param p string path to normalize. */ - export function normalize(p: string): string; + export function normalize(p: string): string; /** * Join all arguments together and normalize the resulting path. * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown. * * @param paths paths to join. */ - export function join(...paths: string[]): string; + export function join(...paths: string[]): string; /** * The right-most parameter is considered {to}. Other parameters are considered an array of {from}. * @@ -2942,27 +4653,24 @@ declare module "path" { * * @param pathSegments string paths to join. Non-string arguments are ignored. */ - export function resolve(...pathSegments: any[]): string; + export function resolve(...pathSegments: string[]): string; /** * Determines whether {path} is an absolute path. An absolute path will always resolve to the same location, regardless of the working directory. * * @param path path to test. */ - export function isAbsolute(path: string): boolean; + export function isAbsolute(path: string): boolean; /** * Solve the relative path from {from} to {to}. * At times we have two absolute paths, and we need to derive the relative path from one to the other. This is actually the reverse transform of path.resolve. - * - * @param from - * @param to */ - export function relative(from: string, to: string): string; + export function relative(from: string, to: string): string; /** * Return the directory name of a path. Similar to the Unix dirname command. * * @param p the path to evaluate. */ - export function dirname(p: string): string; + export function dirname(p: string): string; /** * Return the last portion of a path. Similar to the Unix basename command. * Often used to extract the file name from a fully qualified path. @@ -2970,176 +4678,177 @@ declare module "path" { * @param p the path to evaluate. * @param ext optionally, an extension to remove from the result. */ - export function basename(p: string, ext?: string): string; + export function basename(p: string, ext?: string): string; /** * Return the extension of the path, from the last '.' to end of string in the last portion of the path. * If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string * * @param p the path to evaluate. */ - export function extname(p: string): string; + export function extname(p: string): string; /** * The platform-specific file separator. '\\' or '/'. */ - export var sep: string; + export var sep: '\\' | '/'; /** * The platform-specific file delimiter. ';' or ':'. */ - export var delimiter: string; + export var delimiter: ';' | ':'; /** * Returns an object from a path string - the opposite of format(). * * @param pathString path to evaluate. */ - export function parse(pathString: string): ParsedPath; + export function parse(pathString: string): ParsedPath; /** * Returns a path string from an object - the opposite of parse(). * * @param pathString path to evaluate. */ - export function format(pathObject: ParsedPath): string; + export function format(pathObject: FormatInputPathObject): string; - export module posix { - export function normalize(p: string): string; - export function join(...paths: any[]): string; - export function resolve(...pathSegments: any[]): string; - export function isAbsolute(p: string): boolean; - export function relative(from: string, to: string): string; - export function dirname(p: string): string; - export function basename(p: string, ext?: string): string; - export function extname(p: string): string; - export var sep: string; - export var delimiter: string; - export function parse(p: string): ParsedPath; - export function format(pP: ParsedPath): string; - } + export module posix { + export function normalize(p: string): string; + export function join(...paths: any[]): string; + export function resolve(...pathSegments: any[]): string; + export function isAbsolute(p: string): boolean; + export function relative(from: string, to: string): string; + export function dirname(p: string): string; + export function basename(p: string, ext?: string): string; + export function extname(p: string): string; + export var sep: string; + export var delimiter: string; + export function parse(p: string): ParsedPath; + export function format(pP: FormatInputPathObject): string; + } - export module win32 { - export function normalize(p: string): string; - export function join(...paths: any[]): string; - export function resolve(...pathSegments: any[]): string; - export function isAbsolute(p: string): boolean; - export function relative(from: string, to: string): string; - export function dirname(p: string): string; - export function basename(p: string, ext?: string): string; - export function extname(p: string): string; - export var sep: string; - export var delimiter: string; - export function parse(p: string): ParsedPath; - export function format(pP: ParsedPath): string; - } + export module win32 { + export function normalize(p: string): string; + export function join(...paths: any[]): string; + export function resolve(...pathSegments: any[]): string; + export function isAbsolute(p: string): boolean; + export function relative(from: string, to: string): string; + export function dirname(p: string): string; + export function basename(p: string, ext?: string): string; + export function extname(p: string): string; + export var sep: string; + export var delimiter: string; + export function parse(p: string): ParsedPath; + export function format(pP: FormatInputPathObject): string; + } } declare module "string_decoder" { - export interface NodeStringDecoder { - write(buffer: Buffer): string; - end(buffer?: Buffer): string; - } - export var StringDecoder: { - new(encoding?: string): NodeStringDecoder; - }; + export interface NodeStringDecoder { + write(buffer: Buffer): string; + end(buffer?: Buffer): string; + } + export var StringDecoder: { + new(encoding?: string): NodeStringDecoder; + }; } declare module "tls" { - import * as crypto from "crypto"; - import * as net from "net"; - import * as stream from "stream"; + import * as crypto from "crypto"; + import * as dns from "dns"; + import * as net from "net"; + import * as stream from "stream"; - var CLIENT_RENEG_LIMIT: number; - var CLIENT_RENEG_WINDOW: number; + var CLIENT_RENEG_LIMIT: number; + var CLIENT_RENEG_WINDOW: number; - export interface Certificate { + export interface Certificate { /** * Country code. */ - C: string; + C: string; /** * Street. */ - ST: string; + ST: string; /** * Locality. */ - L: string; + L: string; /** * Organization. */ - O: string; + O: string; /** * Organizational unit. */ - OU: string; + OU: string; /** * Common name. */ - CN: string; - } + CN: string; + } - export interface PeerCertificate { - subject: Certificate; - issuer: Certificate; - subjectaltname: string; - infoAccess: { [index: string]: string[] }; - modulus: string; - exponent: string; - valid_from: string; - valid_to: string; - fingerprint: string; - ext_key_usage: string[]; - serialNumber: string; - raw: Buffer; - } + export interface PeerCertificate { + subject: Certificate; + issuer: Certificate; + subjectaltname: string; + infoAccess: { [index: string]: string[] | undefined }; + modulus: string; + exponent: string; + valid_from: string; + valid_to: string; + fingerprint: string; + ext_key_usage: string[]; + serialNumber: string; + raw: Buffer; + } - export interface DetailedPeerCertificate extends PeerCertificate { - issuerCertificate: DetailedPeerCertificate; - } + export interface DetailedPeerCertificate extends PeerCertificate { + issuerCertificate: DetailedPeerCertificate; + } - export interface CipherNameAndProtocol { + export interface CipherNameAndProtocol { /** * The cipher name. */ - name: string; + name: string; /** * SSL/TLS protocol version. */ - version: string; - } + version: string; + } - export class TLSSocket extends net.Socket { + export class TLSSocket extends net.Socket { /** * Construct a new tls.TLSSocket object from an existing TCP socket. */ - constructor(socket: net.Socket, options?: { + constructor(socket: net.Socket, options?: { /** * An optional TLS context object from tls.createSecureContext() */ - secureContext?: SecureContext, + secureContext?: SecureContext, /** * If true the TLS socket will be instantiated in server-mode. * Defaults to false. */ - isServer?: boolean, + isServer?: boolean, /** * An optional net.Server instance. */ - server?: net.Server, + server?: net.Server, /** * If true the server will request a certificate from clients that * connect and attempt to verify that certificate. Defaults to * false. */ - requestCert?: boolean, + requestCert?: boolean, /** * If true the server will reject any connection which is not * authorized with the list of supplied CAs. This option only has an * effect if requestCert is true. Defaults to false. */ - rejectUnauthorized?: boolean, + rejectUnauthorized?: boolean, /** * An array of strings or a Buffer naming possible NPN protocols. * (Protocols should be ordered by their priority.) */ - NPNProtocols?: string[] | Buffer, + NPNProtocols?: string[] | Buffer[] | Uint8Array[] | Buffer | Uint8Array, /** * An array of strings or a Buffer naming possible ALPN protocols. * (Protocols should be ordered by their priority.) When the server @@ -3147,7 +4856,7 @@ declare module "tls" { * precedence over NPN and the server does not send an NPN extension * to the client. */ - ALPNProtocols?: string[] | Buffer, + ALPNProtocols?: string[] | Buffer[] | Uint8Array[] | Buffer | Uint8Array, /** * SNICallback(servername, cb) <Function> A function that will be * called if the client supports SNI TLS extension. Two arguments @@ -3157,99 +4866,81 @@ declare module "tls" { * SecureContext.) If SNICallback wasn't provided the default callback * with high-level API will be used (see below). */ - SNICallback?: Function, + SNICallback?: (servername: string, cb: (err: Error | null, ctx: SecureContext) => void) => void, /** * An optional Buffer instance containing a TLS session. */ - session?: Buffer, + session?: Buffer, /** * If true, specifies that the OCSP status request extension will be * added to the client hello and an 'OCSPResponse' event will be * emitted on the socket before establishing a secure communication */ - requestOCSP?: boolean - }); - /** - * Returns the bound address, the address family name and port of the underlying socket as reported by - * the operating system. - * @returns {any} - An object with three properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' }. - */ - address(): { port: number; family: string; address: string }; + requestOCSP?: boolean + }); + /** * A boolean that is true if the peer certificate was signed by one of the specified CAs, otherwise false. */ - authorized: boolean; + authorized: boolean; /** * The reason why the peer's certificate has not been verified. * This property becomes available only when tlsSocket.authorized === false. */ - authorizationError: Error; + authorizationError: Error; /** * Static boolean value, always true. * May be used to distinguish TLS sockets from regular ones. */ - encrypted: boolean; + encrypted: boolean; /** * Returns an object representing the cipher name and the SSL/TLS protocol version of the current connection. - * @returns {CipherNameAndProtocol} - Returns an object representing the cipher name + * @returns Returns an object representing the cipher name * and the SSL/TLS protocol version of the current connection. */ - getCipher(): CipherNameAndProtocol; + getCipher(): CipherNameAndProtocol; /** * Returns an object representing the peer's certificate. * The returned object has some properties corresponding to the field of the certificate. * If detailed argument is true the full chain with issuer property will be returned, * if false only the top certificate without issuer property. * If the peer does not provide a certificate, it returns null or an empty object. - * @param {boolean} detailed - If true; the full chain with issuer property will be returned. - * @returns {PeerCertificate | DetailedPeerCertificate} - An object representing the peer's certificate. + * @param detailed - If true; the full chain with issuer property will be returned. + * @returns An object representing the peer's certificate. */ - getPeerCertificate(detailed: true): DetailedPeerCertificate; - getPeerCertificate(detailed?: false): PeerCertificate; - getPeerCertificate(detailed?: boolean): PeerCertificate | DetailedPeerCertificate; + getPeerCertificate(detailed: true): DetailedPeerCertificate; + getPeerCertificate(detailed?: false): PeerCertificate; + getPeerCertificate(detailed?: boolean): PeerCertificate | DetailedPeerCertificate; + /** + * Returns a string containing the negotiated SSL/TLS protocol version of the current connection. + * The value `'unknown'` will be returned for connected sockets that have not completed the handshaking process. + * The value `null` will be returned for server sockets or disconnected client sockets. + * See https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_version.html for more information. + * @returns negotiated SSL/TLS protocol version of the current connection + */ + getProtocol(): string | null; /** * Could be used to speed up handshake establishment when reconnecting to the server. - * @returns {any} - ASN.1 encoded TLS session or undefined if none was negotiated. + * @returns ASN.1 encoded TLS session or undefined if none was negotiated. */ - getSession(): any; + getSession(): any; /** * NOTE: Works only with client TLS sockets. * Useful only for debugging, for session reuse provide session option to tls.connect(). - * @returns {any} - TLS session ticket or undefined if none was negotiated. + * @returns TLS session ticket or undefined if none was negotiated. */ - getTLSTicket(): any; - /** - * The string representation of the local IP address. - */ - localAddress: string; - /** - * The numeric representation of the local port. - */ - localPort: number; - /** - * The string representation of the remote IP address. - * For example, '74.125.127.100' or '2001:4860:a005::68'. - */ - remoteAddress: string; - /** - * The string representation of the remote IP family. 'IPv4' or 'IPv6'. - */ - remoteFamily: string; - /** - * The numeric representation of the remote port. For example, 443. - */ - remotePort: number; + getTLSTicket(): any; /** * Initiate TLS renegotiation process. * * NOTE: Can be used to request peer's certificate after the secure connection has been established. * ANOTHER NOTE: When running as the server, socket will be destroyed with an error after handshakeTimeout timeout. - * @param {TlsOptions} options - The options may contain the following fields: rejectUnauthorized, + * @param options - The options may contain the following fields: rejectUnauthorized, * requestCert (See tls.createServer() for details). - * @param {Function} callback - callback(err) will be executed with null as err, once the renegotiation + * @param callback - callback(err) will be executed with null as err, once the renegotiation * is successfully completed. */ - renegotiate(options: TlsOptions, callback: (err: Error) => any): any; + renegotiate(options: { rejectUnauthorized?: boolean, requestCert?: boolean }, callback: (err: Error | null) => void): any; /** * Set maximum TLS fragment size (default and maximum value is: 16384, minimum is: 512). * Smaller fragment size decreases buffering latency on the client: large fragments are buffered by @@ -3257,97 +4948,74 @@ declare module "tls" { * large fragments can span multiple roundtrips, and their processing can be delayed due to packet * loss or reordering. However, smaller fragments add extra TLS framing bytes and CPU overhead, * which may decrease overall server throughput. - * @param {number} size - TLS fragment size (default and maximum value is: 16384, minimum is: 512). - * @returns {boolean} - Returns true on success, false otherwise. + * @param size - TLS fragment size (default and maximum value is: 16384, minimum is: 512). + * @returns Returns true on success, false otherwise. */ - setMaxSendFragment(size: number): boolean; + setMaxSendFragment(size: number): boolean; /** * events.EventEmitter * 1. OCSPResponse * 2. secureConnect - **/ - addListener(event: string, listener: Function): this; - addListener(event: "OCSPResponse", listener: (response: Buffer) => void): this; - addListener(event: "secureConnect", listener: () => void): this; + */ + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "OCSPResponse", listener: (response: Buffer) => void): this; + addListener(event: "secureConnect", listener: () => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "OCSPResponse", response: Buffer): boolean; - emit(event: "secureConnect"): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "OCSPResponse", response: Buffer): boolean; + emit(event: "secureConnect"): boolean; - on(event: string, listener: Function): this; - on(event: "OCSPResponse", listener: (response: Buffer) => void): this; - on(event: "secureConnect", listener: () => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "OCSPResponse", listener: (response: Buffer) => void): this; + on(event: "secureConnect", listener: () => void): this; - once(event: string, listener: Function): this; - once(event: "OCSPResponse", listener: (response: Buffer) => void): this; - once(event: "secureConnect", listener: () => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "OCSPResponse", listener: (response: Buffer) => void): this; + once(event: "secureConnect", listener: () => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "OCSPResponse", listener: (response: Buffer) => void): this; - prependListener(event: "secureConnect", listener: () => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "OCSPResponse", listener: (response: Buffer) => void): this; + prependListener(event: "secureConnect", listener: () => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "OCSPResponse", listener: (response: Buffer) => void): this; - prependOnceListener(event: "secureConnect", listener: () => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "OCSPResponse", listener: (response: Buffer) => void): this; + prependOnceListener(event: "secureConnect", listener: () => void): this; + } - export interface TlsOptions { - host?: string; - port?: number; - pfx?: string | Buffer[]; - key?: string | string[] | Buffer | any[]; - passphrase?: string; - cert?: string | string[] | Buffer | Buffer[]; - ca?: string | string[] | Buffer | Buffer[]; - crl?: string | string[]; - ciphers?: string; - honorCipherOrder?: boolean; - requestCert?: boolean; - rejectUnauthorized?: boolean; - NPNProtocols?: string[] | Buffer; - SNICallback?: (servername: string, cb: (err: Error, ctx: SecureContext) => any) => any; - ecdhCurve?: string; - dhparam?: string | Buffer; - handshakeTimeout?: number; - ALPNProtocols?: string[] | Buffer; - sessionTimeout?: number; - ticketKeys?: any; - sessionIdContext?: string; - secureProtocol?: string; - } + export interface TlsOptions extends SecureContextOptions { + handshakeTimeout?: number; + requestCert?: boolean; + rejectUnauthorized?: boolean; + NPNProtocols?: string[] | Buffer[] | Uint8Array[] | Buffer | Uint8Array; + ALPNProtocols?: string[] | Buffer[] | Uint8Array[] | Buffer | Uint8Array; + SNICallback?: (servername: string, cb: (err: Error | null, ctx: SecureContext) => void) => void; + sessionTimeout?: number; + ticketKeys?: Buffer; + } - export interface ConnectionOptions { - host?: string; - port?: number; - socket?: net.Socket; - pfx?: string | Buffer - key?: string | string[] | Buffer | Buffer[]; - passphrase?: string; - cert?: string | string[] | Buffer | Buffer[]; - ca?: string | Buffer | (string | Buffer)[]; - rejectUnauthorized?: boolean; - NPNProtocols?: (string | Buffer)[]; - servername?: string; - path?: string; - ALPNProtocols?: (string | Buffer)[]; - checkServerIdentity?: (servername: string, cert: string | Buffer | (string | Buffer)[]) => any; - secureProtocol?: string; - secureContext?: Object; - session?: Buffer; - minDHSize?: number; - } + export interface ConnectionOptions extends SecureContextOptions { + host?: string; + port?: number; + path?: string; // Creates unix socket connection to path. If this option is specified, `host` and `port` are ignored. + socket?: net.Socket; // Establish secure connection on a given socket rather than creating a new socket + rejectUnauthorized?: boolean; // Defaults to true + NPNProtocols?: string[] | Buffer[] | Uint8Array[] | Buffer | Uint8Array; + ALPNProtocols?: string[] | Buffer[] | Uint8Array[] | Buffer | Uint8Array; + checkServerIdentity?: typeof checkServerIdentity; + servername?: string; // SNI TLS Extension + session?: Buffer; + minDHSize?: number; + secureContext?: SecureContext; // If not provided, the entire ConnectionOptions object will be passed to tls.createSecureContext() + lookup?: net.LookupFunction; + } - export interface Server extends net.Server { - close(callback?: Function): Server; - address(): { port: number; family: string; address: string; }; - addContext(hostName: string, credentials: { - key: string; - cert: string; - ca: string; - }): void; - maxConnections: number; - connections: number; + export class Server extends net.Server { + addContext(hostName: string, credentials: { + key: string; + cert: string; + ca: string; + }): void; /** * events.EventEmitter @@ -3356,1022 +5024,2216 @@ declare module "tls" { * 3. OCSPRequest * 4. resumeSession * 5. secureConnection - **/ - addListener(event: string, listener: Function): this; - addListener(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; - addListener(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; - addListener(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; - addListener(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; - addListener(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; + */ + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; + addListener(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; + addListener(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; + addListener(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; + addListener(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "tlsClientError", err: Error, tlsSocket: TLSSocket): boolean; - emit(event: "newSession", sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void): boolean; - emit(event: "OCSPRequest", certificate: Buffer, issuer: Buffer, callback: Function): boolean; - emit(event: "resumeSession", sessionId: any, callback: (err: Error, sessionData: any) => void): boolean; - emit(event: "secureConnection", tlsSocket: TLSSocket): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "tlsClientError", err: Error, tlsSocket: TLSSocket): boolean; + emit(event: "newSession", sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void): boolean; + emit(event: "OCSPRequest", certificate: Buffer, issuer: Buffer, callback: Function): boolean; + emit(event: "resumeSession", sessionId: any, callback: (err: Error, sessionData: any) => void): boolean; + emit(event: "secureConnection", tlsSocket: TLSSocket): boolean; - on(event: string, listener: Function): this; - on(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; - on(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; - on(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; - on(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; - on(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; + on(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; + on(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; + on(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; + on(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; - once(event: string, listener: Function): this; - once(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; - once(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; - once(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; - once(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; - once(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; + once(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; + once(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; + once(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; + once(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; - prependListener(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; - prependListener(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; - prependListener(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; - prependListener(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; + prependListener(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; + prependListener(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; + prependListener(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; + prependListener(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; - prependOnceListener(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; - prependOnceListener(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; - prependOnceListener(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; - prependOnceListener(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; - } + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "tlsClientError", listener: (err: Error, tlsSocket: TLSSocket) => void): this; + prependOnceListener(event: "newSession", listener: (sessionId: any, sessionData: any, callback: (err: Error, resp: Buffer) => void) => void): this; + prependOnceListener(event: "OCSPRequest", listener: (certificate: Buffer, issuer: Buffer, callback: Function) => void): this; + prependOnceListener(event: "resumeSession", listener: (sessionId: any, callback: (err: Error, sessionData: any) => void) => void): this; + prependOnceListener(event: "secureConnection", listener: (tlsSocket: TLSSocket) => void): this; + } - export interface ClearTextStream extends stream.Duplex { - authorized: boolean; - authorizationError: Error; - getPeerCertificate(): any; - getCipher: { - name: string; - version: string; - }; - address: { - port: number; - family: string; - address: string; - }; - remoteAddress: string; - remotePort: number; - } + export interface ClearTextStream extends stream.Duplex { + authorized: boolean; + authorizationError: Error; + getPeerCertificate(): any; + getCipher: { + name: string; + version: string; + }; + address: { + port: number; + family: string; + address: string; + }; + remoteAddress: string; + remotePort: number; + } - export interface SecurePair { - encrypted: any; - cleartext: any; - } + export interface SecurePair { + encrypted: any; + cleartext: any; + } - export interface SecureContextOptions { - pfx?: string | Buffer; - key?: string | Buffer; - passphrase?: string; - cert?: string | Buffer; - ca?: string | Buffer; - crl?: string | string[] - ciphers?: string; - honorCipherOrder?: boolean; - } + export interface SecureContextOptions { + pfx?: string | Buffer | Array<string | Buffer | Object>; + key?: string | Buffer | Array<Buffer | Object>; + passphrase?: string; + cert?: string | Buffer | Array<string | Buffer>; + ca?: string | Buffer | Array<string | Buffer>; + ciphers?: string; + honorCipherOrder?: boolean; + ecdhCurve?: string; + crl?: string | Buffer | Array<string | Buffer>; + dhparam?: string | Buffer; + secureOptions?: number; // Value is a numeric bitmask of the `SSL_OP_*` options + secureProtocol?: string; // SSL Method, e.g. SSLv23_method + sessionIdContext?: string; + } - export interface SecureContext { - context: any; - } + export interface SecureContext { + context: any; + } - export function createServer(options: TlsOptions, secureConnectionListener?: (socket: TLSSocket) => void): Server; - export function connect(options: ConnectionOptions, secureConnectionListener?: () => void): TLSSocket; - export function connect(port: number, host?: string, options?: ConnectionOptions, secureConnectListener?: () => void): TLSSocket; - export function connect(port: number, options?: ConnectionOptions, secureConnectListener?: () => void): TLSSocket; - export function createSecurePair(credentials?: crypto.Credentials, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair; - export function createSecureContext(details: SecureContextOptions): SecureContext; + /* + * Verifies the certificate `cert` is issued to host `host`. + * @host The hostname to verify the certificate against + * @cert PeerCertificate representing the peer's certificate + * + * Returns Error object, populating it with the reason, host and cert on failure. On success, returns undefined. + */ + export function checkServerIdentity(host: string, cert: PeerCertificate): Error | undefined; + export function createServer(options: TlsOptions, secureConnectionListener?: (socket: TLSSocket) => void): Server; + export function connect(options: ConnectionOptions, secureConnectionListener?: () => void): TLSSocket; + export function connect(port: number, host?: string, options?: ConnectionOptions, secureConnectListener?: () => void): TLSSocket; + export function connect(port: number, options?: ConnectionOptions, secureConnectListener?: () => void): TLSSocket; + export function createSecurePair(credentials?: crypto.Credentials, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair; + export function createSecureContext(details: SecureContextOptions): SecureContext; + export function getCiphers(): string[]; + + export var DEFAULT_ECDH_CURVE: string; } declare module "crypto" { - export interface Certificate { - exportChallenge(spkac: string | Buffer): Buffer; - exportPublicKey(spkac: string | Buffer): Buffer; - verifySpkac(spkac: Buffer): boolean; - } - export var Certificate: { - new(): Certificate; - (): Certificate; - } + export interface Certificate { + exportChallenge(spkac: string | Buffer): Buffer; + exportPublicKey(spkac: string | Buffer): Buffer; + verifySpkac(spkac: Buffer): boolean; + } + export var Certificate: { + new(): Certificate; + (): Certificate; + }; - export var fips: boolean; + export var fips: boolean; - export interface CredentialDetails { - pfx: string; - key: string; - passphrase: string; - cert: string; - ca: string | string[]; - crl: string | string[]; - ciphers: string; - } - export interface Credentials { context?: any; } - export function createCredentials(details: CredentialDetails): Credentials; - export function createHash(algorithm: string): Hash; - export function createHmac(algorithm: string, key: string | Buffer): Hmac; + export interface CredentialDetails { + pfx: string; + key: string; + passphrase: string; + cert: string; + ca: string | string[]; + crl: string | string[]; + ciphers: string; + } + export interface Credentials { context?: any; } + export function createCredentials(details: CredentialDetails): Credentials; + export function createHash(algorithm: string): Hash; + export function createHmac(algorithm: string, key: string | Buffer): Hmac; - type Utf8AsciiLatin1Encoding = "utf8" | "ascii" | "latin1"; - type HexBase64Latin1Encoding = "latin1" | "hex" | "base64"; - type Utf8AsciiBinaryEncoding = "utf8" | "ascii" | "binary"; - type HexBase64BinaryEncoding = "binary" | "base64" | "hex"; - type ECDHKeyFormat = "compressed" | "uncompressed" | "hybrid"; + type Utf8AsciiLatin1Encoding = "utf8" | "ascii" | "latin1"; + type HexBase64Latin1Encoding = "latin1" | "hex" | "base64"; + type Utf8AsciiBinaryEncoding = "utf8" | "ascii" | "binary"; + type HexBase64BinaryEncoding = "binary" | "base64" | "hex"; + type ECDHKeyFormat = "compressed" | "uncompressed" | "hybrid"; - export interface Hash extends NodeJS.ReadWriteStream { - update(data: string | Buffer): Hash; - update(data: string | Buffer, input_encoding: Utf8AsciiLatin1Encoding): Hash; - digest(): Buffer; - digest(encoding: HexBase64Latin1Encoding): string; - } - export interface Hmac extends NodeJS.ReadWriteStream { - update(data: string | Buffer): Hmac; - update(data: string | Buffer, input_encoding: Utf8AsciiLatin1Encoding): Hmac; - digest(): Buffer; - digest(encoding: HexBase64Latin1Encoding): string; - } - export function createCipher(algorithm: string, password: any): Cipher; - export function createCipheriv(algorithm: string, key: any, iv: any): Cipher; - export interface Cipher extends NodeJS.ReadWriteStream { - update(data: Buffer): Buffer; - update(data: string, input_encoding: Utf8AsciiBinaryEncoding): Buffer; - update(data: Buffer, input_encoding: any, output_encoding: HexBase64BinaryEncoding): string; - update(data: string, input_encoding: Utf8AsciiBinaryEncoding, output_encoding: HexBase64BinaryEncoding): string; - final(): Buffer; - final(output_encoding: string): string; - setAutoPadding(auto_padding?: boolean): void; - getAuthTag(): Buffer; - setAAD(buffer: Buffer): void; - } - export function createDecipher(algorithm: string, password: any): Decipher; - export function createDecipheriv(algorithm: string, key: any, iv: any): Decipher; - export interface Decipher extends NodeJS.ReadWriteStream { - update(data: Buffer): Buffer; - update(data: string, input_encoding: HexBase64BinaryEncoding): Buffer; - update(data: Buffer, input_encoding: any, output_encoding: Utf8AsciiBinaryEncoding): string; - update(data: string, input_encoding: HexBase64BinaryEncoding, output_encoding: Utf8AsciiBinaryEncoding): string; - final(): Buffer; - final(output_encoding: string): string; - setAutoPadding(auto_padding?: boolean): void; - setAuthTag(tag: Buffer): void; - setAAD(buffer: Buffer): void; - } - export function createSign(algorithm: string): Signer; - export interface Signer extends NodeJS.WritableStream { - update(data: string | Buffer): Signer; - update(data: string | Buffer, input_encoding: Utf8AsciiLatin1Encoding): Signer; - sign(private_key: string | { key: string; passphrase: string }): Buffer; - sign(private_key: string | { key: string; passphrase: string }, output_format: HexBase64Latin1Encoding): string; - } - export function createVerify(algorith: string): Verify; - export interface Verify extends NodeJS.WritableStream { - update(data: string | Buffer): Verify; - update(data: string | Buffer, input_encoding: Utf8AsciiLatin1Encoding): Verify; - verify(object: string, signature: Buffer): boolean; - verify(object: string, signature: string, signature_format: HexBase64Latin1Encoding): boolean; - } - export function createDiffieHellman(prime_length: number, generator?: number): DiffieHellman; - export function createDiffieHellman(prime: Buffer): DiffieHellman; - export function createDiffieHellman(prime: string, prime_encoding: HexBase64Latin1Encoding): DiffieHellman; - export function createDiffieHellman(prime: string, prime_encoding: HexBase64Latin1Encoding, generator: number | Buffer): DiffieHellman; - export function createDiffieHellman(prime: string, prime_encoding: HexBase64Latin1Encoding, generator: string, generator_encoding: HexBase64Latin1Encoding): DiffieHellman; - export interface DiffieHellman { - generateKeys(): Buffer; - generateKeys(encoding: HexBase64Latin1Encoding): string; - computeSecret(other_public_key: Buffer): Buffer; - computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding): Buffer; - computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding, output_encoding: HexBase64Latin1Encoding): string; - getPrime(): Buffer; - getPrime(encoding: HexBase64Latin1Encoding): string; - getGenerator(): Buffer; - getGenerator(encoding: HexBase64Latin1Encoding): string; - getPublicKey(): Buffer; - getPublicKey(encoding: HexBase64Latin1Encoding): string; - getPrivateKey(): Buffer; - getPrivateKey(encoding: HexBase64Latin1Encoding): string; - setPublicKey(public_key: Buffer): void; - setPublicKey(public_key: string, encoding: string): void; - setPrivateKey(private_key: Buffer): void; - setPrivateKey(private_key: string, encoding: string): void; - verifyError: number; - } - export function getDiffieHellman(group_name: string): DiffieHellman; - export function pbkdf2(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number, digest: string, callback: (err: Error, derivedKey: Buffer) => any): void; - export function pbkdf2Sync(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number, digest: string): Buffer; - export function randomBytes(size: number): Buffer; - export function randomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void; - export function pseudoRandomBytes(size: number): Buffer; - export function pseudoRandomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void; - export function randomFillSync(buffer: Buffer | Uint8Array, offset?: number, size?: number): Buffer; - export function randomFill(buffer: Buffer, callback: (err: Error, buf: Buffer) => void): void; - export function randomFill(buffer: Uint8Array, callback: (err: Error, buf: Uint8Array) => void): void; - export function randomFill(buffer: Buffer, offset: number, callback: (err: Error, buf: Buffer) => void): void; - export function randomFill(buffer: Uint8Array, offset: number, callback: (err: Error, buf: Uint8Array) => void): void; - export function randomFill(buffer: Buffer, offset: number, size: number, callback: (err: Error, buf: Buffer) => void): void; - export function randomFill(buffer: Uint8Array, offset: number, size: number, callback: (err: Error, buf: Uint8Array) => void): void; - export interface RsaPublicKey { - key: string; - padding?: number; - } - export interface RsaPrivateKey { - key: string; - passphrase?: string, - padding?: number; - } - export function publicEncrypt(public_key: string | RsaPublicKey, buffer: Buffer): Buffer - export function privateDecrypt(private_key: string | RsaPrivateKey, buffer: Buffer): Buffer - export function privateEncrypt(private_key: string | RsaPrivateKey, buffer: Buffer): Buffer - export function publicDecrypt(public_key: string | RsaPublicKey, buffer: Buffer): Buffer - export function getCiphers(): string[]; - export function getCurves(): string[]; - export function getHashes(): string[]; - export interface ECDH { - generateKeys(): Buffer; - generateKeys(encoding: HexBase64Latin1Encoding): string; - generateKeys(encoding: HexBase64Latin1Encoding, format: ECDHKeyFormat): string; - computeSecret(other_public_key: Buffer): Buffer; - computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding): Buffer; - computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding, output_encoding: HexBase64Latin1Encoding): string; - getPrivateKey(): Buffer; - getPrivateKey(encoding: HexBase64Latin1Encoding): string; - getPublicKey(): Buffer; - getPublicKey(encoding: HexBase64Latin1Encoding): string; - getPublicKey(encoding: HexBase64Latin1Encoding, format: ECDHKeyFormat): string; - setPrivateKey(private_key: Buffer): void; - setPrivateKey(private_key: string, encoding: HexBase64Latin1Encoding): void; - } - export function createECDH(curve_name: string): ECDH; - export function timingSafeEqual(a: Buffer, b: Buffer): boolean; - export var DEFAULT_ENCODING: string; + export interface Hash extends NodeJS.ReadWriteStream { + update(data: string | Buffer | DataView): Hash; + update(data: string | Buffer | DataView, input_encoding: Utf8AsciiLatin1Encoding): Hash; + digest(): Buffer; + digest(encoding: HexBase64Latin1Encoding): string; + } + export interface Hmac extends NodeJS.ReadWriteStream { + update(data: string | Buffer | DataView): Hmac; + update(data: string | Buffer | DataView, input_encoding: Utf8AsciiLatin1Encoding): Hmac; + digest(): Buffer; + digest(encoding: HexBase64Latin1Encoding): string; + } + export function createCipher(algorithm: string, password: any): Cipher; + export function createCipheriv(algorithm: string, key: any, iv: any): Cipher; + export interface Cipher extends NodeJS.ReadWriteStream { + update(data: Buffer | DataView): Buffer; + update(data: string, input_encoding: Utf8AsciiBinaryEncoding): Buffer; + update(data: Buffer | DataView, input_encoding: any, output_encoding: HexBase64BinaryEncoding): string; + update(data: string, input_encoding: Utf8AsciiBinaryEncoding, output_encoding: HexBase64BinaryEncoding): string; + final(): Buffer; + final(output_encoding: string): string; + setAutoPadding(auto_padding?: boolean): this; + getAuthTag(): Buffer; + setAAD(buffer: Buffer): this; + } + export function createDecipher(algorithm: string, password: any): Decipher; + export function createDecipheriv(algorithm: string, key: any, iv: any): Decipher; + export interface Decipher extends NodeJS.ReadWriteStream { + update(data: Buffer | DataView): Buffer; + update(data: string, input_encoding: HexBase64BinaryEncoding): Buffer; + update(data: Buffer | DataView, input_encoding: any, output_encoding: Utf8AsciiBinaryEncoding): string; + update(data: string, input_encoding: HexBase64BinaryEncoding, output_encoding: Utf8AsciiBinaryEncoding): string; + final(): Buffer; + final(output_encoding: string): string; + setAutoPadding(auto_padding?: boolean): this; + setAuthTag(tag: Buffer): this; + setAAD(buffer: Buffer): this; + } + export function createSign(algorithm: string): Signer; + export interface Signer extends NodeJS.WritableStream { + update(data: string | Buffer | DataView): Signer; + update(data: string | Buffer | DataView, input_encoding: Utf8AsciiLatin1Encoding): Signer; + sign(private_key: string | { key: string; passphrase: string }): Buffer; + sign(private_key: string | { key: string; passphrase: string }, output_format: HexBase64Latin1Encoding): string; + } + export function createVerify(algorith: string): Verify; + export interface Verify extends NodeJS.WritableStream { + update(data: string | Buffer | DataView): Verify; + update(data: string | Buffer | DataView, input_encoding: Utf8AsciiLatin1Encoding): Verify; + verify(object: string | Object, signature: Buffer | DataView): boolean; + verify(object: string | Object, signature: string, signature_format: HexBase64Latin1Encoding): boolean; + // https://nodejs.org/api/crypto.html#crypto_verifier_verify_object_signature_signature_format + // The signature field accepts a TypedArray type, but it is only available starting ES2017 + } + export function createDiffieHellman(prime_length: number, generator?: number): DiffieHellman; + export function createDiffieHellman(prime: Buffer): DiffieHellman; + export function createDiffieHellman(prime: string, prime_encoding: HexBase64Latin1Encoding): DiffieHellman; + export function createDiffieHellman(prime: string, prime_encoding: HexBase64Latin1Encoding, generator: number | Buffer): DiffieHellman; + export function createDiffieHellman(prime: string, prime_encoding: HexBase64Latin1Encoding, generator: string, generator_encoding: HexBase64Latin1Encoding): DiffieHellman; + export interface DiffieHellman { + generateKeys(): Buffer; + generateKeys(encoding: HexBase64Latin1Encoding): string; + computeSecret(other_public_key: Buffer): Buffer; + computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding): Buffer; + computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding, output_encoding: HexBase64Latin1Encoding): string; + getPrime(): Buffer; + getPrime(encoding: HexBase64Latin1Encoding): string; + getGenerator(): Buffer; + getGenerator(encoding: HexBase64Latin1Encoding): string; + getPublicKey(): Buffer; + getPublicKey(encoding: HexBase64Latin1Encoding): string; + getPrivateKey(): Buffer; + getPrivateKey(encoding: HexBase64Latin1Encoding): string; + setPublicKey(public_key: Buffer): void; + setPublicKey(public_key: string, encoding: string): void; + setPrivateKey(private_key: Buffer): void; + setPrivateKey(private_key: string, encoding: string): void; + verifyError: number; + } + export function getDiffieHellman(group_name: string): DiffieHellman; + export function pbkdf2(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number, digest: string, callback: (err: Error, derivedKey: Buffer) => any): void; + export function pbkdf2Sync(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number, digest: string): Buffer; + export function randomBytes(size: number): Buffer; + export function randomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void; + export function pseudoRandomBytes(size: number): Buffer; + export function pseudoRandomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void; + export function randomFillSync(buffer: Buffer | Uint8Array, offset?: number, size?: number): Buffer; + export function randomFill(buffer: Buffer, callback: (err: Error, buf: Buffer) => void): void; + export function randomFill(buffer: Uint8Array, callback: (err: Error, buf: Uint8Array) => void): void; + export function randomFill(buffer: Buffer, offset: number, callback: (err: Error, buf: Buffer) => void): void; + export function randomFill(buffer: Uint8Array, offset: number, callback: (err: Error, buf: Uint8Array) => void): void; + export function randomFill(buffer: Buffer, offset: number, size: number, callback: (err: Error, buf: Buffer) => void): void; + export function randomFill(buffer: Uint8Array, offset: number, size: number, callback: (err: Error, buf: Uint8Array) => void): void; + export interface RsaPublicKey { + key: string; + padding?: number; + } + export interface RsaPrivateKey { + key: string; + passphrase?: string; + padding?: number; + } + export function publicEncrypt(public_key: string | RsaPublicKey, buffer: Buffer): Buffer; + export function privateDecrypt(private_key: string | RsaPrivateKey, buffer: Buffer): Buffer; + export function privateEncrypt(private_key: string | RsaPrivateKey, buffer: Buffer): Buffer; + export function publicDecrypt(public_key: string | RsaPublicKey, buffer: Buffer): Buffer; + export function getCiphers(): string[]; + export function getCurves(): string[]; + export function getHashes(): string[]; + export interface ECDH { + generateKeys(): Buffer; + generateKeys(encoding: HexBase64Latin1Encoding): string; + generateKeys(encoding: HexBase64Latin1Encoding, format: ECDHKeyFormat): string; + computeSecret(other_public_key: Buffer): Buffer; + computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding): Buffer; + computeSecret(other_public_key: string, input_encoding: HexBase64Latin1Encoding, output_encoding: HexBase64Latin1Encoding): string; + getPrivateKey(): Buffer; + getPrivateKey(encoding: HexBase64Latin1Encoding): string; + getPublicKey(): Buffer; + getPublicKey(encoding: HexBase64Latin1Encoding): string; + getPublicKey(encoding: HexBase64Latin1Encoding, format: ECDHKeyFormat): string; + setPrivateKey(private_key: Buffer): void; + setPrivateKey(private_key: string, encoding: HexBase64Latin1Encoding): void; + } + export function createECDH(curve_name: string): ECDH; + export function timingSafeEqual(a: Buffer, b: Buffer): boolean; + export var DEFAULT_ENCODING: string; } declare module "stream" { - import * as events from "events"; + import * as events from "events"; - class internal extends events.EventEmitter { - pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T; - } + class internal extends events.EventEmitter { + pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T; + } - namespace internal { + namespace internal { + export class Stream extends internal { } - export class Stream extends internal { } + export interface ReadableOptions { + highWaterMark?: number; + encoding?: string; + objectMode?: boolean; + read?: (this: Readable, size?: number) => any; + destroy?: (error?: Error) => any; + } - export interface ReadableOptions { - highWaterMark?: number; - encoding?: string; - objectMode?: boolean; - read?: (this: Readable, size?: number) => any; - } - - export class Readable extends Stream implements NodeJS.ReadableStream { - readable: boolean; - constructor(opts?: ReadableOptions); - _read(size: number): void; - read(size?: number): any; - setEncoding(encoding: string): this; - pause(): this; - resume(): this; - isPaused(): boolean; - pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T; - unpipe<T extends NodeJS.WritableStream>(destination?: T): this; - unshift(chunk: any): void; - wrap(oldStream: NodeJS.ReadableStream): Readable; - push(chunk: any, encoding?: string): boolean; + export class Readable extends Stream implements NodeJS.ReadableStream { + readable: boolean; + readonly readableHighWaterMark: number; + constructor(opts?: ReadableOptions); + _read(size: number): void; + read(size?: number): any; + setEncoding(encoding: string): this; + pause(): this; + resume(): this; + isPaused(): boolean; + unpipe<T extends NodeJS.WritableStream>(destination?: T): this; + unshift(chunk: any): void; + wrap(oldStream: NodeJS.ReadableStream): this; + push(chunk: any, encoding?: string): boolean; + _destroy(err: Error, callback: Function): void; + destroy(error?: Error): void; /** * Event emitter * The defined events on documents including: - * 1. close - * 2. data - * 3. end - * 4. readable - * 5. error - **/ - addListener(event: string, listener: Function): this; - addListener(event: string, listener: Function): this; - addListener(event: "close", listener: () => void): this; - addListener(event: "data", listener: (chunk: Buffer | string) => void): this; - addListener(event: "end", listener: () => void): this; - addListener(event: "readable", listener: () => void): this; - addListener(event: "error", listener: (err: Error) => void): this; + * 1. close + * 2. data + * 3. end + * 4. readable + * 5. error + */ + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "data", listener: (chunk: Buffer | string) => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "readable", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "close"): boolean; - emit(event: "data", chunk: Buffer | string): boolean; - emit(event: "end"): boolean; - emit(event: "readable"): boolean; - emit(event: "error", err: Error): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close"): boolean; + emit(event: "data", chunk: Buffer | string): boolean; + emit(event: "end"): boolean; + emit(event: "readable"): boolean; + emit(event: "error", err: Error): boolean; - on(event: string, listener: Function): this; - on(event: "close", listener: () => void): this; - on(event: "data", listener: (chunk: Buffer | string) => void): this; - on(event: "end", listener: () => void): this; - on(event: "readable", listener: () => void): this; - on(event: "error", listener: (err: Error) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "data", listener: (chunk: Buffer | string) => void): this; + on(event: "end", listener: () => void): this; + on(event: "readable", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; - once(event: string, listener: Function): this; - once(event: "close", listener: () => void): this; - once(event: "data", listener: (chunk: Buffer | string) => void): this; - once(event: "end", listener: () => void): this; - once(event: "readable", listener: () => void): this; - once(event: "error", listener: (err: Error) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "data", listener: (chunk: Buffer | string) => void): this; + once(event: "end", listener: () => void): this; + once(event: "readable", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "close", listener: () => void): this; - prependListener(event: "data", listener: (chunk: Buffer | string) => void): this; - prependListener(event: "end", listener: () => void): this; - prependListener(event: "readable", listener: () => void): this; - prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "data", listener: (chunk: Buffer | string) => void): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "readable", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "close", listener: () => void): this; - prependOnceListener(event: "data", listener: (chunk: Buffer | string) => void): this; - prependOnceListener(event: "end", listener: () => void): this; - prependOnceListener(event: "readable", listener: () => void): this; - prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "data", listener: (chunk: Buffer | string) => void): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "readable", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; - removeListener(event: string, listener: Function): this; - removeListener(event: "close", listener: () => void): this; - removeListener(event: "data", listener: (chunk: Buffer | string) => void): this; - removeListener(event: "end", listener: () => void): this; - removeListener(event: "readable", listener: () => void): this; - removeListener(event: "error", listener: (err: Error) => void): this; - } + removeListener(event: string, listener: (...args: any[]) => void): this; + removeListener(event: "close", listener: () => void): this; + removeListener(event: "data", listener: (chunk: Buffer | string) => void): this; + removeListener(event: "end", listener: () => void): this; + removeListener(event: "readable", listener: () => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + } - export interface WritableOptions { - highWaterMark?: number; - decodeStrings?: boolean; - objectMode?: boolean; - write?: (chunk: string | Buffer, encoding: string, callback: Function) => any; - writev?: (chunks: { chunk: string | Buffer, encoding: string }[], callback: Function) => any; - } + export interface WritableOptions { + highWaterMark?: number; + decodeStrings?: boolean; + objectMode?: boolean; + write?: (chunk: any, encoding: string, callback: Function) => any; + writev?: (chunks: Array<{ chunk: any, encoding: string }>, callback: Function) => any; + destroy?: (error?: Error) => any; + final?: (callback: (error?: Error) => void) => void; + } - export class Writable extends Stream implements NodeJS.WritableStream { - writable: boolean; - constructor(opts?: WritableOptions); - _write(chunk: any, encoding: string, callback: Function): void; - write(chunk: any, cb?: Function): boolean; - write(chunk: any, encoding?: string, cb?: Function): boolean; - setDefaultEncoding(encoding: string): this; - end(): void; - end(chunk: any, cb?: Function): void; - end(chunk: any, encoding?: string, cb?: Function): void; + export class Writable extends Stream implements NodeJS.WritableStream { + writable: boolean; + readonly writableHighWaterMark: number; + constructor(opts?: WritableOptions); + _write(chunk: any, encoding: string, callback: (err?: Error) => void): void; + _writev?(chunks: Array<{ chunk: any, encoding: string }>, callback: (err?: Error) => void): void; + _destroy(err: Error, callback: Function): void; + _final(callback: Function): void; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + setDefaultEncoding(encoding: string): this; + end(cb?: Function): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + cork(): void; + uncork(): void; + destroy(error?: Error): void; /** * Event emitter * The defined events on documents including: - * 1. close - * 2. drain - * 3. error - * 4. finish - * 5. pipe - * 6. unpipe - **/ - addListener(event: string, listener: Function): this; - addListener(event: "close", listener: () => void): this; - addListener(event: "drain", listener: () => void): this; - addListener(event: "error", listener: (err: Error) => void): this; - addListener(event: "finish", listener: () => void): this; - addListener(event: "pipe", listener: (src: Readable) => void): this; - addListener(event: "unpipe", listener: (src: Readable) => void): this; + * 1. close + * 2. drain + * 3. error + * 4. finish + * 5. pipe + * 6. unpipe + */ + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "finish", listener: () => void): this; + addListener(event: "pipe", listener: (src: Readable) => void): this; + addListener(event: "unpipe", listener: (src: Readable) => void): this; - emit(event: string | symbol, ...args: any[]): boolean; - emit(event: "close"): boolean; - emit(event: "drain", chunk: Buffer | string): boolean; - emit(event: "error", err: Error): boolean; - emit(event: "finish"): boolean; - emit(event: "pipe", src: Readable): boolean; - emit(event: "unpipe", src: Readable): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close"): boolean; + emit(event: "drain", chunk: Buffer | string): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "finish"): boolean; + emit(event: "pipe", src: Readable): boolean; + emit(event: "unpipe", src: Readable): boolean; - on(event: string, listener: Function): this; - on(event: "close", listener: () => void): this; - on(event: "drain", listener: () => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "finish", listener: () => void): this; - on(event: "pipe", listener: (src: Readable) => void): this; - on(event: "unpipe", listener: (src: Readable) => void): this; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "drain", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "finish", listener: () => void): this; + on(event: "pipe", listener: (src: Readable) => void): this; + on(event: "unpipe", listener: (src: Readable) => void): this; - once(event: string, listener: Function): this; - once(event: "close", listener: () => void): this; - once(event: "drain", listener: () => void): this; - once(event: "error", listener: (err: Error) => void): this; - once(event: "finish", listener: () => void): this; - once(event: "pipe", listener: (src: Readable) => void): this; - once(event: "unpipe", listener: (src: Readable) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "drain", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "finish", listener: () => void): this; + once(event: "pipe", listener: (src: Readable) => void): this; + once(event: "unpipe", listener: (src: Readable) => void): this; - prependListener(event: string, listener: Function): this; - prependListener(event: "close", listener: () => void): this; - prependListener(event: "drain", listener: () => void): this; - prependListener(event: "error", listener: (err: Error) => void): this; - prependListener(event: "finish", listener: () => void): this; - prependListener(event: "pipe", listener: (src: Readable) => void): this; - prependListener(event: "unpipe", listener: (src: Readable) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "finish", listener: () => void): this; + prependListener(event: "pipe", listener: (src: Readable) => void): this; + prependListener(event: "unpipe", listener: (src: Readable) => void): this; - prependOnceListener(event: string, listener: Function): this; - prependOnceListener(event: "close", listener: () => void): this; - prependOnceListener(event: "drain", listener: () => void): this; - prependOnceListener(event: "error", listener: (err: Error) => void): this; - prependOnceListener(event: "finish", listener: () => void): this; - prependOnceListener(event: "pipe", listener: (src: Readable) => void): this; - prependOnceListener(event: "unpipe", listener: (src: Readable) => void): this; + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "finish", listener: () => void): this; + prependOnceListener(event: "pipe", listener: (src: Readable) => void): this; + prependOnceListener(event: "unpipe", listener: (src: Readable) => void): this; - removeListener(event: string, listener: Function): this; - removeListener(event: "close", listener: () => void): this; - removeListener(event: "drain", listener: () => void): this; - removeListener(event: "error", listener: (err: Error) => void): this; - removeListener(event: "finish", listener: () => void): this; - removeListener(event: "pipe", listener: (src: Readable) => void): this; - removeListener(event: "unpipe", listener: (src: Readable) => void): this; - } + removeListener(event: string, listener: (...args: any[]) => void): this; + removeListener(event: "close", listener: () => void): this; + removeListener(event: "drain", listener: () => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + removeListener(event: "finish", listener: () => void): this; + removeListener(event: "pipe", listener: (src: Readable) => void): this; + removeListener(event: "unpipe", listener: (src: Readable) => void): this; + } - export interface DuplexOptions extends ReadableOptions, WritableOptions { - allowHalfOpen?: boolean; - readableObjectMode?: boolean; - writableObjectMode?: boolean; - } + export interface DuplexOptions extends ReadableOptions, WritableOptions { + allowHalfOpen?: boolean; + readableObjectMode?: boolean; + writableObjectMode?: boolean; + } - // Note: Duplex extends both Readable and Writable. - export class Duplex extends Readable implements Writable { - writable: boolean; - constructor(opts?: DuplexOptions); - _write(chunk: any, encoding: string, callback: Function): void; - write(chunk: any, cb?: Function): boolean; - write(chunk: any, encoding?: string, cb?: Function): boolean; - setDefaultEncoding(encoding: string): this; - end(): void; - end(chunk: any, cb?: Function): void; - end(chunk: any, encoding?: string, cb?: Function): void; - } + // Note: Duplex extends both Readable and Writable. + export class Duplex extends Readable implements Writable { + writable: boolean; + readonly writableHighWaterMark: number; + constructor(opts?: DuplexOptions); + _write(chunk: any, encoding: string, callback: (err?: Error) => void): void; + _writev?(chunks: Array<{ chunk: any, encoding: string }>, callback: (err?: Error) => void): void; + _destroy(err: Error, callback: Function): void; + _final(callback: Function): void; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + setDefaultEncoding(encoding: string): this; + end(cb?: Function): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + cork(): void; + uncork(): void; + } - export interface TransformOptions extends DuplexOptions { - transform?: (chunk: string | Buffer, encoding: string, callback: Function) => any; - flush?: (callback: Function) => any; - } + export interface TransformOptions extends DuplexOptions { + transform?: (chunk: string | Buffer, encoding: string, callback: Function) => any; + flush?: (callback: Function) => any; + } - export class Transform extends Duplex { - constructor(opts?: TransformOptions); - _transform(chunk: any, encoding: string, callback: Function): void; - } + export class Transform extends Duplex { + constructor(opts?: TransformOptions); + _transform(chunk: any, encoding: string, callback: Function): void; + destroy(error?: Error): void; + } - export class PassThrough extends Transform { } - } + export class PassThrough extends Transform { } + } - export = internal; + export = internal; } declare module "util" { - export interface InspectOptions extends NodeJS.InspectOptions { } - export function format(format: any, ...param: any[]): string; - export function debug(string: string): void; - export function error(...param: any[]): void; - export function puts(...param: any[]): void; - export function print(...param: any[]): void; - export function log(string: string): void; - export function inspect(object: any, showHidden?: boolean, depth?: number | null, color?: boolean): string; - export function inspect(object: any, options: InspectOptions): string; - export function isArray(object: any): object is any[]; - export function isRegExp(object: any): object is RegExp; - export function isDate(object: any): object is Date; - export function isError(object: any): object is Error; - export function inherits(constructor: any, superConstructor: any): void; - export function debuglog(key: string): (msg: string, ...param: any[]) => void; - export function isBoolean(object: any): object is boolean; - export function isBuffer(object: any): object is Buffer; - export function isFunction(object: any): boolean; - export function isNull(object: any): object is null; - export function isNullOrUndefined(object: any): object is null | undefined; - export function isNumber(object: any): object is number; - export function isObject(object: any): boolean; - export function isPrimitive(object: any): boolean; - export function isString(object: any): object is string; - export function isSymbol(object: any): object is symbol; - export function isUndefined(object: any): object is undefined; - export function deprecate<T extends Function>(fn: T, message: string): T; + export interface InspectOptions extends NodeJS.InspectOptions { } + export function format(format: any, ...param: any[]): string; + export function debug(string: string): void; + export function error(...param: any[]): void; + export function puts(...param: any[]): void; + export function print(...param: any[]): void; + export function log(string: string): void; + export var inspect: { + (object: any, showHidden?: boolean, depth?: number | null, color?: boolean): string; + (object: any, options: InspectOptions): string; + colors: { + [color: string]: [number, number] | undefined + } + styles: { + [style: string]: string | undefined + } + defaultOptions: InspectOptions; + custom: symbol; + }; + export function isArray(object: any): object is any[]; + export function isRegExp(object: any): object is RegExp; + export function isDate(object: any): object is Date; + export function isError(object: any): object is Error; + export function inherits(constructor: any, superConstructor: any): void; + export function debuglog(key: string): (msg: string, ...param: any[]) => void; + export function isBoolean(object: any): object is boolean; + export function isBuffer(object: any): object is Buffer; + export function isFunction(object: any): boolean; + export function isNull(object: any): object is null; + export function isNullOrUndefined(object: any): object is null | undefined; + export function isNumber(object: any): object is number; + export function isObject(object: any): boolean; + export function isPrimitive(object: any): boolean; + export function isString(object: any): object is string; + export function isSymbol(object: any): object is symbol; + export function isUndefined(object: any): object is undefined; + export function deprecate<T extends Function>(fn: T, message: string): T; + + export interface CustomPromisify<TCustom extends Function> extends Function { + __promisify__: TCustom; + } + + export function callbackify(fn: () => Promise<void>): (callback: (err: NodeJS.ErrnoException) => void) => void; + export function callbackify<TResult>(fn: () => Promise<TResult>): (callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void; + export function callbackify<T1>(fn: (arg1: T1) => Promise<void>): (arg1: T1, callback: (err: NodeJS.ErrnoException) => void) => void; + export function callbackify<T1, TResult>(fn: (arg1: T1) => Promise<TResult>): (arg1: T1, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void; + export function callbackify<T1, T2>(fn: (arg1: T1, arg2: T2) => Promise<void>): (arg1: T1, arg2: T2, callback: (err: NodeJS.ErrnoException) => void) => void; + export function callbackify<T1, T2, TResult>(fn: (arg1: T1, arg2: T2) => Promise<TResult>): (arg1: T1, arg2: T2, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void; + export function callbackify<T1, T2, T3>(fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, callback: (err: NodeJS.ErrnoException) => void) => void; + export function callbackify<T1, T2, T3, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void; + export function callbackify<T1, T2, T3, T4>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: NodeJS.ErrnoException) => void) => void; + export function callbackify<T1, T2, T3, T4, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void; + export function callbackify<T1, T2, T3, T4, T5>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: NodeJS.ErrnoException) => void) => void; + export function callbackify<T1, T2, T3, T4, T5, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void; + export function callbackify<T1, T2, T3, T4, T5, T6>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, callback: (err: NodeJS.ErrnoException) => void) => void; + export function callbackify<T1, T2, T3, T4, T5, T6, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void; + + export function promisify<TCustom extends Function>(fn: CustomPromisify<TCustom>): TCustom; + export function promisify<TResult>(fn: (callback: (err: Error | null, result: TResult) => void) => void): () => Promise<TResult>; + export function promisify(fn: (callback: (err: Error | null) => void) => void): () => Promise<void>; + export function promisify<T1, TResult>(fn: (arg1: T1, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1) => Promise<TResult>; + export function promisify<T1>(fn: (arg1: T1, callback: (err: Error | null) => void) => void): (arg1: T1) => Promise<void>; + export function promisify<T1, T2, TResult>(fn: (arg1: T1, arg2: T2, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2) => Promise<TResult>; + export function promisify<T1, T2>(fn: (arg1: T1, arg2: T2, callback: (err: Error | null) => void) => void): (arg1: T1, arg2: T2) => Promise<void>; + export function promisify<T1, T2, T3, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2, arg3: T3) => Promise<TResult>; + export function promisify<T1, T2, T3>(fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err: Error | null) => void) => void): (arg1: T1, arg2: T2, arg3: T3) => Promise<void>; + export function promisify<T1, T2, T3, T4, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<TResult>; + export function promisify<T1, T2, T3, T4>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: Error | null) => void) => void): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<void>; + export function promisify<T1, T2, T3, T4, T5, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<TResult>; + export function promisify<T1, T2, T3, T4, T5>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: Error | null) => void) => void): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<void>; + export function promisify(fn: Function): Function; + export namespace promisify { + const custom: symbol; + } + + export class TextDecoder { + readonly encoding: string; + readonly fatal: boolean; + readonly ignoreBOM: boolean; + constructor( + encoding?: string, + options?: { fatal?: boolean; ignoreBOM?: boolean } + ); + decode( + input?: + Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint16Array + | Uint32Array + | Uint8ClampedArray + | Float32Array + | Float64Array + | DataView + | ArrayBuffer + | null, + options?: { stream?: boolean } + ): string; + } + + export class TextEncoder { + readonly encoding: string; + constructor(); + encode(input?: string): Uint8Array; + } } declare module "assert" { - function internal(value: any, message?: string): void; - namespace internal { - export class AssertionError implements Error { - name: string; - message: string; - actual: any; - expected: any; - operator: string; - generatedMessage: boolean; + function internal(value: any, message?: string): void; + namespace internal { + export class AssertionError implements Error { + name: string; + message: string; + actual: any; + expected: any; + operator: string; + generatedMessage: boolean; - constructor(options?: { - message?: string; actual?: any; expected?: any; - operator?: string; stackStartFunction?: Function - }); - } + constructor(options?: { + message?: string; actual?: any; expected?: any; + operator?: string; stackStartFunction?: Function + }); + } - export function fail(actual?: any, expected?: any, message?: string, operator?: string): void; - export function ok(value: any, message?: string): void; - export function equal(actual: any, expected: any, message?: string): void; - export function notEqual(actual: any, expected: any, message?: string): void; - export function deepEqual(actual: any, expected: any, message?: string): void; - export function notDeepEqual(acutal: any, expected: any, message?: string): void; - export function strictEqual(actual: any, expected: any, message?: string): void; - export function notStrictEqual(actual: any, expected: any, message?: string): void; - export function deepStrictEqual(actual: any, expected: any, message?: string): void; - export function notDeepStrictEqual(actual: any, expected: any, message?: string): void; + export function fail(message: string): never; + export function fail(actual: any, expected: any, message?: string, operator?: string): never; + export function ok(value: any, message?: string): void; + export function equal(actual: any, expected: any, message?: string): void; + export function notEqual(actual: any, expected: any, message?: string): void; + export function deepEqual(actual: any, expected: any, message?: string): void; + export function notDeepEqual(acutal: any, expected: any, message?: string): void; + export function strictEqual(actual: any, expected: any, message?: string): void; + export function notStrictEqual(actual: any, expected: any, message?: string): void; + export function deepStrictEqual(actual: any, expected: any, message?: string): void; + export function notDeepStrictEqual(actual: any, expected: any, message?: string): void; - export function throws(block: Function, message?: string): void; - export function throws(block: Function, error: Function, message?: string): void; - export function throws(block: Function, error: RegExp, message?: string): void; - export function throws(block: Function, error: (err: any) => boolean, message?: string): void; + export function throws(block: Function, message?: string): void; + export function throws(block: Function, error: Function, message?: string): void; + export function throws(block: Function, error: RegExp, message?: string): void; + export function throws(block: Function, error: (err: any) => boolean, message?: string): void; - export function doesNotThrow(block: Function, message?: string): void; - export function doesNotThrow(block: Function, error: Function, message?: string): void; - export function doesNotThrow(block: Function, error: RegExp, message?: string): void; - export function doesNotThrow(block: Function, error: (err: any) => boolean, message?: string): void; + export function doesNotThrow(block: Function, message?: string): void; + export function doesNotThrow(block: Function, error: Function, message?: string): void; + export function doesNotThrow(block: Function, error: RegExp, message?: string): void; + export function doesNotThrow(block: Function, error: (err: any) => boolean, message?: string): void; - export function ifError(value: any): void; - } + export function ifError(value: any): void; + } - export = internal; + export = internal; } declare module "tty" { - import * as net from "net"; + import * as net from "net"; - export function isatty(fd: number): boolean; - export interface ReadStream extends net.Socket { - isRaw: boolean; - setRawMode(mode: boolean): void; - isTTY: boolean; - } - export interface WriteStream extends net.Socket { - columns: number; - rows: number; - isTTY: boolean; - } + export function isatty(fd: number): boolean; + export class ReadStream extends net.Socket { + isRaw: boolean; + setRawMode(mode: boolean): void; + isTTY: boolean; + } + export class WriteStream extends net.Socket { + columns: number; + rows: number; + isTTY: boolean; + } } declare module "domain" { - import * as events from "events"; + import * as events from "events"; - export class Domain extends events.EventEmitter implements NodeJS.Domain { - run(fn: Function): void; - add(emitter: events.EventEmitter): void; - remove(emitter: events.EventEmitter): void; - bind(cb: (err: Error, data: any) => any): any; - intercept(cb: (data: any) => any): any; - dispose(): void; - members: any[]; - enter(): void; - exit(): void; - } + export class Domain extends events.EventEmitter implements NodeJS.Domain { + run(fn: Function): void; + add(emitter: events.EventEmitter): void; + remove(emitter: events.EventEmitter): void; + bind(cb: (err: Error, data: any) => any): any; + intercept(cb: (data: any) => any): any; + dispose(): void; + members: any[]; + enter(): void; + exit(): void; + } - export function create(): Domain; + export function create(): Domain; } declare module "constants" { - export var E2BIG: number; - export var EACCES: number; - export var EADDRINUSE: number; - export var EADDRNOTAVAIL: number; - export var EAFNOSUPPORT: number; - export var EAGAIN: number; - export var EALREADY: number; - export var EBADF: number; - export var EBADMSG: number; - export var EBUSY: number; - export var ECANCELED: number; - export var ECHILD: number; - export var ECONNABORTED: number; - export var ECONNREFUSED: number; - export var ECONNRESET: number; - export var EDEADLK: number; - export var EDESTADDRREQ: number; - export var EDOM: number; - export var EEXIST: number; - export var EFAULT: number; - export var EFBIG: number; - export var EHOSTUNREACH: number; - export var EIDRM: number; - export var EILSEQ: number; - export var EINPROGRESS: number; - export var EINTR: number; - export var EINVAL: number; - export var EIO: number; - export var EISCONN: number; - export var EISDIR: number; - export var ELOOP: number; - export var EMFILE: number; - export var EMLINK: number; - export var EMSGSIZE: number; - export var ENAMETOOLONG: number; - export var ENETDOWN: number; - export var ENETRESET: number; - export var ENETUNREACH: number; - export var ENFILE: number; - export var ENOBUFS: number; - export var ENODATA: number; - export var ENODEV: number; - export var ENOENT: number; - export var ENOEXEC: number; - export var ENOLCK: number; - export var ENOLINK: number; - export var ENOMEM: number; - export var ENOMSG: number; - export var ENOPROTOOPT: number; - export var ENOSPC: number; - export var ENOSR: number; - export var ENOSTR: number; - export var ENOSYS: number; - export var ENOTCONN: number; - export var ENOTDIR: number; - export var ENOTEMPTY: number; - export var ENOTSOCK: number; - export var ENOTSUP: number; - export var ENOTTY: number; - export var ENXIO: number; - export var EOPNOTSUPP: number; - export var EOVERFLOW: number; - export var EPERM: number; - export var EPIPE: number; - export var EPROTO: number; - export var EPROTONOSUPPORT: number; - export var EPROTOTYPE: number; - export var ERANGE: number; - export var EROFS: number; - export var ESPIPE: number; - export var ESRCH: number; - export var ETIME: number; - export var ETIMEDOUT: number; - export var ETXTBSY: number; - export var EWOULDBLOCK: number; - export var EXDEV: number; - export var WSAEINTR: number; - export var WSAEBADF: number; - export var WSAEACCES: number; - export var WSAEFAULT: number; - export var WSAEINVAL: number; - export var WSAEMFILE: number; - export var WSAEWOULDBLOCK: number; - export var WSAEINPROGRESS: number; - export var WSAEALREADY: number; - export var WSAENOTSOCK: number; - export var WSAEDESTADDRREQ: number; - export var WSAEMSGSIZE: number; - export var WSAEPROTOTYPE: number; - export var WSAENOPROTOOPT: number; - export var WSAEPROTONOSUPPORT: number; - export var WSAESOCKTNOSUPPORT: number; - export var WSAEOPNOTSUPP: number; - export var WSAEPFNOSUPPORT: number; - export var WSAEAFNOSUPPORT: number; - export var WSAEADDRINUSE: number; - export var WSAEADDRNOTAVAIL: number; - export var WSAENETDOWN: number; - export var WSAENETUNREACH: number; - export var WSAENETRESET: number; - export var WSAECONNABORTED: number; - export var WSAECONNRESET: number; - export var WSAENOBUFS: number; - export var WSAEISCONN: number; - export var WSAENOTCONN: number; - export var WSAESHUTDOWN: number; - export var WSAETOOMANYREFS: number; - export var WSAETIMEDOUT: number; - export var WSAECONNREFUSED: number; - export var WSAELOOP: number; - export var WSAENAMETOOLONG: number; - export var WSAEHOSTDOWN: number; - export var WSAEHOSTUNREACH: number; - export var WSAENOTEMPTY: number; - export var WSAEPROCLIM: number; - export var WSAEUSERS: number; - export var WSAEDQUOT: number; - export var WSAESTALE: number; - export var WSAEREMOTE: number; - export var WSASYSNOTREADY: number; - export var WSAVERNOTSUPPORTED: number; - export var WSANOTINITIALISED: number; - export var WSAEDISCON: number; - export var WSAENOMORE: number; - export var WSAECANCELLED: number; - export var WSAEINVALIDPROCTABLE: number; - export var WSAEINVALIDPROVIDER: number; - export var WSAEPROVIDERFAILEDINIT: number; - export var WSASYSCALLFAILURE: number; - export var WSASERVICE_NOT_FOUND: number; - export var WSATYPE_NOT_FOUND: number; - export var WSA_E_NO_MORE: number; - export var WSA_E_CANCELLED: number; - export var WSAEREFUSED: number; - export var SIGHUP: number; - export var SIGINT: number; - export var SIGILL: number; - export var SIGABRT: number; - export var SIGFPE: number; - export var SIGKILL: number; - export var SIGSEGV: number; - export var SIGTERM: number; - export var SIGBREAK: number; - export var SIGWINCH: number; - export var SSL_OP_ALL: number; - export var SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: number; - export var SSL_OP_CIPHER_SERVER_PREFERENCE: number; - export var SSL_OP_CISCO_ANYCONNECT: number; - export var SSL_OP_COOKIE_EXCHANGE: number; - export var SSL_OP_CRYPTOPRO_TLSEXT_BUG: number; - export var SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: number; - export var SSL_OP_EPHEMERAL_RSA: number; - export var SSL_OP_LEGACY_SERVER_CONNECT: number; - export var SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: number; - export var SSL_OP_MICROSOFT_SESS_ID_BUG: number; - export var SSL_OP_MSIE_SSLV2_RSA_PADDING: number; - export var SSL_OP_NETSCAPE_CA_DN_BUG: number; - export var SSL_OP_NETSCAPE_CHALLENGE_BUG: number; - export var SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: number; - export var SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: number; - export var SSL_OP_NO_COMPRESSION: number; - export var SSL_OP_NO_QUERY_MTU: number; - export var SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: number; - export var SSL_OP_NO_SSLv2: number; - export var SSL_OP_NO_SSLv3: number; - export var SSL_OP_NO_TICKET: number; - export var SSL_OP_NO_TLSv1: number; - export var SSL_OP_NO_TLSv1_1: number; - export var SSL_OP_NO_TLSv1_2: number; - export var SSL_OP_PKCS1_CHECK_1: number; - export var SSL_OP_PKCS1_CHECK_2: number; - export var SSL_OP_SINGLE_DH_USE: number; - export var SSL_OP_SINGLE_ECDH_USE: number; - export var SSL_OP_SSLEAY_080_CLIENT_DH_BUG: number; - export var SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: number; - export var SSL_OP_TLS_BLOCK_PADDING_BUG: number; - export var SSL_OP_TLS_D5_BUG: number; - export var SSL_OP_TLS_ROLLBACK_BUG: number; - export var ENGINE_METHOD_DSA: number; - export var ENGINE_METHOD_DH: number; - export var ENGINE_METHOD_RAND: number; - export var ENGINE_METHOD_ECDH: number; - export var ENGINE_METHOD_ECDSA: number; - export var ENGINE_METHOD_CIPHERS: number; - export var ENGINE_METHOD_DIGESTS: number; - export var ENGINE_METHOD_STORE: number; - export var ENGINE_METHOD_PKEY_METHS: number; - export var ENGINE_METHOD_PKEY_ASN1_METHS: number; - export var ENGINE_METHOD_ALL: number; - export var ENGINE_METHOD_NONE: number; - export var DH_CHECK_P_NOT_SAFE_PRIME: number; - export var DH_CHECK_P_NOT_PRIME: number; - export var DH_UNABLE_TO_CHECK_GENERATOR: number; - export var DH_NOT_SUITABLE_GENERATOR: number; - export var NPN_ENABLED: number; - export var RSA_PKCS1_PADDING: number; - export var RSA_SSLV23_PADDING: number; - export var RSA_NO_PADDING: number; - export var RSA_PKCS1_OAEP_PADDING: number; - export var RSA_X931_PADDING: number; - export var RSA_PKCS1_PSS_PADDING: number; - export var POINT_CONVERSION_COMPRESSED: number; - export var POINT_CONVERSION_UNCOMPRESSED: number; - export var POINT_CONVERSION_HYBRID: number; - export var O_RDONLY: number; - export var O_WRONLY: number; - export var O_RDWR: number; - export var S_IFMT: number; - export var S_IFREG: number; - export var S_IFDIR: number; - export var S_IFCHR: number; - export var S_IFBLK: number; - export var S_IFIFO: number; - export var S_IFSOCK: number; - export var S_IRWXU: number; - export var S_IRUSR: number; - export var S_IWUSR: number; - export var S_IXUSR: number; - export var S_IRWXG: number; - export var S_IRGRP: number; - export var S_IWGRP: number; - export var S_IXGRP: number; - export var S_IRWXO: number; - export var S_IROTH: number; - export var S_IWOTH: number; - export var S_IXOTH: number; - export var S_IFLNK: number; - export var O_CREAT: number; - export var O_EXCL: number; - export var O_NOCTTY: number; - export var O_DIRECTORY: number; - export var O_NOATIME: number; - export var O_NOFOLLOW: number; - export var O_SYNC: number; - export var O_SYMLINK: number; - export var O_DIRECT: number; - export var O_NONBLOCK: number; - export var O_TRUNC: number; - export var O_APPEND: number; - export var F_OK: number; - export var R_OK: number; - export var W_OK: number; - export var X_OK: number; - export var UV_UDP_REUSEADDR: number; - export var SIGQUIT: number; - export var SIGTRAP: number; - export var SIGIOT: number; - export var SIGBUS: number; - export var SIGUSR1: number; - export var SIGUSR2: number; - export var SIGPIPE: number; - export var SIGALRM: number; - export var SIGCHLD: number; - export var SIGSTKFLT: number; - export var SIGCONT: number; - export var SIGSTOP: number; - export var SIGTSTP: number; - export var SIGTTIN: number; - export var SIGTTOU: number; - export var SIGURG: number; - export var SIGXCPU: number; - export var SIGXFSZ: number; - export var SIGVTALRM: number; - export var SIGPROF: number; - export var SIGIO: number; - export var SIGPOLL: number; - export var SIGPWR: number; - export var SIGSYS: number; - export var SIGUNUSED: number; - export var defaultCoreCipherList: string; - export var defaultCipherList: string; - export var ENGINE_METHOD_RSA: number; - export var ALPN_ENABLED: number; + export var E2BIG: number; + export var EACCES: number; + export var EADDRINUSE: number; + export var EADDRNOTAVAIL: number; + export var EAFNOSUPPORT: number; + export var EAGAIN: number; + export var EALREADY: number; + export var EBADF: number; + export var EBADMSG: number; + export var EBUSY: number; + export var ECANCELED: number; + export var ECHILD: number; + export var ECONNABORTED: number; + export var ECONNREFUSED: number; + export var ECONNRESET: number; + export var EDEADLK: number; + export var EDESTADDRREQ: number; + export var EDOM: number; + export var EEXIST: number; + export var EFAULT: number; + export var EFBIG: number; + export var EHOSTUNREACH: number; + export var EIDRM: number; + export var EILSEQ: number; + export var EINPROGRESS: number; + export var EINTR: number; + export var EINVAL: number; + export var EIO: number; + export var EISCONN: number; + export var EISDIR: number; + export var ELOOP: number; + export var EMFILE: number; + export var EMLINK: number; + export var EMSGSIZE: number; + export var ENAMETOOLONG: number; + export var ENETDOWN: number; + export var ENETRESET: number; + export var ENETUNREACH: number; + export var ENFILE: number; + export var ENOBUFS: number; + export var ENODATA: number; + export var ENODEV: number; + export var ENOENT: number; + export var ENOEXEC: number; + export var ENOLCK: number; + export var ENOLINK: number; + export var ENOMEM: number; + export var ENOMSG: number; + export var ENOPROTOOPT: number; + export var ENOSPC: number; + export var ENOSR: number; + export var ENOSTR: number; + export var ENOSYS: number; + export var ENOTCONN: number; + export var ENOTDIR: number; + export var ENOTEMPTY: number; + export var ENOTSOCK: number; + export var ENOTSUP: number; + export var ENOTTY: number; + export var ENXIO: number; + export var EOPNOTSUPP: number; + export var EOVERFLOW: number; + export var EPERM: number; + export var EPIPE: number; + export var EPROTO: number; + export var EPROTONOSUPPORT: number; + export var EPROTOTYPE: number; + export var ERANGE: number; + export var EROFS: number; + export var ESPIPE: number; + export var ESRCH: number; + export var ETIME: number; + export var ETIMEDOUT: number; + export var ETXTBSY: number; + export var EWOULDBLOCK: number; + export var EXDEV: number; + export var WSAEINTR: number; + export var WSAEBADF: number; + export var WSAEACCES: number; + export var WSAEFAULT: number; + export var WSAEINVAL: number; + export var WSAEMFILE: number; + export var WSAEWOULDBLOCK: number; + export var WSAEINPROGRESS: number; + export var WSAEALREADY: number; + export var WSAENOTSOCK: number; + export var WSAEDESTADDRREQ: number; + export var WSAEMSGSIZE: number; + export var WSAEPROTOTYPE: number; + export var WSAENOPROTOOPT: number; + export var WSAEPROTONOSUPPORT: number; + export var WSAESOCKTNOSUPPORT: number; + export var WSAEOPNOTSUPP: number; + export var WSAEPFNOSUPPORT: number; + export var WSAEAFNOSUPPORT: number; + export var WSAEADDRINUSE: number; + export var WSAEADDRNOTAVAIL: number; + export var WSAENETDOWN: number; + export var WSAENETUNREACH: number; + export var WSAENETRESET: number; + export var WSAECONNABORTED: number; + export var WSAECONNRESET: number; + export var WSAENOBUFS: number; + export var WSAEISCONN: number; + export var WSAENOTCONN: number; + export var WSAESHUTDOWN: number; + export var WSAETOOMANYREFS: number; + export var WSAETIMEDOUT: number; + export var WSAECONNREFUSED: number; + export var WSAELOOP: number; + export var WSAENAMETOOLONG: number; + export var WSAEHOSTDOWN: number; + export var WSAEHOSTUNREACH: number; + export var WSAENOTEMPTY: number; + export var WSAEPROCLIM: number; + export var WSAEUSERS: number; + export var WSAEDQUOT: number; + export var WSAESTALE: number; + export var WSAEREMOTE: number; + export var WSASYSNOTREADY: number; + export var WSAVERNOTSUPPORTED: number; + export var WSANOTINITIALISED: number; + export var WSAEDISCON: number; + export var WSAENOMORE: number; + export var WSAECANCELLED: number; + export var WSAEINVALIDPROCTABLE: number; + export var WSAEINVALIDPROVIDER: number; + export var WSAEPROVIDERFAILEDINIT: number; + export var WSASYSCALLFAILURE: number; + export var WSASERVICE_NOT_FOUND: number; + export var WSATYPE_NOT_FOUND: number; + export var WSA_E_NO_MORE: number; + export var WSA_E_CANCELLED: number; + export var WSAEREFUSED: number; + export var SIGHUP: number; + export var SIGINT: number; + export var SIGILL: number; + export var SIGABRT: number; + export var SIGFPE: number; + export var SIGKILL: number; + export var SIGSEGV: number; + export var SIGTERM: number; + export var SIGBREAK: number; + export var SIGWINCH: number; + export var SSL_OP_ALL: number; + export var SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: number; + export var SSL_OP_CIPHER_SERVER_PREFERENCE: number; + export var SSL_OP_CISCO_ANYCONNECT: number; + export var SSL_OP_COOKIE_EXCHANGE: number; + export var SSL_OP_CRYPTOPRO_TLSEXT_BUG: number; + export var SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: number; + export var SSL_OP_EPHEMERAL_RSA: number; + export var SSL_OP_LEGACY_SERVER_CONNECT: number; + export var SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: number; + export var SSL_OP_MICROSOFT_SESS_ID_BUG: number; + export var SSL_OP_MSIE_SSLV2_RSA_PADDING: number; + export var SSL_OP_NETSCAPE_CA_DN_BUG: number; + export var SSL_OP_NETSCAPE_CHALLENGE_BUG: number; + export var SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: number; + export var SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: number; + export var SSL_OP_NO_COMPRESSION: number; + export var SSL_OP_NO_QUERY_MTU: number; + export var SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: number; + export var SSL_OP_NO_SSLv2: number; + export var SSL_OP_NO_SSLv3: number; + export var SSL_OP_NO_TICKET: number; + export var SSL_OP_NO_TLSv1: number; + export var SSL_OP_NO_TLSv1_1: number; + export var SSL_OP_NO_TLSv1_2: number; + export var SSL_OP_PKCS1_CHECK_1: number; + export var SSL_OP_PKCS1_CHECK_2: number; + export var SSL_OP_SINGLE_DH_USE: number; + export var SSL_OP_SINGLE_ECDH_USE: number; + export var SSL_OP_SSLEAY_080_CLIENT_DH_BUG: number; + export var SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: number; + export var SSL_OP_TLS_BLOCK_PADDING_BUG: number; + export var SSL_OP_TLS_D5_BUG: number; + export var SSL_OP_TLS_ROLLBACK_BUG: number; + export var ENGINE_METHOD_DSA: number; + export var ENGINE_METHOD_DH: number; + export var ENGINE_METHOD_RAND: number; + export var ENGINE_METHOD_ECDH: number; + export var ENGINE_METHOD_ECDSA: number; + export var ENGINE_METHOD_CIPHERS: number; + export var ENGINE_METHOD_DIGESTS: number; + export var ENGINE_METHOD_STORE: number; + export var ENGINE_METHOD_PKEY_METHS: number; + export var ENGINE_METHOD_PKEY_ASN1_METHS: number; + export var ENGINE_METHOD_ALL: number; + export var ENGINE_METHOD_NONE: number; + export var DH_CHECK_P_NOT_SAFE_PRIME: number; + export var DH_CHECK_P_NOT_PRIME: number; + export var DH_UNABLE_TO_CHECK_GENERATOR: number; + export var DH_NOT_SUITABLE_GENERATOR: number; + export var NPN_ENABLED: number; + export var RSA_PKCS1_PADDING: number; + export var RSA_SSLV23_PADDING: number; + export var RSA_NO_PADDING: number; + export var RSA_PKCS1_OAEP_PADDING: number; + export var RSA_X931_PADDING: number; + export var RSA_PKCS1_PSS_PADDING: number; + export var POINT_CONVERSION_COMPRESSED: number; + export var POINT_CONVERSION_UNCOMPRESSED: number; + export var POINT_CONVERSION_HYBRID: number; + export var O_RDONLY: number; + export var O_WRONLY: number; + export var O_RDWR: number; + export var S_IFMT: number; + export var S_IFREG: number; + export var S_IFDIR: number; + export var S_IFCHR: number; + export var S_IFBLK: number; + export var S_IFIFO: number; + export var S_IFSOCK: number; + export var S_IRWXU: number; + export var S_IRUSR: number; + export var S_IWUSR: number; + export var S_IXUSR: number; + export var S_IRWXG: number; + export var S_IRGRP: number; + export var S_IWGRP: number; + export var S_IXGRP: number; + export var S_IRWXO: number; + export var S_IROTH: number; + export var S_IWOTH: number; + export var S_IXOTH: number; + export var S_IFLNK: number; + export var O_CREAT: number; + export var O_EXCL: number; + export var O_NOCTTY: number; + export var O_DIRECTORY: number; + export var O_NOATIME: number; + export var O_NOFOLLOW: number; + export var O_SYNC: number; + export var O_DSYNC: number; + export var O_SYMLINK: number; + export var O_DIRECT: number; + export var O_NONBLOCK: number; + export var O_TRUNC: number; + export var O_APPEND: number; + export var F_OK: number; + export var R_OK: number; + export var W_OK: number; + export var X_OK: number; + export var UV_UDP_REUSEADDR: number; + export var SIGQUIT: number; + export var SIGTRAP: number; + export var SIGIOT: number; + export var SIGBUS: number; + export var SIGUSR1: number; + export var SIGUSR2: number; + export var SIGPIPE: number; + export var SIGALRM: number; + export var SIGCHLD: number; + export var SIGSTKFLT: number; + export var SIGCONT: number; + export var SIGSTOP: number; + export var SIGTSTP: number; + export var SIGTTIN: number; + export var SIGTTOU: number; + export var SIGURG: number; + export var SIGXCPU: number; + export var SIGXFSZ: number; + export var SIGVTALRM: number; + export var SIGPROF: number; + export var SIGIO: number; + export var SIGPOLL: number; + export var SIGPWR: number; + export var SIGSYS: number; + export var SIGUNUSED: number; + export var defaultCoreCipherList: string; + export var defaultCipherList: string; + export var ENGINE_METHOD_RSA: number; + export var ALPN_ENABLED: number; +} + +declare module "module" { + export = NodeJS.Module; } declare module "process" { - export = process; + export = process; } +// tslint:disable-next-line:no-declare-current-package declare module "v8" { - interface HeapSpaceInfo { - space_name: string; - space_size: number; - space_used_size: number; - space_available_size: number; - physical_space_size: number; - } + interface HeapSpaceInfo { + space_name: string; + space_size: number; + space_used_size: number; + space_available_size: number; + physical_space_size: number; + } - //** Signifies if the --zap_code_space option is enabled or not. 1 == enabled, 0 == disabled. */ - type DoesZapCodeSpaceFlag = 0 | 1; + // ** Signifies if the --zap_code_space option is enabled or not. 1 == enabled, 0 == disabled. */ + type DoesZapCodeSpaceFlag = 0 | 1; - interface HeapInfo { - total_heap_size: number; - total_heap_size_executable: number; - total_physical_size: number; - total_available_size: number; - used_heap_size: number; - heap_size_limit: number; - malloced_memory: number; - peak_malloced_memory: number; - does_zap_garbage: DoesZapCodeSpaceFlag; - } + interface HeapInfo { + total_heap_size: number; + total_heap_size_executable: number; + total_physical_size: number; + total_available_size: number; + used_heap_size: number; + heap_size_limit: number; + malloced_memory: number; + peak_malloced_memory: number; + does_zap_garbage: DoesZapCodeSpaceFlag; + } - export function getHeapStatistics(): HeapInfo; - export function getHeapSpaceStatistics(): HeapSpaceInfo[]; - export function setFlagsFromString(flags: string): void; + export function getHeapStatistics(): HeapInfo; + export function getHeapSpaceStatistics(): HeapSpaceInfo[]; + export function setFlagsFromString(flags: string): void; } declare module "timers" { - export function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; - export function clearTimeout(timeoutId: NodeJS.Timer): void; - export function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; - export function clearInterval(intervalId: NodeJS.Timer): void; - export function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; - export function clearImmediate(immediateId: any): void; + export function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; + export namespace setTimeout { + export function __promisify__(ms: number): Promise<void>; + export function __promisify__<T>(ms: number, value: T): Promise<T>; + } + export function clearTimeout(timeoutId: NodeJS.Timer): void; + export function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; + export function clearInterval(intervalId: NodeJS.Timer): void; + export function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; + export namespace setImmediate { + export function __promisify__(): Promise<void>; + export function __promisify__<T>(value: T): Promise<T>; + } + export function clearImmediate(immediateId: any): void; } declare module "console" { - export = console; + export = console; } /** - * _debugger module is not documented. - * Source code is at https://github.com/nodejs/node/blob/master/lib/_debugger.js + * Async Hooks module: https://nodejs.org/api/async_hooks.html */ -declare module "_debugger" { - export interface Packet { - raw: string; - headers: string[]; - body: Message; - } +declare module "async_hooks" { + /** + * Returns the asyncId of the current execution context. + */ + export function executionAsyncId(): number; + /// @deprecated - replaced by executionAsyncId() + export function currentId(): number; - export interface Message { - seq: number; - type: string; - } + /** + * Returns the ID of the resource responsible for calling the callback that is currently being executed. + */ + export function triggerAsyncId(): number; + /// @deprecated - replaced by triggerAsyncId() + export function triggerId(): number; - export interface RequestInfo { - command: string; - arguments: any; - } + export interface HookCallbacks { + /** + * Called when a class is constructed that has the possibility to emit an asynchronous event. + * @param asyncId a unique ID for the async resource + * @param type the type of the async resource + * @param triggerAsyncId the unique ID of the async resource in whose execution context this async resource was created + * @param resource reference to the resource representing the async operation, needs to be released during destroy + */ + init?(asyncId: number, type: string, triggerAsyncId: number, resource: Object): void; - export interface Request extends Message, RequestInfo { - } + /** + * When an asynchronous operation is initiated or completes a callback is called to notify the user. + * The before callback is called just before said callback is executed. + * @param asyncId the unique identifier assigned to the resource about to execute the callback. + */ + before?(asyncId: number): void; - export interface Event extends Message { - event: string; - body?: any; - } + /** + * Called immediately after the callback specified in before is completed. + * @param asyncId the unique identifier assigned to the resource which has executed the callback. + */ + after?(asyncId: number): void; - export interface Response extends Message { - request_seq: number; - success: boolean; - /** Contains error message if success === false. */ - message?: string; - /** Contains message body if success === true. */ - body?: any; - } + /** + * Called when a promise has resolve() called. This may not be in the same execution id + * as the promise itself. + * @param asyncId the unique id for the promise that was resolve()d. + */ + promiseResolve?(asyncId: number): void; - export interface BreakpointMessageBody { - type: string; - target: number; - line: number; - } + /** + * Called after the resource corresponding to asyncId is destroyed + * @param asyncId a unique ID for the async resource + */ + destroy?(asyncId: number): void; + } - export class Protocol { - res: Packet; - state: string; - execute(data: string): void; - serialize(rq: Request): string; - onResponse: (pkt: Packet) => void; - } + export interface AsyncHook { + /** + * Enable the callbacks for a given AsyncHook instance. If no callbacks are provided enabling is a noop. + */ + enable(): this; - export var NO_FRAME: number; - export var port: number; + /** + * Disable the callbacks for a given AsyncHook instance from the global pool of AsyncHook callbacks to be executed. Once a hook has been disabled it will not be called again until enabled. + */ + disable(): this; + } - export interface ScriptDesc { - name: string; - id: number; - isNative?: boolean; - handle?: number; - type: string; - lineOffset?: number; - columnOffset?: number; - lineCount?: number; - } + /** + * Registers functions to be called for different lifetime events of each async operation. + * @param options the callbacks to register + * @return an AsyncHooks instance used for disabling and enabling hooks + */ + export function createHook(options: HookCallbacks): AsyncHook; - export interface Breakpoint { - id: number; - scriptId: number; - script: ScriptDesc; - line: number; - condition?: string; - scriptReq?: string; - } + export interface AsyncResourceOptions { + /** + * The ID of the execution context that created this async event. + * Default: `executionAsyncId()` + */ + triggerAsyncId?: number; - export interface RequestHandler { - (err: boolean, body: Message, res: Packet): void; - request_seq?: number; - } + /** + * Disables automatic `emitDestroy` when the object is garbage collected. + * This usually does not need to be set (even if `emitDestroy` is called + * manually), unless the resource's `asyncId` is retrieved and the + * sensitive API's `emitDestroy` is called with it. + * Default: `false` + */ + requireManualDestroy?: boolean; + } - export interface ResponseBodyHandler { - (err: boolean, body?: any): void; - request_seq?: number; - } + /** + * The class AsyncResource was designed to be extended by the embedder's async resources. + * Using this users can easily trigger the lifetime events of their own resources. + */ + export class AsyncResource { + /** + * AsyncResource() is meant to be extended. Instantiating a + * new AsyncResource() also triggers init. If triggerAsyncId is omitted then + * async_hook.executionAsyncId() is used. + * @param type The type of async event. + * @param triggerAsyncId The ID of the execution context that created + * this async event (default: `executionAsyncId()`), or an + * AsyncResourceOptions object (since 8.10) + */ + constructor(type: string, triggerAsyncId?: number | AsyncResourceOptions); - export interface ExceptionInfo { - text: string; - } + /** + * Call AsyncHooks before callbacks. + */ + emitBefore(): void; - export interface BreakResponse { - script?: ScriptDesc; - exception?: ExceptionInfo; - sourceLine: number; - sourceLineText: string; - sourceColumn: number; - } + /** + * Call AsyncHooks after callbacks + */ + emitAfter(): void; - export function SourceInfo(body: BreakResponse): string; + /** + * Call AsyncHooks destroy callbacks. + */ + emitDestroy(): void; - export interface ClientInstance extends NodeJS.EventEmitter { - protocol: Protocol; - scripts: ScriptDesc[]; - handles: ScriptDesc[]; - breakpoints: Breakpoint[]; - currentSourceLine: number; - currentSourceColumn: number; - currentSourceLineText: string; - currentFrame: number; - currentScript: string; + /** + * @return the unique ID assigned to this AsyncResource instance. + */ + asyncId(): number; - connect(port: number, host: string): void; - req(req: any, cb: RequestHandler): void; - reqFrameEval(code: string, frame: number, cb: RequestHandler): void; - mirrorObject(obj: any, depth: number, cb: ResponseBodyHandler): void; - setBreakpoint(rq: BreakpointMessageBody, cb: RequestHandler): void; - clearBreakpoint(rq: Request, cb: RequestHandler): void; - listbreakpoints(cb: RequestHandler): void; - reqSource(from: number, to: number, cb: RequestHandler): void; - reqScripts(cb: any): void; - reqContinue(cb: RequestHandler): void; - } - - export var Client: { - new(): ClientInstance - } + /** + * @return the trigger ID for this AsyncResource instance. + */ + triggerAsyncId(): number; + } } + +declare module "http2" { + import * as events from "events"; + import * as fs from "fs"; + import * as net from "net"; + import * as stream from "stream"; + import * as tls from "tls"; + import * as url from "url"; + + import { IncomingHttpHeaders, OutgoingHttpHeaders } from "http"; + export { IncomingHttpHeaders, OutgoingHttpHeaders } from "http"; + + // Http2Stream + + export interface StreamPriorityOptions { + exclusive?: boolean; + parent?: number; + weight?: number; + silent?: boolean; + } + + export interface StreamState { + localWindowSize?: number; + state?: number; + streamLocalClose?: number; + streamRemoteClose?: number; + sumDependencyWeight?: number; + weight?: number; + } + + export interface ServerStreamResponseOptions { + endStream?: boolean; + getTrailers?: (trailers: OutgoingHttpHeaders) => void; + } + + export interface StatOptions { + offset: number; + length: number; + } + + export interface ServerStreamFileResponseOptions { + statCheck?: (stats: fs.Stats, headers: OutgoingHttpHeaders, statOptions: StatOptions) => void | boolean; + getTrailers?: (trailers: OutgoingHttpHeaders) => void; + offset?: number; + length?: number; + } + + export interface ServerStreamFileResponseOptionsWithError extends ServerStreamFileResponseOptions { + onError?: (err: NodeJS.ErrnoException) => void; + } + + export interface Http2Stream extends stream.Duplex { + readonly aborted: boolean; + readonly destroyed: boolean; + priority(options: StreamPriorityOptions): void; + readonly rstCode: number; + rstStream(code: number): void; + rstWithNoError(): void; + rstWithProtocolError(): void; + rstWithCancel(): void; + rstWithRefuse(): void; + rstWithInternalError(): void; + readonly session: Http2Session; + setTimeout(msecs: number, callback?: () => void): void; + readonly state: StreamState; + + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "aborted", listener: () => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "data", listener: (chunk: Buffer | string) => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "finish", listener: () => void): this; + addListener(event: "frameError", listener: (frameType: number, errorCode: number) => void): this; + addListener(event: "pipe", listener: (src: stream.Readable) => void): this; + addListener(event: "unpipe", listener: (src: stream.Readable) => void): this; + addListener(event: "streamClosed", listener: (code: number) => void): this; + addListener(event: "timeout", listener: () => void): this; + addListener(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "aborted"): boolean; + emit(event: "close"): boolean; + emit(event: "data", chunk: Buffer | string): boolean; + emit(event: "drain"): boolean; + emit(event: "end"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "finish"): boolean; + emit(event: "frameError", frameType: number, errorCode: number): boolean; + emit(event: "pipe", src: stream.Readable): boolean; + emit(event: "unpipe", src: stream.Readable): boolean; + emit(event: "streamClosed", code: number): boolean; + emit(event: "timeout"): boolean; + emit(event: "trailers", trailers: IncomingHttpHeaders, flags: number): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "aborted", listener: () => void): this; + on(event: "close", listener: () => void): this; + on(event: "data", listener: (chunk: Buffer | string) => void): this; + on(event: "drain", listener: () => void): this; + on(event: "end", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "finish", listener: () => void): this; + on(event: "frameError", listener: (frameType: number, errorCode: number) => void): this; + on(event: "pipe", listener: (src: stream.Readable) => void): this; + on(event: "unpipe", listener: (src: stream.Readable) => void): this; + on(event: "streamClosed", listener: (code: number) => void): this; + on(event: "timeout", listener: () => void): this; + on(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "aborted", listener: () => void): this; + once(event: "close", listener: () => void): this; + once(event: "data", listener: (chunk: Buffer | string) => void): this; + once(event: "drain", listener: () => void): this; + once(event: "end", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "finish", listener: () => void): this; + once(event: "frameError", listener: (frameType: number, errorCode: number) => void): this; + once(event: "pipe", listener: (src: stream.Readable) => void): this; + once(event: "unpipe", listener: (src: stream.Readable) => void): this; + once(event: "streamClosed", listener: (code: number) => void): this; + once(event: "timeout", listener: () => void): this; + once(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "aborted", listener: () => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "data", listener: (chunk: Buffer | string) => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "finish", listener: () => void): this; + prependListener(event: "frameError", listener: (frameType: number, errorCode: number) => void): this; + prependListener(event: "pipe", listener: (src: stream.Readable) => void): this; + prependListener(event: "unpipe", listener: (src: stream.Readable) => void): this; + prependListener(event: "streamClosed", listener: (code: number) => void): this; + prependListener(event: "timeout", listener: () => void): this; + prependListener(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "aborted", listener: () => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "data", listener: (chunk: Buffer | string) => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "finish", listener: () => void): this; + prependOnceListener(event: "frameError", listener: (frameType: number, errorCode: number) => void): this; + prependOnceListener(event: "pipe", listener: (src: stream.Readable) => void): this; + prependOnceListener(event: "unpipe", listener: (src: stream.Readable) => void): this; + prependOnceListener(event: "streamClosed", listener: (code: number) => void): this; + prependOnceListener(event: "timeout", listener: () => void): this; + prependOnceListener(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): this; + } + + export interface ClientHttp2Stream extends Http2Stream { + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "headers", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + addListener(event: "push", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + addListener(event: "response", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "headers", headers: IncomingHttpHeaders, flags: number): boolean; + emit(event: "push", headers: IncomingHttpHeaders, flags: number): boolean; + emit(event: "response", headers: IncomingHttpHeaders, flags: number): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "headers", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + on(event: "push", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + on(event: "response", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "headers", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + once(event: "push", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + once(event: "response", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "headers", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + prependListener(event: "push", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + prependListener(event: "response", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "headers", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + prependOnceListener(event: "push", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + prependOnceListener(event: "response", listener: (headers: IncomingHttpHeaders, flags: number) => void): this; + } + + export interface ServerHttp2Stream extends Http2Stream { + additionalHeaders(headers: OutgoingHttpHeaders): void; + readonly headersSent: boolean; + readonly pushAllowed: boolean; + pushStream(headers: OutgoingHttpHeaders, callback?: (pushStream: ServerHttp2Stream) => void): void; + pushStream(headers: OutgoingHttpHeaders, options?: StreamPriorityOptions, callback?: (pushStream: ServerHttp2Stream) => void): void; + respond(headers?: OutgoingHttpHeaders, options?: ServerStreamResponseOptions): void; + respondWithFD(fd: number, headers?: OutgoingHttpHeaders, options?: ServerStreamFileResponseOptions): void; + respondWithFile(path: string, headers?: OutgoingHttpHeaders, options?: ServerStreamFileResponseOptionsWithError): void; + } + + // Http2Session + + export interface Settings { + headerTableSize?: number; + enablePush?: boolean; + initialWindowSize?: number; + maxFrameSize?: number; + maxConcurrentStreams?: number; + maxHeaderListSize?: number; + } + + export interface ClientSessionRequestOptions { + endStream?: boolean; + exclusive?: boolean; + parent?: number; + weight?: number; + getTrailers?: (trailers: OutgoingHttpHeaders, flags: number) => void; + } + + export interface SessionShutdownOptions { + graceful?: boolean; + errorCode?: number; + lastStreamID?: number; + opaqueData?: Buffer | Uint8Array; + } + + export interface SessionState { + effectiveLocalWindowSize?: number; + effectiveRecvDataLength?: number; + nextStreamID?: number; + localWindowSize?: number; + lastProcStreamID?: number; + remoteWindowSize?: number; + outboundQueueSize?: number; + deflateDynamicTableSize?: number; + inflateDynamicTableSize?: number; + } + + export interface Http2Session extends events.EventEmitter { + destroy(): void; + readonly destroyed: boolean; + readonly localSettings: Settings; + readonly pendingSettingsAck: boolean; + readonly remoteSettings: Settings; + rstStream(stream: Http2Stream, code?: number): void; + setTimeout(msecs: number, callback?: () => void): void; + shutdown(callback?: () => void): void; + shutdown(options: SessionShutdownOptions, callback?: () => void): void; + readonly socket: net.Socket | tls.TLSSocket; + readonly state: SessionState; + priority(stream: Http2Stream, options: StreamPriorityOptions): void; + settings(settings: Settings): void; + readonly type: number; + + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "frameError", listener: (frameType: number, errorCode: number, streamID: number) => void): this; + addListener(event: "goaway", listener: (errorCode: number, lastStreamID: number, opaqueData: Buffer) => void): this; + addListener(event: "localSettings", listener: (settings: Settings) => void): this; + addListener(event: "remoteSettings", listener: (settings: Settings) => void): this; + addListener(event: "socketError", listener: (err: Error) => void): this; + addListener(event: "timeout", listener: () => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "frameError", frameType: number, errorCode: number, streamID: number): boolean; + emit(event: "goaway", errorCode: number, lastStreamID: number, opaqueData: Buffer): boolean; + emit(event: "localSettings", settings: Settings): boolean; + emit(event: "remoteSettings", settings: Settings): boolean; + emit(event: "socketError", err: Error): boolean; + emit(event: "timeout"): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "frameError", listener: (frameType: number, errorCode: number, streamID: number) => void): this; + on(event: "goaway", listener: (errorCode: number, lastStreamID: number, opaqueData: Buffer) => void): this; + on(event: "localSettings", listener: (settings: Settings) => void): this; + on(event: "remoteSettings", listener: (settings: Settings) => void): this; + on(event: "socketError", listener: (err: Error) => void): this; + on(event: "timeout", listener: () => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "frameError", listener: (frameType: number, errorCode: number, streamID: number) => void): this; + once(event: "goaway", listener: (errorCode: number, lastStreamID: number, opaqueData: Buffer) => void): this; + once(event: "localSettings", listener: (settings: Settings) => void): this; + once(event: "remoteSettings", listener: (settings: Settings) => void): this; + once(event: "socketError", listener: (err: Error) => void): this; + once(event: "timeout", listener: () => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "frameError", listener: (frameType: number, errorCode: number, streamID: number) => void): this; + prependListener(event: "goaway", listener: (errorCode: number, lastStreamID: number, opaqueData: Buffer) => void): this; + prependListener(event: "localSettings", listener: (settings: Settings) => void): this; + prependListener(event: "remoteSettings", listener: (settings: Settings) => void): this; + prependListener(event: "socketError", listener: (err: Error) => void): this; + prependListener(event: "timeout", listener: () => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "frameError", listener: (frameType: number, errorCode: number, streamID: number) => void): this; + prependOnceListener(event: "goaway", listener: (errorCode: number, lastStreamID: number, opaqueData: Buffer) => void): this; + prependOnceListener(event: "localSettings", listener: (settings: Settings) => void): this; + prependOnceListener(event: "remoteSettings", listener: (settings: Settings) => void): this; + prependOnceListener(event: "socketError", listener: (err: Error) => void): this; + prependOnceListener(event: "timeout", listener: () => void): this; + } + + export interface ClientHttp2Session extends Http2Session { + request(headers?: OutgoingHttpHeaders, options?: ClientSessionRequestOptions): ClientHttp2Stream; + + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "connect", listener: (session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + addListener(event: "stream", listener: (stream: ClientHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "connect", session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket): boolean; + emit(event: "stream", stream: ClientHttp2Stream, headers: IncomingHttpHeaders, flags: number): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "connect", listener: (session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + on(event: "stream", listener: (stream: ClientHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "connect", listener: (session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + once(event: "stream", listener: (stream: ClientHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "connect", listener: (session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + prependListener(event: "stream", listener: (stream: ClientHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "connect", listener: (session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + prependOnceListener(event: "stream", listener: (stream: ClientHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + } + + export interface ServerHttp2Session extends Http2Session { + readonly server: Http2Server | Http2SecureServer; + + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "connect", listener: (session: ServerHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + addListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "connect", session: ServerHttp2Session, socket: net.Socket | tls.TLSSocket): boolean; + emit(event: "stream", stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "connect", listener: (session: ServerHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + on(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "connect", listener: (session: ServerHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + once(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "connect", listener: (session: ServerHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + prependListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "connect", listener: (session: ServerHttp2Session, socket: net.Socket | tls.TLSSocket) => void): this; + prependOnceListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + } + + // Http2Server + + export interface SessionOptions { + maxDeflateDynamicTableSize?: number; + maxReservedRemoteStreams?: number; + maxSendHeaderBlockLength?: number; + paddingStrategy?: number; + peerMaxConcurrentStreams?: number; + selectPadding?: (frameLen: number, maxFrameLen: number) => number; + settings?: Settings; + } + + export type ClientSessionOptions = SessionOptions; + export type ServerSessionOptions = SessionOptions; + + export interface SecureClientSessionOptions extends ClientSessionOptions, tls.ConnectionOptions { } + export interface SecureServerSessionOptions extends ServerSessionOptions, tls.TlsOptions { } + + export interface ServerOptions extends ServerSessionOptions { + allowHTTP1?: boolean; + } + + export interface SecureServerOptions extends SecureServerSessionOptions { + allowHTTP1?: boolean; + } + + export interface Http2Server extends net.Server { + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + addListener(event: "sessionError", listener: (err: Error) => void): this; + addListener(event: "socketError", listener: (err: Error) => void): this; + addListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + addListener(event: "timeout", listener: () => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "request", request: Http2ServerRequest, response: Http2ServerResponse): boolean; + emit(event: "sessionError", err: Error): boolean; + emit(event: "socketError", err: Error): boolean; + emit(event: "stream", stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number): boolean; + emit(event: "timeout"): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + on(event: "sessionError", listener: (err: Error) => void): this; + on(event: "socketError", listener: (err: Error) => void): this; + on(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + on(event: "timeout", listener: () => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + once(event: "sessionError", listener: (err: Error) => void): this; + once(event: "socketError", listener: (err: Error) => void): this; + once(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + once(event: "timeout", listener: () => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + prependListener(event: "sessionError", listener: (err: Error) => void): this; + prependListener(event: "socketError", listener: (err: Error) => void): this; + prependListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + prependListener(event: "timeout", listener: () => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + prependOnceListener(event: "sessionError", listener: (err: Error) => void): this; + prependOnceListener(event: "socketError", listener: (err: Error) => void): this; + prependOnceListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + prependOnceListener(event: "timeout", listener: () => void): this; + } + + export interface Http2SecureServer extends tls.Server { + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + addListener(event: "sessionError", listener: (err: Error) => void): this; + addListener(event: "socketError", listener: (err: Error) => void): this; + addListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + addListener(event: "timeout", listener: () => void): this; + addListener(event: "unknownProtocol", listener: (socket: tls.TLSSocket) => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "request", request: Http2ServerRequest, response: Http2ServerResponse): boolean; + emit(event: "sessionError", err: Error): boolean; + emit(event: "socketError", err: Error): boolean; + emit(event: "stream", stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number): boolean; + emit(event: "timeout"): boolean; + emit(event: "unknownProtocol", socket: tls.TLSSocket): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + on(event: "sessionError", listener: (err: Error) => void): this; + on(event: "socketError", listener: (err: Error) => void): this; + on(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + on(event: "timeout", listener: () => void): this; + on(event: "unknownProtocol", listener: (socket: tls.TLSSocket) => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + once(event: "sessionError", listener: (err: Error) => void): this; + once(event: "socketError", listener: (err: Error) => void): this; + once(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + once(event: "timeout", listener: () => void): this; + once(event: "unknownProtocol", listener: (socket: tls.TLSSocket) => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + prependListener(event: "sessionError", listener: (err: Error) => void): this; + prependListener(event: "socketError", listener: (err: Error) => void): this; + prependListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + prependListener(event: "timeout", listener: () => void): this; + prependListener(event: "unknownProtocol", listener: (socket: tls.TLSSocket) => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "request", listener: (request: Http2ServerRequest, response: Http2ServerResponse) => void): this; + prependOnceListener(event: "sessionError", listener: (err: Error) => void): this; + prependOnceListener(event: "socketError", listener: (err: Error) => void): this; + prependOnceListener(event: "stream", listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void): this; + prependOnceListener(event: "timeout", listener: () => void): this; + prependOnceListener(event: "unknownProtocol", listener: (socket: tls.TLSSocket) => void): this; + } + + export interface Http2ServerRequest extends stream.Readable { + headers: IncomingHttpHeaders; + httpVersion: string; + method: string; + rawHeaders: string[]; + rawTrailers: string[]; + setTimeout(msecs: number, callback?: () => void): void; + socket: net.Socket | tls.TLSSocket; + stream: ServerHttp2Stream; + trailers: IncomingHttpHeaders; + url: string; + + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "aborted", hadError: boolean, code: number): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + } + + export interface Http2ServerResponse extends events.EventEmitter { + addTrailers(trailers: OutgoingHttpHeaders): void; + connection: net.Socket | tls.TLSSocket; + end(callback?: () => void): void; + end(data?: string | Buffer, callback?: () => void): void; + end(data?: string | Buffer, encoding?: string, callback?: () => void): void; + readonly finished: boolean; + getHeader(name: string): string; + getHeaderNames(): string[]; + getHeaders(): OutgoingHttpHeaders; + hasHeader(name: string): boolean; + readonly headersSent: boolean; + removeHeader(name: string): void; + sendDate: boolean; + setHeader(name: string, value: number | string | string[]): void; + setTimeout(msecs: number, callback?: () => void): void; + socket: net.Socket | tls.TLSSocket; + statusCode: number; + statusMessage: ''; + stream: ServerHttp2Stream; + write(chunk: string | Buffer, callback?: (err: Error) => void): boolean; + write(chunk: string | Buffer, encoding?: string, callback?: (err: Error) => void): boolean; + writeContinue(): void; + writeHead(statusCode: number, headers?: OutgoingHttpHeaders): void; + writeHead(statusCode: number, statusMessage?: string, headers?: OutgoingHttpHeaders): void; + createPushResponse(headers: OutgoingHttpHeaders, callback: (err: Error | null, res: Http2ServerResponse) => void): void; + + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "error", listener: (error: Error) => void): this; + addListener(event: "finish", listener: () => void): this; + + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "aborted", hadError: boolean, code: number): boolean; + emit(event: "close"): boolean; + emit(event: "drain"): boolean; + emit(event: "error", error: Error): boolean; + emit(event: "finish"): boolean; + + on(event: string, listener: (...args: any[]) => void): this; + on(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + on(event: "close", listener: () => void): this; + on(event: "drain", listener: () => void): this; + on(event: "error", listener: (error: Error) => void): this; + on(event: "finish", listener: () => void): this; + + once(event: string, listener: (...args: any[]) => void): this; + once(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + once(event: "close", listener: () => void): this; + once(event: "drain", listener: () => void): this; + once(event: "error", listener: (error: Error) => void): this; + once(event: "finish", listener: () => void): this; + + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "error", listener: (error: Error) => void): this; + prependListener(event: "finish", listener: () => void): this; + + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "aborted", listener: (hadError: boolean, code: number) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "error", listener: (error: Error) => void): this; + prependOnceListener(event: "finish", listener: () => void): this; + } + + // Public API + + export namespace constants { + export const NGHTTP2_SESSION_SERVER: number; + export const NGHTTP2_SESSION_CLIENT: number; + export const NGHTTP2_STREAM_STATE_IDLE: number; + export const NGHTTP2_STREAM_STATE_OPEN: number; + export const NGHTTP2_STREAM_STATE_RESERVED_LOCAL: number; + export const NGHTTP2_STREAM_STATE_RESERVED_REMOTE: number; + export const NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL: number; + export const NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE: number; + export const NGHTTP2_STREAM_STATE_CLOSED: number; + export const NGHTTP2_NO_ERROR: number; + export const NGHTTP2_PROTOCOL_ERROR: number; + export const NGHTTP2_INTERNAL_ERROR: number; + export const NGHTTP2_FLOW_CONTROL_ERROR: number; + export const NGHTTP2_SETTINGS_TIMEOUT: number; + export const NGHTTP2_STREAM_CLOSED: number; + export const NGHTTP2_FRAME_SIZE_ERROR: number; + export const NGHTTP2_REFUSED_STREAM: number; + export const NGHTTP2_CANCEL: number; + export const NGHTTP2_COMPRESSION_ERROR: number; + export const NGHTTP2_CONNECT_ERROR: number; + export const NGHTTP2_ENHANCE_YOUR_CALM: number; + export const NGHTTP2_INADEQUATE_SECURITY: number; + export const NGHTTP2_HTTP_1_1_REQUIRED: number; + export const NGHTTP2_ERR_FRAME_SIZE_ERROR: number; + export const NGHTTP2_FLAG_NONE: number; + export const NGHTTP2_FLAG_END_STREAM: number; + export const NGHTTP2_FLAG_END_HEADERS: number; + export const NGHTTP2_FLAG_ACK: number; + export const NGHTTP2_FLAG_PADDED: number; + export const NGHTTP2_FLAG_PRIORITY: number; + export const DEFAULT_SETTINGS_HEADER_TABLE_SIZE: number; + export const DEFAULT_SETTINGS_ENABLE_PUSH: number; + export const DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE: number; + export const DEFAULT_SETTINGS_MAX_FRAME_SIZE: number; + export const MAX_MAX_FRAME_SIZE: number; + export const MIN_MAX_FRAME_SIZE: number; + export const MAX_INITIAL_WINDOW_SIZE: number; + export const NGHTTP2_DEFAULT_WEIGHT: number; + export const NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: number; + export const NGHTTP2_SETTINGS_ENABLE_PUSH: number; + export const NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: number; + export const NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: number; + export const NGHTTP2_SETTINGS_MAX_FRAME_SIZE: number; + export const NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: number; + export const PADDING_STRATEGY_NONE: number; + export const PADDING_STRATEGY_MAX: number; + export const PADDING_STRATEGY_CALLBACK: number; + export const HTTP2_HEADER_STATUS: string; + export const HTTP2_HEADER_METHOD: string; + export const HTTP2_HEADER_AUTHORITY: string; + export const HTTP2_HEADER_SCHEME: string; + export const HTTP2_HEADER_PATH: string; + export const HTTP2_HEADER_ACCEPT_CHARSET: string; + export const HTTP2_HEADER_ACCEPT_ENCODING: string; + export const HTTP2_HEADER_ACCEPT_LANGUAGE: string; + export const HTTP2_HEADER_ACCEPT_RANGES: string; + export const HTTP2_HEADER_ACCEPT: string; + export const HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN: string; + export const HTTP2_HEADER_AGE: string; + export const HTTP2_HEADER_ALLOW: string; + export const HTTP2_HEADER_AUTHORIZATION: string; + export const HTTP2_HEADER_CACHE_CONTROL: string; + export const HTTP2_HEADER_CONNECTION: string; + export const HTTP2_HEADER_CONTENT_DISPOSITION: string; + export const HTTP2_HEADER_CONTENT_ENCODING: string; + export const HTTP2_HEADER_CONTENT_LANGUAGE: string; + export const HTTP2_HEADER_CONTENT_LENGTH: string; + export const HTTP2_HEADER_CONTENT_LOCATION: string; + export const HTTP2_HEADER_CONTENT_MD5: string; + export const HTTP2_HEADER_CONTENT_RANGE: string; + export const HTTP2_HEADER_CONTENT_TYPE: string; + export const HTTP2_HEADER_COOKIE: string; + export const HTTP2_HEADER_DATE: string; + export const HTTP2_HEADER_ETAG: string; + export const HTTP2_HEADER_EXPECT: string; + export const HTTP2_HEADER_EXPIRES: string; + export const HTTP2_HEADER_FROM: string; + export const HTTP2_HEADER_HOST: string; + export const HTTP2_HEADER_IF_MATCH: string; + export const HTTP2_HEADER_IF_MODIFIED_SINCE: string; + export const HTTP2_HEADER_IF_NONE_MATCH: string; + export const HTTP2_HEADER_IF_RANGE: string; + export const HTTP2_HEADER_IF_UNMODIFIED_SINCE: string; + export const HTTP2_HEADER_LAST_MODIFIED: string; + export const HTTP2_HEADER_LINK: string; + export const HTTP2_HEADER_LOCATION: string; + export const HTTP2_HEADER_MAX_FORWARDS: string; + export const HTTP2_HEADER_PREFER: string; + export const HTTP2_HEADER_PROXY_AUTHENTICATE: string; + export const HTTP2_HEADER_PROXY_AUTHORIZATION: string; + export const HTTP2_HEADER_RANGE: string; + export const HTTP2_HEADER_REFERER: string; + export const HTTP2_HEADER_REFRESH: string; + export const HTTP2_HEADER_RETRY_AFTER: string; + export const HTTP2_HEADER_SERVER: string; + export const HTTP2_HEADER_SET_COOKIE: string; + export const HTTP2_HEADER_STRICT_TRANSPORT_SECURITY: string; + export const HTTP2_HEADER_TRANSFER_ENCODING: string; + export const HTTP2_HEADER_TE: string; + export const HTTP2_HEADER_UPGRADE: string; + export const HTTP2_HEADER_USER_AGENT: string; + export const HTTP2_HEADER_VARY: string; + export const HTTP2_HEADER_VIA: string; + export const HTTP2_HEADER_WWW_AUTHENTICATE: string; + export const HTTP2_HEADER_HTTP2_SETTINGS: string; + export const HTTP2_HEADER_KEEP_ALIVE: string; + export const HTTP2_HEADER_PROXY_CONNECTION: string; + export const HTTP2_METHOD_ACL: string; + export const HTTP2_METHOD_BASELINE_CONTROL: string; + export const HTTP2_METHOD_BIND: string; + export const HTTP2_METHOD_CHECKIN: string; + export const HTTP2_METHOD_CHECKOUT: string; + export const HTTP2_METHOD_CONNECT: string; + export const HTTP2_METHOD_COPY: string; + export const HTTP2_METHOD_DELETE: string; + export const HTTP2_METHOD_GET: string; + export const HTTP2_METHOD_HEAD: string; + export const HTTP2_METHOD_LABEL: string; + export const HTTP2_METHOD_LINK: string; + export const HTTP2_METHOD_LOCK: string; + export const HTTP2_METHOD_MERGE: string; + export const HTTP2_METHOD_MKACTIVITY: string; + export const HTTP2_METHOD_MKCALENDAR: string; + export const HTTP2_METHOD_MKCOL: string; + export const HTTP2_METHOD_MKREDIRECTREF: string; + export const HTTP2_METHOD_MKWORKSPACE: string; + export const HTTP2_METHOD_MOVE: string; + export const HTTP2_METHOD_OPTIONS: string; + export const HTTP2_METHOD_ORDERPATCH: string; + export const HTTP2_METHOD_PATCH: string; + export const HTTP2_METHOD_POST: string; + export const HTTP2_METHOD_PRI: string; + export const HTTP2_METHOD_PROPFIND: string; + export const HTTP2_METHOD_PROPPATCH: string; + export const HTTP2_METHOD_PUT: string; + export const HTTP2_METHOD_REBIND: string; + export const HTTP2_METHOD_REPORT: string; + export const HTTP2_METHOD_SEARCH: string; + export const HTTP2_METHOD_TRACE: string; + export const HTTP2_METHOD_UNBIND: string; + export const HTTP2_METHOD_UNCHECKOUT: string; + export const HTTP2_METHOD_UNLINK: string; + export const HTTP2_METHOD_UNLOCK: string; + export const HTTP2_METHOD_UPDATE: string; + export const HTTP2_METHOD_UPDATEREDIRECTREF: string; + export const HTTP2_METHOD_VERSION_CONTROL: string; + export const HTTP_STATUS_CONTINUE: number; + export const HTTP_STATUS_SWITCHING_PROTOCOLS: number; + export const HTTP_STATUS_PROCESSING: number; + export const HTTP_STATUS_OK: number; + export const HTTP_STATUS_CREATED: number; + export const HTTP_STATUS_ACCEPTED: number; + export const HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION: number; + export const HTTP_STATUS_NO_CONTENT: number; + export const HTTP_STATUS_RESET_CONTENT: number; + export const HTTP_STATUS_PARTIAL_CONTENT: number; + export const HTTP_STATUS_MULTI_STATUS: number; + export const HTTP_STATUS_ALREADY_REPORTED: number; + export const HTTP_STATUS_IM_USED: number; + export const HTTP_STATUS_MULTIPLE_CHOICES: number; + export const HTTP_STATUS_MOVED_PERMANENTLY: number; + export const HTTP_STATUS_FOUND: number; + export const HTTP_STATUS_SEE_OTHER: number; + export const HTTP_STATUS_NOT_MODIFIED: number; + export const HTTP_STATUS_USE_PROXY: number; + export const HTTP_STATUS_TEMPORARY_REDIRECT: number; + export const HTTP_STATUS_PERMANENT_REDIRECT: number; + export const HTTP_STATUS_BAD_REQUEST: number; + export const HTTP_STATUS_UNAUTHORIZED: number; + export const HTTP_STATUS_PAYMENT_REQUIRED: number; + export const HTTP_STATUS_FORBIDDEN: number; + export const HTTP_STATUS_NOT_FOUND: number; + export const HTTP_STATUS_METHOD_NOT_ALLOWED: number; + export const HTTP_STATUS_NOT_ACCEPTABLE: number; + export const HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED: number; + export const HTTP_STATUS_REQUEST_TIMEOUT: number; + export const HTTP_STATUS_CONFLICT: number; + export const HTTP_STATUS_GONE: number; + export const HTTP_STATUS_LENGTH_REQUIRED: number; + export const HTTP_STATUS_PRECONDITION_FAILED: number; + export const HTTP_STATUS_PAYLOAD_TOO_LARGE: number; + export const HTTP_STATUS_URI_TOO_LONG: number; + export const HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE: number; + export const HTTP_STATUS_RANGE_NOT_SATISFIABLE: number; + export const HTTP_STATUS_EXPECTATION_FAILED: number; + export const HTTP_STATUS_TEAPOT: number; + export const HTTP_STATUS_MISDIRECTED_REQUEST: number; + export const HTTP_STATUS_UNPROCESSABLE_ENTITY: number; + export const HTTP_STATUS_LOCKED: number; + export const HTTP_STATUS_FAILED_DEPENDENCY: number; + export const HTTP_STATUS_UNORDERED_COLLECTION: number; + export const HTTP_STATUS_UPGRADE_REQUIRED: number; + export const HTTP_STATUS_PRECONDITION_REQUIRED: number; + export const HTTP_STATUS_TOO_MANY_REQUESTS: number; + export const HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE: number; + export const HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS: number; + export const HTTP_STATUS_INTERNAL_SERVER_ERROR: number; + export const HTTP_STATUS_NOT_IMPLEMENTED: number; + export const HTTP_STATUS_BAD_GATEWAY: number; + export const HTTP_STATUS_SERVICE_UNAVAILABLE: number; + export const HTTP_STATUS_GATEWAY_TIMEOUT: number; + export const HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED: number; + export const HTTP_STATUS_VARIANT_ALSO_NEGOTIATES: number; + export const HTTP_STATUS_INSUFFICIENT_STORAGE: number; + export const HTTP_STATUS_LOOP_DETECTED: number; + export const HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED: number; + export const HTTP_STATUS_NOT_EXTENDED: number; + export const HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED: number; + } + + export function getDefaultSettings(): Settings; + export function getPackedSettings(settings: Settings): Settings; + export function getUnpackedSettings(buf: Buffer | Uint8Array): Settings; + + export function createServer(onRequestHandler?: (request: Http2ServerRequest, response: Http2ServerResponse) => void): Http2Server; + export function createServer(options: ServerOptions, onRequestHandler?: (request: Http2ServerRequest, response: Http2ServerResponse) => void): Http2Server; + + export function createSecureServer(onRequestHandler?: (request: Http2ServerRequest, response: Http2ServerResponse) => void): Http2SecureServer; + export function createSecureServer(options: SecureServerOptions, onRequestHandler?: (request: Http2ServerRequest, response: Http2ServerResponse) => void): Http2SecureServer; + + export function connect(authority: string | url.URL, listener?: (session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket) => void): ClientHttp2Session; + export function connect(authority: string | url.URL, options?: ClientSessionOptions | SecureClientSessionOptions, listener?: (session: ClientHttp2Session, socket: net.Socket | tls.TLSSocket) => void): ClientHttp2Session; +} + +declare module "perf_hooks" { + export interface PerformanceEntry { + /** + * The total number of milliseconds elapsed for this entry. + * This value will not be meaningful for all Performance Entry types. + */ + readonly duration: number; + + /** + * The name of the performance entry. + */ + readonly name: string; + + /** + * The high resolution millisecond timestamp marking the starting time of the Performance Entry. + */ + readonly startTime: number; + + /** + * The type of the performance entry. + * Currently it may be one of: 'node', 'mark', 'measure', 'gc', or 'function'. + */ + readonly entryType: string; + + /** + * When performanceEntry.entryType is equal to 'gc', the performance.kind property identifies + * the type of garbage collection operation that occurred. + * The value may be one of perf_hooks.constants. + */ + readonly kind?: number; + } + + export interface PerformanceNodeTiming extends PerformanceEntry { + /** + * The high resolution millisecond timestamp at which the Node.js process completed bootstrap. + */ + readonly bootstrapComplete: number; + + /** + * The high resolution millisecond timestamp at which cluster processing ended. + */ + readonly clusterSetupEnd: number; + + /** + * The high resolution millisecond timestamp at which cluster processing started. + */ + readonly clusterSetupStart: number; + + /** + * The high resolution millisecond timestamp at which the Node.js event loop exited. + */ + readonly loopExit: number; + + /** + * The high resolution millisecond timestamp at which the Node.js event loop started. + */ + readonly loopStart: number; + + /** + * The high resolution millisecond timestamp at which main module load ended. + */ + readonly moduleLoadEnd: number; + + /** + * The high resolution millisecond timestamp at which main module load started. + */ + readonly moduleLoadStart: number; + + /** + * The high resolution millisecond timestamp at which the Node.js process was initialized. + */ + readonly nodeStart: number; + + /** + * The high resolution millisecond timestamp at which preload module load ended. + */ + readonly preloadModuleLoadEnd: number; + + /** + * The high resolution millisecond timestamp at which preload module load started. + */ + readonly preloadModuleLoadStart: number; + + /** + * The high resolution millisecond timestamp at which third_party_main processing ended. + */ + readonly thirdPartyMainEnd: number; + + /** + * The high resolution millisecond timestamp at which third_party_main processing started. + */ + readonly thirdPartyMainStart: number; + + /** + * The high resolution millisecond timestamp at which the V8 platform was initialized. + */ + readonly v8Start: number; + } + + export interface Performance { + /** + * If name is not provided, removes all PerformanceFunction objects from the Performance Timeline. + * If name is provided, removes entries with name. + * @param name + */ + clearFunctions(name?: string): void; + + /** + * If name is not provided, removes all PerformanceMark objects from the Performance Timeline. + * If name is provided, removes only the named mark. + * @param name + */ + clearMarks(name?: string): void; + + /** + * If name is not provided, removes all PerformanceMeasure objects from the Performance Timeline. + * If name is provided, removes only objects whose performanceEntry.name matches name. + */ + clearMeasures(name?: string): void; + + /** + * Returns a list of all PerformanceEntry objects in chronological order with respect to performanceEntry.startTime. + * @return list of all PerformanceEntry objects + */ + getEntries(): PerformanceEntry[]; + + /** + * Returns a list of all PerformanceEntry objects in chronological order with respect to performanceEntry.startTime + * whose performanceEntry.name is equal to name, and optionally, whose performanceEntry.entryType is equal to type. + * @param name + * @param type + * @return list of all PerformanceEntry objects + */ + getEntriesByName(name: string, type?: string): PerformanceEntry[]; + + /** + * Returns a list of all PerformanceEntry objects in chronological order with respect to performanceEntry.startTime + * whose performanceEntry.entryType is equal to type. + * @param type + * @return list of all PerformanceEntry objects + */ + getEntriesByType(type: string): PerformanceEntry[]; + + /** + * Creates a new PerformanceMark entry in the Performance Timeline. + * A PerformanceMark is a subclass of PerformanceEntry whose performanceEntry.entryType is always 'mark', + * and whose performanceEntry.duration is always 0. + * Performance marks are used to mark specific significant moments in the Performance Timeline. + * @param name + */ + mark(name?: string): void; + + /** + * Creates a new PerformanceMeasure entry in the Performance Timeline. + * A PerformanceMeasure is a subclass of PerformanceEntry whose performanceEntry.entryType is always 'measure', + * and whose performanceEntry.duration measures the number of milliseconds elapsed since startMark and endMark. + * + * The startMark argument may identify any existing PerformanceMark in the the Performance Timeline, or may identify + * any of the timestamp properties provided by the PerformanceNodeTiming class. If the named startMark does not exist, + * then startMark is set to timeOrigin by default. + * + * The endMark argument must identify any existing PerformanceMark in the the Performance Timeline or any of the timestamp + * properties provided by the PerformanceNodeTiming class. If the named endMark does not exist, an error will be thrown. + * @param name + * @param startMark + * @param endMark + */ + measure(name: string, startMark: string, endMark: string): void; + + /** + * An instance of the PerformanceNodeTiming class that provides performance metrics for specific Node.js operational milestones. + */ + readonly nodeTiming: PerformanceNodeTiming; + + /** + * @return the current high resolution millisecond timestamp + */ + now(): number; + + /** + * The timeOrigin specifies the high resolution millisecond timestamp from which all performance metric durations are measured. + */ + readonly timeOrigin: number; + + /** + * Wraps a function within a new function that measures the running time of the wrapped function. + * A PerformanceObserver must be subscribed to the 'function' event type in order for the timing details to be accessed. + * @param fn + */ + timerify<T extends (...optionalParams: any[]) => any>(fn: T): T; + } + + export interface PerformanceObserverEntryList { + /** + * @return a list of PerformanceEntry objects in chronological order with respect to performanceEntry.startTime. + */ + getEntries(): PerformanceEntry[]; + + /** + * @return a list of PerformanceEntry objects in chronological order with respect to performanceEntry.startTime + * whose performanceEntry.name is equal to name, and optionally, whose performanceEntry.entryType is equal to type. + */ + getEntriesByName(name: string, type?: string): PerformanceEntry[]; + + /** + * @return Returns a list of PerformanceEntry objects in chronological order with respect to performanceEntry.startTime + * whose performanceEntry.entryType is equal to type. + */ + getEntriesByType(type: string): PerformanceEntry[]; + } + + export type PerformanceObserverCallback = (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void; + + export class PerformanceObserver { + constructor(callback: PerformanceObserverCallback); + + /** + * Disconnects the PerformanceObserver instance from all notifications. + */ + disconnect(): void; + + /** + * Subscribes the PerformanceObserver instance to notifications of new PerformanceEntry instances identified by options.entryTypes. + * When options.buffered is false, the callback will be invoked once for every PerformanceEntry instance. + * Property buffered defaults to false. + * @param options + */ + observe(options: { entryTypes: string[], buffered?: boolean }): void; + } + + export namespace constants { + export const NODE_PERFORMANCE_GC_MAJOR: number; + export const NODE_PERFORMANCE_GC_MINOR: number; + export const NODE_PERFORMANCE_GC_INCREMENTAL: number; + export const NODE_PERFORMANCE_GC_WEAKCB: number; + } + + const performance: Performance; +} \ No newline at end of file diff --git a/src/typings/require.d.ts b/src/typings/require.d.ts index 6278f80dfe7..028ee087293 100644 --- a/src/typings/require.d.ts +++ b/src/typings/require.d.ts @@ -3,6 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +declare const enum LoaderEventType { + LoaderAvailable = 1, + + BeginLoadingScript = 10, + EndLoadingScriptOK = 11, + EndLoadingScriptError = 12, + + BeginInvokeFactory = 21, + EndInvokeFactory = 22, + + NodeBeginEvaluatingScript = 31, + NodeEndEvaluatingScript = 32, + + NodeBeginNativeRequire = 33, + NodeEndNativeRequire = 34 +} + +declare class LoaderEvent { + readonly type: LoaderEventType; + readonly timestamp: number; + readonly detail: string; +} declare var define: { (moduleName: string, dependencies: string[], callback: (...args: any[]) => any): any; @@ -20,4 +42,5 @@ declare var require: { config(data: any): any; onError: Function; __$__nodeRequire<T>(moduleName: string): T; -}; \ No newline at end of file + getStats(): ReadonlyArray<LoaderEvent> +}; diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index b3a6c367c60..5f1e27de082 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -692,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 <https://github.com/RX14>, BobBuehler <https://github.com/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<string>; + + /** + * 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<string>; + + /** + * 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<Winreg.RegistryItem>) => 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<Registry>) => 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/typings/yazl.d.ts b/src/typings/yazl.d.ts new file mode 100644 index 00000000000..0aa227a6c82 --- /dev/null +++ b/src/typings/yazl.d.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'yazl' { + import * as stream from 'stream'; + + class ZipFile { + outputStream: stream.Stream; + addBuffer(buffer: Buffer, path: string); + addFile(localPath: string, path: string); + end(); + } +} \ No newline at end of file diff --git a/src/vs/base/browser/builder.ts b/src/vs/base/browser/builder.ts index 9c285274a99..6abfaece89d 100644 --- a/src/vs/base/browser/builder.ts +++ b/src/vs/base/browser/builder.ts @@ -12,23 +12,9 @@ import * as assert from 'vs/base/common/assert'; import * as DOM from 'vs/base/browser/dom'; /** - * Welcome to the monaco builder. The recommended way to use it is: + * !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! * - * import Builder = require('vs/base/browser/builder'); - * let $ = Builder.$; - * $(....).fn(...); - * - * See below for examples how to invoke the $(): - * - * $() - creates an offdom builder - * $(builder) - wraps the given builder - * $(builder[]) - wraps the given builders into a multibuilder - * $('div') - creates a div - * $('.big') - creates a div with class `big` - * $('#head') - creates a div with id `head` - * $('ul#head') - creates an unordered list with id `head` - * $('<a href="back"></a>') - constructs a builder from the given HTML - * $('a', { href: 'back'}) - constructs a builder, similarly to the Builder#element() call + * @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! */ export interface QuickBuilder { (): Builder; @@ -62,7 +48,9 @@ function hasData(element: any): boolean { } /** - * Wraps around the provided element to manipulate it and add more child elements. + * !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! + * + * @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! */ export class Builder implements IDisposable { private currentElement: HTMLElement; @@ -199,7 +187,7 @@ export class Builder implements IDisposable { assert.ok(child, 'Need a child to append'); if (DOM.isHTMLElement(child)) { - child = withElement(child); + child = _withElement(child); } assert.ok(child instanceof Builder || child instanceof MultiBuilder, 'Need a child to append'); @@ -1018,7 +1006,7 @@ export class Builder implements IDisposable { * Allows to store arbritary data into the current element. */ setProperty(key: string, value: any): Builder { - setPropertyOnElement(this.currentElement, key, value); + _setPropertyOnElement(this.currentElement, key, value); return this; } @@ -1027,7 +1015,7 @@ export class Builder implements IDisposable { * Allows to get arbritary data from the current element. */ getProperty(key: string, fallback?: any): any { - return getPropertyFromElement(this.currentElement, key, fallback); + return _getPropertyFromElement(this.currentElement, key, fallback); } /** @@ -1047,7 +1035,7 @@ export class Builder implements IDisposable { child(index = 0): Builder { let children = this.currentElement.children; - return withElement(<HTMLElement>children.item(index)); + return _withElement(<HTMLElement>children.item(index)); } /** @@ -1191,8 +1179,9 @@ export class Builder implements IDisposable { } /** - * The multi builder provides the same methods as the builder, but allows to call - * them on an array of builders. + * !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! + * + * @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! */ export class MultiBuilder extends Builder { @@ -1215,7 +1204,7 @@ export class MultiBuilder extends Builder { if (types.isArray(builders)) { for (let i = 0; i < builders.length; i++) { if (builders[i] instanceof HTMLElement) { - this.push(withElement(builders[i])); + this.push(_withElement(builders[i])); } else { this.push(builders[i]); } @@ -1302,7 +1291,7 @@ function withBuilder(builder: Builder, offdom?: boolean): Builder { return new Builder(builder.getHTMLElement(), offdom); } -export function withElement(element: HTMLElement, offdom?: boolean): Builder { +export function _withElement(element: HTMLElement, offdom?: boolean): Builder { return new Builder(element, offdom); } @@ -1315,14 +1304,14 @@ function offDOM(): Builder { /** * Allows to store arbritary data into element. */ -export function setPropertyOnElement(element: HTMLElement, key: string, value: any): void { +export function _setPropertyOnElement(element: HTMLElement, key: string, value: any): void { data(element)[key] = value; } /** * Allows to get arbritary data from element. */ -export function getPropertyFromElement(element: HTMLElement, key: string, fallback?: any): any { +export function _getPropertyFromElement(element: HTMLElement, key: string, fallback?: any): any { if (hasData(element)) { let value = data(element)[key]; if (!types.isUndefined(value)) { @@ -1337,12 +1326,17 @@ export function getPropertyFromElement(element: HTMLElement, key: string, fallba * Adds the provided object as property to the given element. Call getBinding() * to retrieve it again. */ -export function bindElement(element: HTMLElement, object: any): void { - setPropertyOnElement(element, DATA_BINDING_ID, object); +export function _bindElement(element: HTMLElement, object: any): void { + _setPropertyOnElement(element, DATA_BINDING_ID, object); } let SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/; +/** + * !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! + * + * @deprecated !!! DO NOT USE. USE vs/base/browser/dom.$ INSTEAD !!! + */ export const $: QuickBuilder = function (arg?: any): Builder { // Off-DOM use @@ -1357,7 +1351,7 @@ export const $: QuickBuilder = function (arg?: any): Builder { // Wrap the given element if (DOM.isHTMLElement(arg) || arg === window) { - return withElement(arg); + return _withElement(arg); } // Wrap the given builders @@ -1386,14 +1380,14 @@ export const $: QuickBuilder = function (arg?: any): Builder { element = container.firstChild; container.removeChild(element); - return withElement(<HTMLElement>element); + return _withElement(<HTMLElement>element); } let builders: Builder[] = []; while (container.firstChild) { element = container.firstChild; container.removeChild(element); - builders.push(withElement(<HTMLElement>element)); + builders.push(_withElement(<HTMLElement>element)); } return new MultiBuilder(builders); diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index c303d45eeb3..5dd631aa97f 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { SubmenuAction } from 'vs/base/browser/ui/menu/menu'; -export interface IEvent { +export interface IContextMenuEvent { shiftKey?: boolean; ctrlKey?: boolean; altKey?: boolean; @@ -27,7 +27,7 @@ export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; }; getActions(): TPromise<(IAction | ContextSubMenu)[]>; getActionItem?(action: IAction): IActionItem; - getActionsContext?(event?: IEvent): any; + getActionsContext?(event?: IContextMenuEvent): any; getKeyBinding?(action: IAction): ResolvedKeybinding; getMenuClassName?(): string; onHide?(didCancel: boolean): void; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index e8ed9bec491..f82eec031b4 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -16,12 +16,18 @@ import { CharCode } from 'vs/base/common/charCode'; import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -export function clearNode(node: HTMLElement) { +export function clearNode(node: HTMLElement): void { while (node.firstChild) { node.removeChild(node.firstChild); } } +export function removeNode(node: HTMLElement): void { + if (node.parentNode) { + node.parentNode.removeChild(node); + } +} + export function isInDOM(node: Node): boolean { while (node) { if (node === document.body) { @@ -486,8 +492,8 @@ export function getClientArea(element: HTMLElement): Dimension { return new Dimension(window.innerWidth, window.innerHeight); } - // Try with document.body.clientWidth / document.body.clientHeigh - if (document.body && document.body.clientWidth && document.body.clientWidth) { + // Try with document.body.clientWidth / document.body.clientHeight + if (document.body && document.body.clientWidth && document.body.clientHeight) { return new Dimension(document.body.clientWidth, document.body.clientHeight); } @@ -920,7 +926,7 @@ class FocusTracker implements IFocusTracker { private disposables: IDisposable[] = []; constructor(element: HTMLElement | Window) { - let hasFocus = false; + let hasFocus = isAncestor(document.activeElement, <HTMLElement>element); let loosingFocus = false; let onFocus = () => { diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index 5620c65ccb6..7dac4446b02 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -5,7 +5,7 @@ 'use strict'; import * as arrays from 'vs/base/common/arrays'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import * as DomUtils from 'vs/base/browser/dom'; import { memoize } from 'vs/base/common/decorators'; @@ -64,7 +64,7 @@ interface TouchEvent extends Event { changedTouches: TouchList; } -export class Gesture implements IDisposable { +export class Gesture extends Disposable { private static readonly SCROLL_FRICTION = -0.005; private static INSTANCE: Gesture; @@ -72,19 +72,19 @@ export class Gesture implements IDisposable { private dispatched: boolean; private targets: HTMLElement[]; - private toDispose: IDisposable[]; private handle: IDisposable; private activeTouches: { [id: number]: TouchData; }; private constructor() { - this.toDispose = []; + super(); + this.activeTouches = {}; this.handle = null; this.targets = []; - this.toDispose.push(DomUtils.addDisposableListener(document, 'touchstart', (e) => this.onTouchStart(e))); - this.toDispose.push(DomUtils.addDisposableListener(document, 'touchend', (e) => this.onTouchEnd(e))); - this.toDispose.push(DomUtils.addDisposableListener(document, 'touchmove', (e) => this.onTouchMove(e))); + this._register(DomUtils.addDisposableListener(document, 'touchstart', (e) => this.onTouchStart(e))); + this._register(DomUtils.addDisposableListener(document, 'touchend', (e) => this.onTouchEnd(e))); + this._register(DomUtils.addDisposableListener(document, 'touchmove', (e) => this.onTouchMove(e))); } public static addTarget(element: HTMLElement): void { @@ -106,9 +106,10 @@ export class Gesture implements IDisposable { public dispose(): void { if (this.handle) { this.handle.dispose(); - dispose(this.toDispose); this.handle = null; } + + super.dispose(); } private onTouchStart(e: TouchEvent): void { diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 72742b71751..8898840ba5e 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -10,7 +10,6 @@ import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import * as lifecycle from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; -import { Builder, $ } from 'vs/base/browser/builder'; import { SelectBox, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, IRunEvent } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; @@ -36,23 +35,23 @@ export interface IBaseActionItemOptions { isMenu?: boolean; } -export class BaseActionItem implements IActionItem { +export class BaseActionItem extends lifecycle.Disposable implements IActionItem { - public builder: Builder; - public _callOnDispose: lifecycle.IDisposable[]; + public element: HTMLElement; public _context: any; public _action: IAction; private _actionRunner: IActionRunner; constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) { - this._callOnDispose = []; + super(); + this._context = context || this; this._action = action; if (action instanceof Action) { - this._callOnDispose.push(action.onDidChange(event => { - if (!this.builder) { + this._register(action.onDidChange(event => { + if (!this.element) { // we have not been rendered yet, so there // is no point in updating the UI return; @@ -81,10 +80,6 @@ export class BaseActionItem implements IActionItem { } } - public get callOnDispose() { - return this._callOnDispose; - } - public set actionRunner(actionRunner: IActionRunner) { this._actionRunner = actionRunner; } @@ -106,7 +101,7 @@ export class BaseActionItem implements IActionItem { } public render(container: HTMLElement): void { - this.builder = $(container); + this.element = container; Gesture.addTarget(container); const enableDragging = this.options && this.options.draggable; @@ -114,20 +109,20 @@ export class BaseActionItem implements IActionItem { container.draggable = true; } - this.builder.on(EventType.Tap, e => this.onClick(e)); + this._register(DOM.addDisposableListener(this.element, EventType.Tap, e => this.onClick(e))); - this.builder.on(DOM.EventType.MOUSE_DOWN, (e) => { + this._register(DOM.addDisposableListener(this.element, DOM.EventType.MOUSE_DOWN, e => { if (!enableDragging) { DOM.EventHelper.stop(e, true); // do not run when dragging is on because that would disable it } const mouseEvent = e as MouseEvent; if (this._action.enabled && mouseEvent.button === 0) { - this.builder.addClass('active'); + DOM.addClass(this.element, 'active'); } - }); + })); - this.builder.on(DOM.EventType.CLICK, (e) => { + this._register(DOM.addDisposableListener(this.element, DOM.EventType.CLICK, e => { DOM.EventHelper.stop(e, true); // See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard // > Writing to the clipboard @@ -142,11 +137,13 @@ export class BaseActionItem implements IActionItem { } else { platform.setImmediate(() => this.onClick(e)); } - }); + })); - this.builder.on([DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT], (e) => { - DOM.EventHelper.stop(e); - this.builder.removeClass('active'); + [DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT].forEach(event => { + this._register(DOM.addDisposableListener(this.element, event, e => { + DOM.EventHelper.stop(e); + DOM.removeClass(this.element, 'active'); + })); }); } @@ -154,27 +151,30 @@ export class BaseActionItem implements IActionItem { DOM.EventHelper.stop(event, true); let context: any; - if (types.isUndefinedOrNull(this._context) || !types.isObject(this._context)) { + if (types.isUndefinedOrNull(this._context)) { context = event; } else { context = this._context; - context.event = event; + + if (types.isObject(context)) { + context.event = event; + } } this._actionRunner.run(this._action, context); } public focus(): void { - if (this.builder) { - this.builder.domFocus(); - this.builder.addClass('focused'); + if (this.element) { + this.element.focus(); + DOM.addClass(this.element, 'focused'); } } public blur(): void { - if (this.builder) { - this.builder.domBlur(); - this.builder.removeClass('focused'); + if (this.element) { + this.element.blur(); + DOM.removeClass(this.element, 'focused'); } } @@ -199,12 +199,12 @@ export class BaseActionItem implements IActionItem { } public dispose(): void { - if (this.builder) { - this.builder.destroy(); - this.builder = null; + if (this.element) { + DOM.removeNode(this.element); + this.element = null; } - this._callOnDispose = lifecycle.dispose(this._callOnDispose); + super.dispose(); } } @@ -229,8 +229,9 @@ export interface IActionItemOptions extends IBaseActionItemOptions { export class ActionItem extends BaseActionItem { - protected $e: Builder; + protected label: HTMLElement; protected options: IActionItemOptions; + private cssClass: string; constructor(context: any, action: IAction, options: IActionItemOptions = {}) { @@ -245,20 +246,20 @@ export class ActionItem extends BaseActionItem { public render(container: HTMLElement): void { super.render(container); - this.$e = $('a.action-label').appendTo(this.builder); + this.label = DOM.append(this.element, DOM.$('a.action-label')); if (this._action.id === Separator.ID) { // A separator is a presentation item - this.$e.attr({ role: 'presentation' }); + this.label.setAttribute('role', 'presentation'); } else { if (this.options.isMenu) { - this.$e.attr({ role: 'menuitem' }); + this.label.setAttribute('role', 'menuitem'); } else { - this.$e.attr({ role: 'button' }); + this.label.setAttribute('role', 'button'); } } if (this.options.label && this.options.keybinding) { - $('span.keybinding').text(this.options.keybinding).appendTo(this.builder); + DOM.append(this.element, DOM.$('span.keybinding')).textContent = this.options.keybinding; } this._updateClass(); @@ -270,12 +271,12 @@ export class ActionItem extends BaseActionItem { public focus(): void { super.focus(); - this.$e.domFocus(); + this.label.focus(); } public _updateLabel(): void { if (this.options.label) { - this.$e.text(this.getAction().label); + this.label.textContent = this.getAction().label; } } @@ -294,43 +295,43 @@ export class ActionItem extends BaseActionItem { } if (title) { - this.$e.attr({ title: title }); + this.label.title = title; } } public _updateClass(): void { if (this.cssClass) { - this.$e.removeClass(this.cssClass); + DOM.removeClasses(this.label, this.cssClass); } if (this.options.icon) { this.cssClass = this.getAction().class; - this.$e.addClass('icon'); + DOM.addClass(this.label, 'icon'); if (this.cssClass) { - this.$e.addClass(this.cssClass); + DOM.addClasses(this.label, this.cssClass); } this._updateEnabled(); } else { - this.$e.removeClass('icon'); + DOM.removeClass(this.label, 'icon'); } } public _updateEnabled(): void { if (this.getAction().enabled) { - this.builder.removeClass('disabled'); - this.$e.removeClass('disabled'); - this.$e.attr({ tabindex: 0 }); + DOM.removeClass(this.element, 'disabled'); + DOM.removeClass(this.label, 'disabled'); + this.label.tabIndex = 0; } else { - this.builder.addClass('disabled'); - this.$e.addClass('disabled'); - DOM.removeTabIndexAndUpdateFocus(this.$e.getHTMLElement()); + DOM.addClass(this.element, 'disabled'); + DOM.addClass(this.label, 'disabled'); + DOM.removeTabIndexAndUpdateFocus(this.label); } } public _updateChecked(): void { if (this.getAction().checked) { - this.$e.addClass('checked'); + DOM.addClass(this.label, 'checked'); } else { - this.$e.removeClass('checked'); + DOM.removeClass(this.label, 'checked'); } } } @@ -353,7 +354,6 @@ export interface IActionBarOptions { actionRunner?: IActionRunner; ariaLabel?: string; animated?: boolean; - isMenu?: boolean; } let defaultOptions: IActionBarOptions = { @@ -365,7 +365,7 @@ export interface IActionOptions extends IActionItemOptions { index?: number; } -export class ActionBar implements IActionRunner { +export class ActionBar extends lifecycle.Disposable implements IActionRunner { public options: IActionBarOptions; @@ -374,14 +374,12 @@ export class ActionBar implements IActionRunner { // Items public items: IActionItem[]; - private focusedItem: number; + protected focusedItem: number; private focusTracker: DOM.IFocusTracker; // Elements public domNode: HTMLElement; - private actionsList: HTMLElement; - - private toDispose: lifecycle.IDisposable[]; + protected actionsList: HTMLElement; private _onDidBlur = new Emitter<void>(); private _onDidCancel = new Emitter<void>(); @@ -389,18 +387,19 @@ export class ActionBar implements IActionRunner { private _onDidBeforeRun = new Emitter<IRunEvent>(); constructor(container: HTMLElement, options: IActionBarOptions = defaultOptions) { + super(); + this.options = options; this._context = options.context; - this.toDispose = []; this._actionRunner = this.options.actionRunner; if (!this._actionRunner) { this._actionRunner = new ActionRunner(); - this.toDispose.push(this._actionRunner); + this._register(this._actionRunner); } - this.toDispose.push(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); - this.toDispose.push(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); + this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); + this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); this.items = []; this.focusedItem = undefined; @@ -437,7 +436,7 @@ export class ActionBar implements IActionRunner { break; } - $(this.domNode).on(DOM.EventType.KEY_DOWN, (e) => { + this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => { let event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; @@ -457,9 +456,9 @@ export class ActionBar implements IActionRunner { event.preventDefault(); event.stopPropagation(); } - }); + })); - $(this.domNode).on(DOM.EventType.KEY_UP, (e) => { + this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_UP, e => { let event = new StandardKeyboardEvent(e as KeyboardEvent); // Run action on Enter/Space @@ -473,51 +472,26 @@ export class ActionBar implements IActionRunner { else if (event.equals(KeyCode.Tab) || event.equals(KeyMod.Shift | KeyCode.Tab)) { this.updateFocusedItem(); } - }); + })); - this.focusTracker = DOM.trackFocus(this.domNode); - this.toDispose.push(this.focusTracker.onDidBlur(() => { + this.focusTracker = this._register(DOM.trackFocus(this.domNode)); + this._register(this.focusTracker.onDidBlur(() => { if (document.activeElement === this.domNode || !DOM.isAncestor(document.activeElement, this.domNode)) { this._onDidBlur.fire(); this.focusedItem = undefined; } })); - this.toDispose.push(this.focusTracker.onDidFocus(() => this.updateFocusedItem())); + this._register(this.focusTracker.onDidFocus(() => this.updateFocusedItem())); this.actionsList = document.createElement('ul'); this.actionsList.className = 'actions-container'; - if (this.options.isMenu) { - this.actionsList.setAttribute('role', 'menu'); - } else { - this.actionsList.setAttribute('role', 'toolbar'); - } + this.actionsList.setAttribute('role', 'toolbar'); + if (this.options.ariaLabel) { this.actionsList.setAttribute('aria-label', this.options.ariaLabel); } - if (this.options.isMenu) { - $(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') && !DOM.hasClass(target, 'disabled')) { - const lastFocusedItem = this.focusedItem; - this.setFocusedItem(target); - - if (lastFocusedItem !== this.focusedItem) { - this.updateFocus(); - } - } - }); - } - this.domNode.appendChild(this.actionsList); container.appendChild(this.domNode); @@ -547,16 +521,6 @@ 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]; @@ -603,10 +567,10 @@ export class ActionBar implements IActionRunner { actionItemElement.setAttribute('role', 'presentation'); // Prevent native context menu on actions - $(actionItemElement).on(DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { + this._register(DOM.addDisposableListener(actionItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { e.preventDefault(); e.stopPropagation(); - }); + })); let item: IActionItem = null; @@ -659,7 +623,7 @@ export class ActionBar implements IActionRunner { public clear(): void { this.items = lifecycle.dispose(this.items); - $(this.actionsList).empty(); + DOM.clearNode(this.actionsList); } public length(): number { @@ -725,10 +689,9 @@ export class ActionBar implements IActionRunner { this.updateFocus(true); } - private updateFocus(fromRight?: boolean): void { + protected updateFocus(fromRight?: boolean): void { if (typeof this.focusedItem === 'undefined') { - this.domNode.focus(); - return; + this.actionsList.focus(); } for (let i = 0; i < this.items.length; i++) { @@ -737,8 +700,12 @@ export class ActionBar implements IActionRunner { let actionItem = <any>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.actionsList.focus(); + } } } else { if (types.isFunction(actionItem.blur)) { @@ -779,28 +746,22 @@ export class ActionBar implements IActionRunner { } this.items = null; - if (this.focusTracker) { - this.focusTracker.dispose(); - this.focusTracker = null; - } + DOM.removeNode(this.getContainer()); - this.toDispose = lifecycle.dispose(this.toDispose); - - $(this.getContainer()).destroy(); + super.dispose(); } } export class SelectActionItem extends BaseActionItem { protected selectBox: SelectBox; - protected toDispose: lifecycle.IDisposable[]; constructor(ctx: any, action: IAction, options: string[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions ) { super(ctx, action); + this.selectBox = new SelectBox(options, selected, contextViewProvider, null, selectBoxOptions); - this.toDispose = []; - this.toDispose.push(this.selectBox); + this._register(this.selectBox); this.registerListeners(); } @@ -813,7 +774,7 @@ export class SelectActionItem extends BaseActionItem { } private registerListeners(): void { - this.toDispose.push(this.selectBox.onDidSelect(e => { + this._register(this.selectBox.onDidSelect(e => { this.actionRunner.run(this._action, this.getActionContext(e.selected)).done(); })); } @@ -837,10 +798,4 @@ export class SelectActionItem extends BaseActionItem { public render(container: HTMLElement): void { this.selectBox.render(container); } - - public dispose(): void { - this.toDispose = lifecycle.dispose(this.toDispose); - - super.dispose(); - } } \ No newline at end of file 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..afade1a3413 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -20,20 +20,23 @@ cursor: pointer; align-self: center; height: 100%; + outline: none; } -.monaco-breadcrumbs .monaco-breadcrumb-item:not(:first-child)::before { - background-image: url(./collapsed.svg); - opacity: .7; - width: 16px; +.monaco-breadcrumbs .monaco-breadcrumb-item::before { + width: 14px; height: 16px; display: inline-block; - background-size: 16px; - background-position: 50% 50%; content: ' '; } -.vs-dark .monaco-breadcrumbs .monaco-breadcrumb-item:not(:first-child)::before { - background-image: url(./collpased-dark.svg); +.monaco-breadcrumbs .monaco-breadcrumb-item:not(:nth-child(2))::before { + background-image: url(./collapsed.svg); + opacity: .7; + background-size: 16px; + background-position: 50% 50%; } +.vs-dark .monaco-breadcrumbs .monaco-breadcrumb-item:not(:nth-child(2))::before { + background-image: url(./collpased-dark.svg); +} diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 0966ceca010..6028ef92c35 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 { } @@ -45,10 +45,8 @@ export class SimpleBreadcrumbsItem extends BreadcrumbsItem { export interface IBreadcrumbsWidgetStyles { breadcrumbsBackground?: Color; breadcrumbsForeground?: Color; - breadcrumbsHoverBackground?: Color; breadcrumbsHoverForeground?: Color; breadcrumbsFocusForeground?: Color; - breadcrumbsFocusAndSelectionBackground?: Color; breadcrumbsFocusAndSelectionForeground?: Color; } @@ -86,12 +84,14 @@ 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, horizontalScrollbarSize: 3, - useShadows: false + useShadows: false, + scrollYToX: true }); this._disposables.push(this._scrollable); this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e))); @@ -134,15 +134,9 @@ export class BreadcrumbsWidget { if (style.breadcrumbsFocusForeground) { content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsFocusForeground}}\n`; } - if (style.breadcrumbsFocusAndSelectionBackground) { - content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused.selected { background-color: ${style.breadcrumbsFocusAndSelectionBackground}}\n`; - } if (style.breadcrumbsFocusAndSelectionForeground) { content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused.selected { color: ${style.breadcrumbsFocusAndSelectionForeground}}\n`; } - if (style.breadcrumbsHoverBackground) { - content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { background-color: ${style.breadcrumbsHoverBackground}}\n`; - } if (style.breadcrumbsHoverForeground) { content += `.monaco-breadcrumbs .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { color: ${style.breadcrumbsHoverForeground}}\n`; } @@ -152,13 +146,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 +194,7 @@ export class BreadcrumbsWidget { } else { this._focusedItemIdx = i; dom.addClass(node, 'focused'); + node.focus(); } } this._reveal(this._focusedItemIdx); @@ -239,11 +244,20 @@ export class BreadcrumbsWidget { } 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)); - this._render(prefix); - dispose(removed); - this._focus(-1, undefined); + let prefix: number; + let removed: BreadcrumbsItem[]; + try { + prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b)); + removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix)); + this._render(prefix); + dispose(removed); + this._focus(-1, undefined); + } catch (e) { + let newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`); + newError.name = e.name; + newError.stack = e.stack; + throw newError; + } } private _render(start: number): void { @@ -253,11 +267,11 @@ export class BreadcrumbsWidget { this._renderItem(item, node); } // case a: more nodes -> remove them - for (; start < this._nodes.length; start++) { - this._nodes[start].remove(); - this._freeNodes.push(this._nodes[start]); + while (start < this._nodes.length) { + const free = this._nodes.pop(); + this._freeNodes.push(free); + free.remove(); } - this._nodes.length = this._items.length; // case b: more items -> render them for (; start < this._items.length; start++) { @@ -265,7 +279,7 @@ export class BreadcrumbsWidget { let node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div'); this._renderItem(item, node); this._domNode.appendChild(node); - this._nodes[start] = node; + this._nodes.push(node); } this.layout(undefined); } @@ -274,7 +288,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/button/button.ts b/src/vs/base/browser/ui/button/button.ts index dec147c543d..5bee0452536 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -7,14 +7,13 @@ import 'vs/css!./button'; import * as DOM from 'vs/base/browser/dom'; -import { Builder, $ } from 'vs/base/browser/builder'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Gesture, EventType } from 'vs/base/browser/touch'; +import { Gesture } from 'vs/base/browser/touch'; export interface IButtonOptions extends IButtonStyles { title?: boolean; @@ -35,7 +34,7 @@ const defaultOptions: IButtonStyles = { export class Button extends Disposable { - private $el: Builder; + private _element: HTMLElement; private options: IButtonOptions; private buttonBackground: Color; @@ -59,50 +58,52 @@ export class Button extends Disposable { this.buttonForeground = this.options.buttonForeground; this.buttonBorder = this.options.buttonBorder; - this.$el = this._register($('a.monaco-button').attr({ - 'tabIndex': '0', - 'role': 'button' - }).appendTo(container)); + this._element = document.createElement('a'); + DOM.addClass(this._element, 'monaco-button'); + this._element.tabIndex = 0; + this._element.setAttribute('role', 'button'); - Gesture.addTarget(this.$el.getHTMLElement()); + container.appendChild(this._element); - this.$el.on([DOM.EventType.CLICK, EventType.Tap], e => { + Gesture.addTarget(this._element); + + this._register(DOM.addDisposableListener(this._element, DOM.EventType.CLICK, e => { if (!this.enabled) { DOM.EventHelper.stop(e); return; } this._onDidClick.fire(e); - }); + })); - this.$el.on(DOM.EventType.KEY_DOWN, e => { + this._register(DOM.addDisposableListener(this._element, DOM.EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = false; if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { this._onDidClick.fire(e); eventHandled = true; } else if (event.equals(KeyCode.Escape)) { - this.$el.domBlur(); + this._element.blur(); eventHandled = true; } if (eventHandled) { DOM.EventHelper.stop(event, true); } - }); + })); - this.$el.on(DOM.EventType.MOUSE_OVER, e => { - if (!this.$el.hasClass('disabled')) { + this._register(DOM.addDisposableListener(this._element, DOM.EventType.MOUSE_OVER, e => { + if (!DOM.hasClass(this._element, 'disabled')) { this.setHoverBackground(); } - }); + })); - this.$el.on(DOM.EventType.MOUSE_OUT, e => { + this._register(DOM.addDisposableListener(this._element, DOM.EventType.MOUSE_OUT, e => { this.applyStyles(); // restore standard styles - }); + })); // Also set hover background when button is focused for feedback - this.focusTracker = this._register(DOM.trackFocus(this.$el.getHTMLElement())); + this.focusTracker = this._register(DOM.trackFocus(this._element)); this._register(this.focusTracker.onDidFocus(() => this.setHoverBackground())); this._register(this.focusTracker.onDidBlur(() => this.applyStyles())); // restore standard styles @@ -112,7 +113,7 @@ export class Button extends Disposable { private setHoverBackground(): void { const hoverBackground = this.buttonHoverBackground ? this.buttonHoverBackground.toString() : null; if (hoverBackground) { - this.$el.style('background-color', hoverBackground); + this._element.style.backgroundColor = hoverBackground; } } @@ -126,58 +127,56 @@ export class Button extends Disposable { } private applyStyles(): void { - if (this.$el) { + if (this._element) { const background = this.buttonBackground ? this.buttonBackground.toString() : null; const foreground = this.buttonForeground ? this.buttonForeground.toString() : null; const border = this.buttonBorder ? this.buttonBorder.toString() : null; - this.$el.style('color', foreground); - this.$el.style('background-color', background); + this._element.style.color = foreground; + this._element.style.backgroundColor = background; - this.$el.style('border-width', border ? '1px' : null); - this.$el.style('border-style', border ? 'solid' : null); - this.$el.style('border-color', border); + this._element.style.borderWidth = border ? '1px' : null; + this._element.style.borderStyle = border ? 'solid' : null; + this._element.style.borderColor = border; } } get element(): HTMLElement { - return this.$el.getHTMLElement(); + return this._element; } set label(value: string) { - if (!this.$el.hasClass('monaco-text-button')) { - this.$el.addClass('monaco-text-button'); + if (!DOM.hasClass(this._element, 'monaco-text-button')) { + DOM.addClass(this._element, 'monaco-text-button'); } - this.$el.text(value); + this._element.textContent = value; if (this.options.title) { - this.$el.title(value); + this._element.title = value; } } set icon(iconClassName: string) { - this.$el.addClass(iconClassName); + DOM.addClass(this._element, iconClassName); } set enabled(value: boolean) { if (value) { - this.$el.removeClass('disabled'); - this.$el.attr({ - 'aria-disabled': 'false', - 'tabIndex': '0' - }); + DOM.removeClass(this._element, 'disabled'); + this._element.setAttribute('aria-disabled', String(false)); + this._element.tabIndex = 0; } else { - this.$el.addClass('disabled'); - this.$el.attr('aria-disabled', String(true)); - DOM.removeTabIndexAndUpdateFocus(this.$el.getHTMLElement()); + DOM.addClass(this._element, 'disabled'); + this._element.setAttribute('aria-disabled', String(true)); + DOM.removeTabIndexAndUpdateFocus(this._element); } } get enabled() { - return !this.$el.hasClass('disabled'); + return !DOM.hasClass(this._element, 'disabled'); } focus(): void { - this.$el.domFocus(); + this._element.focus(); } } @@ -201,7 +200,7 @@ export class ButtonGroup extends Disposable { // Implement keyboard access in buttons if there are multiple if (count > 1) { - $(button.element).on(DOM.EventType.KEY_DOWN, e => { + this._register(DOM.addDisposableListener(button.element, DOM.EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; @@ -219,7 +218,8 @@ export class ButtonGroup extends Disposable { this._buttons[buttonIndexToFocus].focus(); DOM.EventHelper.stop(e, true); } - }, this.toDispose); + + })); } } } diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 8a14b87c36c..1b6b190e533 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -6,9 +6,8 @@ 'use strict'; import 'vs/css!./contextview'; -import { Builder, $ } from 'vs/base/browser/builder'; import * as DOM from 'vs/base/browser/dom'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; export interface IAnchor { x: number; @@ -54,91 +53,96 @@ export interface ISize { export interface IView extends IPosition, ISize { } -function layout(view: ISize, around: IView, viewport: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment): IPosition { - - let chooseBiased = (a: number, aIsGood: boolean, b: number, bIsGood: boolean) => { - if (aIsGood) { - return a; - } - if (bIsGood) { - return b; - } - 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); - } - }; - - let top = (() => { - // Compute both options (putting the segment above and below) - let posAbove = around.top - view.height; - let posBelow = around.top + around.height; - - // 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 }; +export enum LayoutAnchorPosition { + Before, + After } -export class ContextView { +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 (viewSize <= anchor.offset) { + return anchor.offset - viewSize; // ok case, lay it out before the anchor + } + + 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 + } + + if (viewSize <= viewportSize - anchorEnd) { + return anchorEnd; // ok case, lay it out after the anchor + } + + return 0; // sad case, lay it over the anchor + } +} + +export class ContextView extends Disposable { private static readonly BUBBLE_UP_EVENTS = ['click', 'keydown', 'focus', 'blur']; private static readonly BUBBLE_DOWN_EVENTS = ['click']; - private $container: Builder; - private $view: Builder; + private container: HTMLElement; + private view: HTMLElement; private delegate: IDelegate; - private toDispose: IDisposable[]; private toDisposeOnClean: IDisposable; + private toDisposeOnSetContainer: IDisposable; constructor(container: HTMLElement) { - this.$view = $('.context-view').hide(); + super(); + + this.view = DOM.$('.context-view'); + + DOM.hide(this.view); + this.setContainer(container); - this.toDispose = [toDisposable(() => { - this.setContainer(null); - })]; - - this.toDisposeOnClean = null; + this._register(toDisposable(() => this.setContainer(null))); } public setContainer(container: HTMLElement): void { - if (this.$container) { - this.$container.getHTMLElement().removeChild(this.$view.getHTMLElement()); - this.$container.off(ContextView.BUBBLE_UP_EVENTS); - this.$container.off(ContextView.BUBBLE_DOWN_EVENTS, true); - this.$container = null; + if (this.container) { + this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer); + this.container.removeChild(this.view); + this.container = null; } if (container) { - this.$container = $(container); - this.$view.appendTo(this.$container); - this.$container.on(ContextView.BUBBLE_UP_EVENTS, (e: Event) => { - this.onDOMEvent(e, <HTMLElement>document.activeElement, false); + this.container = container; + this.container.appendChild(this.view); + + const toDisposeOnSetContainer: IDisposable[] = []; + + ContextView.BUBBLE_UP_EVENTS.forEach(event => { + toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container, event, (e: Event) => { + this.onDOMEvent(e, <HTMLElement>document.activeElement, false); + })); }); - this.$container.on(ContextView.BUBBLE_DOWN_EVENTS, (e: Event) => { - this.onDOMEvent(e, <HTMLElement>document.activeElement, true); - }, null, true); + + ContextView.BUBBLE_DOWN_EVENTS.forEach(event => { + toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container, event, (e: Event) => { + this.onDOMEvent(e, <HTMLElement>document.activeElement, true); + }, true)); + }); + + this.toDisposeOnSetContainer = combinedDisposable(toDisposeOnSetContainer); } } @@ -148,10 +152,14 @@ export class ContextView { } // Show static box - this.$view.setClass('context-view').empty().style({ top: '0px', left: '0px' }).show(); + DOM.clearNode(this.view); + this.view.className = 'context-view'; + this.view.style.top = '0px'; + this.view.style.left = '0px'; + DOM.show(this.view); // Render content - this.toDisposeOnClean = delegate.render(this.$view.getHTMLElement()); + this.toDisposeOnClean = delegate.render(this.view); // Set active delegate this.delegate = delegate; @@ -178,6 +186,11 @@ export class ContextView { } private doLayout(): void { + // Check that we still have a delegate - this.delegate.layout may have hidden + if (!this.isVisible()) { + return; + } + // Get anchor let anchor = this.delegate.getAnchor(); @@ -205,30 +218,33 @@ export class ContextView { }; } - let viewport = { - top: DOM.StandardWindow.scrollY, - left: DOM.StandardWindow.scrollX, - height: window.innerHeight, - width: window.innerWidth - }; + const viewSizeWidth = DOM.getTotalWidth(this.view); + const viewSizeHeight = DOM.getTotalHeight(this.view); - // Get the view's size - let viewSize = this.$view.getTotalSize(); - let view = { width: viewSize.width, height: viewSize.height }; + const anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; + const anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; - let anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; - let anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; + const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; - let result = layout(view, around, viewport, anchorPosition, anchorAlignment); + let horizontalAnchor: ILayoutAnchor; - let containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement()); - result.top -= containerPosition.top; - result.left -= containerPosition.left; + 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 }; + } - 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' }); + const containerPosition = DOM.getDomNodePagePosition(this.container); + const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) - containerPosition.top; + const left = layout(window.innerWidth, viewSizeWidth, horizontalAnchor) - containerPosition.left; + + DOM.removeClasses(this.view, 'top', 'bottom', 'left', 'right'); + DOM.addClass(this.view, anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); + DOM.addClass(this.view, anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right'); + + this.view.style.top = `${top}px`; + this.view.style.left = `${left}px`; + this.view.style.width = 'initial'; } public hide(data?: any): void { @@ -243,7 +259,7 @@ export class ContextView { this.toDisposeOnClean = null; } - this.$view.hide(); + DOM.hide(this.view); } private isVisible(): boolean { @@ -254,7 +270,7 @@ export class ContextView { if (this.delegate) { if (this.delegate.onDOMEvent) { this.delegate.onDOMEvent(e, <HTMLElement>document.activeElement); - } else if (onCapture && !DOM.isAncestor(<HTMLElement>e.target, this.$container.getHTMLElement())) { + } else if (onCapture && !DOM.isAncestor(<HTMLElement>e.target, this.container)) { this.hide(); } } @@ -263,6 +279,6 @@ export class ContextView { public dispose(): void { this.hide(); - this.toDispose = dispose(this.toDispose); + super.dispose(); } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index f2b9a1f842e..3035fa9f768 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -6,7 +6,6 @@ 'use strict'; import 'vs/css!./dropdown'; -import { Builder, $ } from 'vs/base/browser/builder'; import { TPromise } from 'vs/base/common/winjs.base'; import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch'; import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; @@ -15,7 +14,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IContextViewProvider, IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IMenuOptions } from 'vs/base/browser/ui/menu/menu'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { EventHelper, EventType, removeClass, addClass } from 'vs/base/browser/dom'; +import { EventHelper, EventType, removeClass, addClass, append, $, removeNode, addDisposableListener, addClasses } from 'vs/base/browser/dom'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; export interface ILabelRenderer { @@ -30,47 +29,52 @@ export interface IBaseDropdownOptions { export class BaseDropdown extends ActionRunner { private _toDispose: IDisposable[] = []; - private $el: Builder; - private $boxContainer: Builder; - private $label: Builder; - private $contents: Builder; + private _element: HTMLElement; + private boxContainer: HTMLElement; + private _label: HTMLElement; + private contents: HTMLElement; private visible: boolean; constructor(container: HTMLElement, options: IBaseDropdownOptions) { super(); - this.$el = $('.monaco-dropdown').appendTo(container); + this._element = append(container, $('.monaco-dropdown')); - this.$label = $('.dropdown-label'); + this._label = append(this._element, $('.dropdown-label')); let labelRenderer = options.labelRenderer; if (!labelRenderer) { labelRenderer = (container: HTMLElement): IDisposable => { - $(container).text(options.label || ''); + container.textContent = options.label || ''; + return null; }; } - this.$label.on([EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => { - EventHelper.stop(e, true); // prevent default click behaviour to trigger - }).on([EventType.MOUSE_DOWN, GestureEventType.Tap], (e: Event) => { - if (e instanceof MouseEvent && e.detail > 1) { - return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363) - } + [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + this._toDispose.push(addDisposableListener(this._label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger + }); - if (this.visible) { - this.hide(); - } else { - this.show(); - } - }).appendTo(this.$el); + [EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + this._toDispose.push(addDisposableListener(this._label, event, e => { + if (e instanceof MouseEvent && e.detail > 1) { + return; // prevent multiple clicks to open multiple context menus (https://github.com/Microsoft/vscode/issues/41363) + } - const cleanupFn = labelRenderer(this.$label.getHTMLElement()); + if (this.visible) { + this.hide(); + } else { + this.show(); + } + })); + }); + + const cleanupFn = labelRenderer(this._label); if (cleanupFn) { this._toDispose.push(cleanupFn); } - Gesture.addTarget(this.$label.getHTMLElement()); + Gesture.addTarget(this._label); } get toDispose(): IDisposable[] { @@ -78,15 +82,15 @@ export class BaseDropdown extends ActionRunner { } get element(): HTMLElement { - return this.$el.getHTMLElement(); + return this._element; } get label(): HTMLElement { - return this.$label.getHTMLElement(); + return this._label; } set tooltip(tooltip: string) { - this.$label.title(tooltip); + this._label.title = tooltip; } show(): void { @@ -107,19 +111,19 @@ export class BaseDropdown extends ActionRunner { this._toDispose = dispose(this.toDispose); - if (this.$boxContainer) { - this.$boxContainer.destroy(); - this.$boxContainer = null; + if (this.boxContainer) { + removeNode(this.boxContainer); + this.boxContainer = null; } - if (this.$contents) { - this.$contents.destroy(); - this.$contents = null; + if (this.contents) { + removeNode(this.contents); + this.contents = null; } - if (this.$label) { - this.$label.destroy(); - this.$label = null; + if (this._label) { + removeNode(this._label); + this._label = null; } } } @@ -279,15 +283,13 @@ export class DropdownMenuActionItem extends BaseActionItem { render(container: HTMLElement): void { const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => { - this.builder = $('a.action-label').attr({ - tabIndex: '0', - role: 'button', - 'aria-haspopup': 'true', - title: this._action.label || '', - class: this.clazz - }); + this.element = append(el, $('a.action-label')); + addClasses(this.element, this.clazz); - this.builder.appendTo(el); + this.element.tabIndex = 0; + this.element.setAttribute('role', 'button'); + this.element.setAttribute('aria-haspopup', 'true'); + this.element.title = this._action.label || ''; return null; }; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index cb972251f77..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 { @@ -167,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..785d2a0e44e 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -40,7 +40,7 @@ } .monaco-icon-label > .monaco-icon-label-description-container > .label-description { - opacity: 0.7; + opacity: .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/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 10094f4e5a6..73f8ec7f4ca 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -10,6 +10,7 @@ import { IVirtualDelegate, IRenderer, IListEvent, IListOpenEvent } from './list' import { List, IListStyles, IListOptions } from './listWidget'; import { IPagedModel } from 'vs/base/common/paging'; import { Event, mapEvent } from 'vs/base/common/event'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export interface IPagedRenderer<TElement, TTemplateData> extends IRenderer<TElement, TTemplateData> { renderPlaceholder(index: number, templateData: TTemplateData): void; @@ -43,11 +44,12 @@ class PagedRenderer<TElement, TTemplateData> implements IRenderer<number, ITempl return this.renderer.renderElement(model.get(index), index, data.data); } - const promise = model.resolve(index); - data.disposable = { dispose: () => promise.cancel() }; + const cts = new CancellationTokenSource(); + const promise = model.resolve(index, cts.token); + data.disposable = { dispose: () => cts.cancel() }; this.renderer.renderPlaceholder(index, data.data); - promise.done(entry => this.renderer.renderElement(entry, index, data.data)); + promise.then(entry => this.renderer.renderElement(entry, index, data.data)); } disposeElement(): void { diff --git a/src/vs/base/browser/ui/menu/check.svg b/src/vs/base/browser/ui/menu/check.svg new file mode 100644 index 00000000000..3f365c4800e --- /dev/null +++ b/src/vs/base/browser/ui/menu/check.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-2 -2 16 16" enable-background="new -2 -2 16 16"><polygon fill="#424242" points="9,0 4.5,9 3,6 0,6 3,12 6,12 12,0"/></svg> \ No newline at end of file diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index 76f2599c526..bea8dd19777 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -40,7 +40,7 @@ flex: 1 1 auto; display: -ms-flexbox; display: flex; - height: 2.6em; + height: 2em; align-items: center; } @@ -50,7 +50,7 @@ text-decoration: none; padding: 0 1em; background: none; - font-size: inherit; + font-size: 12px; line-height: 1; } @@ -61,7 +61,7 @@ flex: 2 1 auto; padding: 0 1em; text-align: right; - font-size: inherit; + font-size: 12px; line-height: 1; } @@ -97,8 +97,18 @@ color: inherit; } -.monaco-menu .monaco-action-bar.vertical .action-label.checked:after { - content: ' \2713'; +.monaco-menu .monaco-action-bar.vertical .menu-item-check { + position: absolute; + visibility: hidden; + background-color: #646465; + -webkit-mask: url('check.svg') no-repeat 50% 56%/15px 15px; + mask: url('check.svg') no-repeat 50% 56%/15px 15px; + width: 1em; + height: 100%; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check { + visibility: visible; } /* Context Menu */ @@ -117,7 +127,9 @@ 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; } @@ -147,4 +159,8 @@ .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 .menu-item-check { + background-color: white; } \ 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 a2cb23d310e..4be890a1a1a 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -7,15 +7,18 @@ import 'vs/css!./menu'; import * as nls from 'vs/nls'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import * as strings from 'vs/base/common/strings'; 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 { ResolvedKeybinding, KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; +import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass } 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'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export const MENU_MNEMONIC_REGEX: RegExp = /\(&{1,2}(\w)\)|&{1,2}(\w)/; +export const MENU_ESCAPED_MNEMONIC_REGEX: RegExp = /(?:&){1,2}(\w)/; export interface IMenuOptions { context?: any; @@ -23,6 +26,7 @@ export interface IMenuOptions { actionRunner?: IActionRunner; getKeyBinding?: (action: IAction) => ResolvedKeybinding; ariaLabel?: string; + enableMnemonics?: boolean; } @@ -37,43 +41,139 @@ interface ISubMenuData { submenu?: Menu; } -export class Menu { - - private actionBar: ActionBar; - private listener: IDisposable; +export class Menu extends ActionBar { + private mnemonics: Map<KeyCode, Array<MenuActionItem>>; + private menuDisposables: IDisposable[]; constructor(container: HTMLElement, actions: IAction[], options: IMenuOptions = {}) { + addClass(container, 'monaco-menu-container'); container.setAttribute('role', 'presentation'); - let menuContainer = document.createElement('div'); addClass(menuContainer, 'monaco-menu'); menuContainer.setAttribute('role', 'presentation'); container.appendChild(menuContainer); - let parentData: ISubMenuData = { - parent: this - }; - - this.actionBar = new ActionBar(menuContainer, { + super(menuContainer, { orientation: ActionsOrientation.VERTICAL, actionItemProvider: action => this.doGetActionItem(action, options, parentData), context: options.context, actionRunner: options.actionRunner, - isMenu: true, ariaLabel: options.ariaLabel }); - this.actionBar.push(actions, { icon: true, label: true, isMenu: true }); + this.actionsList.setAttribute('role', 'menu'); + + this.actionsList.tabIndex = 0; + + this.menuDisposables = []; + + if (options.enableMnemonics) { + this.menuDisposables.push(addDisposableListener(menuContainer, EventType.KEY_DOWN, (e) => { + const key = KeyCodeUtils.fromString(e.key); + if (this.mnemonics.has(key)) { + EventHelper.stop(e, true); + const actions = this.mnemonics.get(key); + + if (actions.length === 1) { + if (actions[0] instanceof SubmenuActionItem) { + this.focusItemByElement(actions[0].container); + } + + actions[0].onClick(event); + } + + if (actions.length > 1) { + const action = actions.shift(); + this.focusItemByElement(action.container); + + actions.push(action); + this.mnemonics.set(key, actions); + } + } + })); + } + + $(this.domNode).on(EventType.MOUSE_OUT, (e) => { + let relatedTarget = (e as MouseEvent).relatedTarget as HTMLElement; + if (!isAncestor(relatedTarget, this.domNode)) { + this.focusedItem = undefined; + this.updateFocus(); + e.stopPropagation(); + } + }); + + $(this.actionsList).on(EventType.MOUSE_OVER, (e) => { + let target = e.target as HTMLElement; + if (!target || !isAncestor(target, this.actionsList) || target === this.actionsList) { + return; + } + + while (target.parentElement !== this.actionsList) { + target = target.parentElement; + } + + if (hasClass(target, 'action-item')) { + const lastFocusedItem = this.focusedItem; + this.setFocusedItem(target); + + if (lastFocusedItem !== this.focusedItem) { + this.updateFocus(); + } + } + }); + + let parentData: ISubMenuData = { + parent: this + }; + + this.mnemonics = new Map<KeyCode, Array<MenuActionItem>>(); + + this.push(actions, { icon: true, label: true, isMenu: true }); + } + + private focusItemByElement(element: HTMLElement) { + const lastFocusedItem = this.focusedItem; + this.setFocusedItem(element); + + if (lastFocusedItem !== this.focusedItem) { + this.updateFocus(); + } + } + + 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 doGetActionItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): BaseActionItem { if (action instanceof Separator) { return new ActionItem(options.context, action, { icon: true }); } else if (action instanceof SubmenuAction) { - return new SubmenuActionItem(action, action.entries, parentData, options); + const menuActionItem = new SubmenuActionItem(action, action.entries, parentData, options); + + if (options.enableMnemonics) { + const mnemonic = menuActionItem.getMnemonic(); + if (mnemonic && menuActionItem.isEnabled()) { + let actionItems = []; + if (this.mnemonics.has(mnemonic)) { + actionItems = this.mnemonics.get(mnemonic); + } + + actionItems.push(menuActionItem); + + this.mnemonics.set(mnemonic, actionItems); + } + } + + return menuActionItem; } else { - const menuItemOptions: IActionItemOptions = {}; + const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics }; if (options.getKeyBinding) { const keybinding = options.getKeyBinding(action); if (keybinding) { @@ -81,46 +181,45 @@ export class Menu { } } - return new MenuActionItem(options.context, action, menuItemOptions); + const menuActionItem = new MenuActionItem(options.context, action, menuItemOptions); + + if (options.enableMnemonics) { + const mnemonic = menuActionItem.getMnemonic(); + if (mnemonic && menuActionItem.isEnabled()) { + let actionItems = []; + if (this.mnemonics.has(mnemonic)) { + actionItems = this.mnemonics.get(mnemonic); + } + + actionItems.push(menuActionItem); + + this.mnemonics.set(mnemonic, actionItems); + } + } + + return menuActionItem; } } - public get onDidCancel(): Event<void> { - return this.actionBar.onDidCancel; - } - - public get onDidBlur(): Event<void> { - return this.actionBar.onDidBlur; - } - - public focus() { - if (this.actionBar) { - this.actionBar.focus(true); - } - } - - public dispose() { - if (this.actionBar) { - this.actionBar.dispose(); - this.actionBar = null; - } - - if (this.listener) { - this.listener.dispose(); - this.listener = null; - } + public focus(selectFirst = true) { + super.focus(selectFirst); } } -class MenuActionItem extends BaseActionItem { - static MNEMONIC_REGEX: RegExp = /&&(.)/g; +interface IMenuItemOptions extends IActionItemOptions { + enableMnemonics?: boolean; +} +class MenuActionItem extends BaseActionItem { + public container: HTMLElement; protected $e: Builder; protected $label: Builder; - protected options: IActionItemOptions; + protected $check: Builder; + protected options: IMenuItemOptions; + protected mnemonic: KeyCode; private cssClass: string; - constructor(ctx: any, action: IAction, options: IActionItemOptions = {}) { + constructor(ctx: any, action: IAction, options: IMenuItemOptions = {}) { options.isMenu = true; super(action, action, options); @@ -128,19 +227,36 @@ class MenuActionItem extends BaseActionItem { this.options.icon = options.icon !== undefined ? options.icon : false; this.options.label = options.label !== undefined ? options.label : true; this.cssClass = ''; + + // Set mnemonic + if (this.options.label && options.enableMnemonics) { + let label = this.getAction().label; + if (label) { + let matches = MENU_MNEMONIC_REGEX.exec(label); + if (matches) { + this.mnemonic = KeyCodeUtils.fromString((!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase()); + } + } + } } - public render(container: HTMLElement): void { + render(container: HTMLElement): void { super.render(container); - this.$e = $('a.action-menu-item').appendTo(this.builder); + this.container = container; + + this.$e = $('a.action-menu-item').appendTo(this.element); if (this._action.id === Separator.ID) { // A separator is a presentation item this.$e.attr({ role: 'presentation' }); } else { this.$e.attr({ role: 'menuitem' }); + if (this.mnemonic) { + this.$e.attr({ 'aria-keyshortcuts': this.mnemonic }); + } } + this.$check = $('span.menu-item-check').attr({ 'role': 'none' }).appendTo(this.$e); this.$label = $('span.action-label').appendTo(this.$e); if (this.options.label && this.options.keybinding) { @@ -154,35 +270,35 @@ class MenuActionItem extends BaseActionItem { this._updateChecked(); } - public focus(): void { + focus(): void { super.focus(); this.$e.domFocus(); } - public _updateLabel(): void { + _updateLabel(): void { if (this.options.label) { let label = this.getAction().label; if (label) { - let matches = MenuActionItem.MNEMONIC_REGEX.exec(label); - if (matches && matches.length === 2) { - let mnemonic = matches[1]; - - let ariaLabel = label.replace(MenuActionItem.MNEMONIC_REGEX, mnemonic); - - this.$e.getHTMLElement().accessKey = mnemonic.toLocaleLowerCase(); - this.$label.attr('aria-label', ariaLabel); - } else { - this.$label.attr('aria-label', label); + const cleanLabel = cleanMnemonic(label); + if (!this.options.enableMnemonics) { + label = cleanLabel; } - label = label.replace(MenuActionItem.MNEMONIC_REGEX, '$1\u0332'); + this.$label.attr('aria-label', cleanLabel); + + const matches = MENU_MNEMONIC_REGEX.exec(label); + + if (matches) { + label = strings.escape(label).replace(MENU_ESCAPED_MNEMONIC_REGEX, '<u aria-hidden="true">$1</u>'); + this.$e.attr({ 'aria-keyshortcuts': (!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase() }); + } } - this.$label.text(label); + this.$label.innerHtml(label.trim()); } } - public _updateTooltip(): void { + _updateTooltip(): void { let title: string = null; if (this.getAction().tooltip) { @@ -201,7 +317,7 @@ class MenuActionItem extends BaseActionItem { } } - public _updateClass(): void { + _updateClass(): void { if (this.cssClass) { this.$e.removeClass(this.cssClass); } @@ -217,25 +333,31 @@ class MenuActionItem extends BaseActionItem { } } - public _updateEnabled(): void { + _updateEnabled(): void { if (this.getAction().enabled) { - this.builder.removeClass('disabled'); + removeClass(this.element, 'disabled'); this.$e.removeClass('disabled'); this.$e.attr({ tabindex: 0 }); } else { - this.builder.addClass('disabled'); + addClass(this.element, 'disabled'); this.$e.addClass('disabled'); removeTabIndexAndUpdateFocus(this.$e.getHTMLElement()); } } - public _updateChecked(): void { + _updateChecked(): void { if (this.getAction().checked) { - this.$label.addClass('checked'); + this.$e.addClass('checked'); + this.$e.attr({ 'role': 'menuitemcheckbox', 'aria-checked': true }); } else { - this.$label.removeClass('checked'); + this.$e.removeClass('checked'); + this.$e.attr({ 'role': 'menuitem', 'aria-checked': false }); } } + + getMnemonic(): KeyCode { + return this.mnemonic; + } } class SubmenuActionItem extends MenuActionItem { @@ -251,47 +373,47 @@ class SubmenuActionItem extends MenuActionItem { private parentData: ISubMenuData, private submenuOptions?: IMenuOptions ) { - super(action, action, { label: true, isMenu: true }); + super(action, action, submenuOptions); 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.element) && this.parentData.submenu === this.mysubmenu)) { + this.parentData.parent.focus(false); this.cleanupExistingSubmenu(true); } }, 750); } - public render(container: HTMLElement): void { + render(container: HTMLElement): void { super.render(container); this.$e.addClass('monaco-submenu-item'); this.$e.attr('aria-haspopup', 'true'); - $('span.submenu-indicator').text('\u25B6').appendTo(this.$e); + $('span.submenu-indicator').attr('aria-hidden', 'true').text('\u25B6').appendTo(this.$e); - $(this.builder).on(EventType.KEY_UP, (e) => { + $(this.element).on(EventType.KEY_UP, (e) => { let event = new StandardKeyboardEvent(e as KeyboardEvent); - if (event.equals(KeyCode.RightArrow)) { + if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { EventHelper.stop(e, true); - this.createSubmenu(); + this.createSubmenu(true); } }); - $(this.builder).on(EventType.KEY_DOWN, (e) => { + $(this.element).on(EventType.KEY_DOWN, (e) => { let event = new StandardKeyboardEvent(e as KeyboardEvent); - if (event.equals(KeyCode.RightArrow)) { + if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { EventHelper.stop(e, true); } }); - $(this.builder).on(EventType.MOUSE_OVER, (e) => { + $(this.element).on(EventType.MOUSE_OVER, (e) => { if (!this.mouseOver) { this.mouseOver = true; @@ -299,18 +421,23 @@ class SubmenuActionItem extends MenuActionItem { } }); - $(this.builder).on(EventType.MOUSE_LEAVE, (e) => { + $(this.element).on(EventType.MOUSE_LEAVE, (e) => { this.mouseOver = false; + }); - this.hideScheduler.schedule(); + $(this.element).on(EventType.FOCUS_OUT, (e) => { + if (!isAncestor(document.activeElement, this.element)) { + this.hideScheduler.schedule(); + } }); } - public onClick(e: EventLike) { + onClick(e: EventLike) { // stop clicking from trying to run an action EventHelper.stop(e, true); - this.createSubmenu(); + this.cleanupExistingSubmenu(false); + this.createSubmenu(false); } private cleanupExistingSubmenu(force: boolean) { @@ -325,12 +452,12 @@ 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' }); + this.submenuContainer = $(this.element).div({ class: 'monaco-submenu menubar-menu-items-holder context-view' }); $(this.submenuContainer).style({ - 'left': `${$(this.builder).getClientArea().width}px` + 'left': `${$(this.element).getClientArea().width}px` }); $(this.submenuContainer).on(EventType.KEY_UP, (e) => { @@ -356,19 +483,28 @@ class SubmenuActionItem extends MenuActionItem { this.parentData.submenu = new Menu(this.submenuContainer.getHTMLElement(), this.submenuActions, this.submenuOptions); - this.parentData.submenu.focus(); + + this.parentData.submenu.onDidCancel(() => { + this.parentData.parent.focus(); + this.parentData.submenu.dispose(); + this.parentData.submenu = null; + + this.submenuContainer.dispose(); + this.submenuContainer = null; + }); + + this.parentData.submenu.focus(selectFirstItem); this.mysubmenu = this.parentData.submenu; } else { - this.parentData.submenu.focus(); + this.parentData.submenu.focus(false); } } - public dispose() { + dispose() { super.dispose(); this.hideScheduler.dispose(); - this.showScheduler.dispose(); if (this.mysubmenu) { this.mysubmenu.dispose(); @@ -380,4 +516,17 @@ class SubmenuActionItem extends MenuActionItem { this.submenuContainer = null; } } +} + +export function cleanMnemonic(label: string): string { + const regex = MENU_MNEMONIC_REGEX; + + const matches = regex.exec(label); + if (!matches) { + return label; + } + + const mnemonicInText = matches[0].charAt(0) === '&'; + + return label.replace(regex, mnemonicInText ? '$2' : '').trim(); } \ No newline at end of file diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts index 7d294d601f5..5db9797a33a 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.ts +++ b/src/vs/base/browser/ui/progressbar/progressbar.ts @@ -6,13 +6,12 @@ 'use strict'; import 'vs/css!./progressbar'; -import { TPromise, ValueCallback } from 'vs/base/common/winjs.base'; import * as assert from 'vs/base/common/assert'; -import { Builder, $ } from 'vs/base/browser/builder'; -import * as DOM from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; +import { removeClasses, addClass, hasClass, addClasses, removeClass, hide, show } from 'vs/base/browser/dom'; +import { RunOnceScheduler } from 'vs/base/common/async'; const css_done = 'done'; const css_active = 'active'; @@ -38,11 +37,11 @@ const defaultOpts = { export class ProgressBar extends Disposable { private options: IProgressBarOptions; private workedVal: number; - private element: Builder; + private element: HTMLElement; private bit: HTMLElement; private totalWork: number; - private animationStopToken: ValueCallback; private progressBarBackground: Color; + private showDelayedScheduler: RunOnceScheduler; constructor(container: HTMLElement, options?: IProgressBarOptions) { super(); @@ -54,26 +53,19 @@ export class ProgressBar extends Disposable { this.progressBarBackground = this.options.progressBarBackground; + this._register(this.showDelayedScheduler = new RunOnceScheduler(() => show(this.element), 0)); + this.create(container); } private create(container: HTMLElement): void { - $(container).div({ 'class': css_progress_container }, builder => { - this.element = builder.clone(); + this.element = document.createElement('div'); + addClass(this.element, css_progress_container); + container.appendChild(this.element); - builder.div({ 'class': css_progress_bit }).on([DOM.EventType.ANIMATION_START, DOM.EventType.ANIMATION_END, DOM.EventType.ANIMATION_ITERATION], (e: Event) => { - switch (e.type) { - case DOM.EventType.ANIMATION_ITERATION: - if (this.animationStopToken) { - this.animationStopToken(null); - } - break; - } - - }, this.toDispose); - - this.bit = builder.getHTMLElement(); - }); + this.bit = document.createElement('div'); + addClass(this.bit, css_progress_bit); + this.element.appendChild(this.bit); this.applyStyles(); } @@ -81,9 +73,7 @@ export class ProgressBar extends Disposable { private off(): void { this.bit.style.width = 'inherit'; this.bit.style.opacity = '1'; - this.element.removeClass(css_active); - this.element.removeClass(css_infinite); - this.element.removeClass(css_discrete); + removeClasses(this.element, css_active, css_infinite, css_discrete); this.workedVal = 0; this.totalWork = undefined; @@ -104,14 +94,14 @@ export class ProgressBar extends Disposable { } private doDone(delayed: boolean): ProgressBar { - this.element.addClass(css_done); + addClass(this.element, css_done); // let it grow to 100% width and hide afterwards - if (!this.element.hasClass(css_infinite)) { + if (!hasClass(this.element, css_infinite)) { this.bit.style.width = 'inherit'; if (delayed) { - TPromise.timeout(200).then(() => this.off()); + setTimeout(200, () => this.off()); } else { this.off(); } @@ -121,7 +111,7 @@ export class ProgressBar extends Disposable { else { this.bit.style.opacity = '0'; if (delayed) { - TPromise.timeout(200).then(() => this.off()); + setTimeout(200, () => this.off()); } else { this.off(); } @@ -137,10 +127,8 @@ export class ProgressBar extends Disposable { this.bit.style.width = '2%'; this.bit.style.opacity = '1'; - this.element.removeClass(css_discrete); - this.element.removeClass(css_done); - this.element.addClass(css_active); - this.element.addClass(css_infinite); + removeClasses(this.element, css_discrete, css_done); + addClasses(this.element, css_active, css_infinite); return this; } @@ -191,20 +179,20 @@ export class ProgressBar extends Disposable { this.workedVal = value; this.workedVal = Math.min(this.totalWork, this.workedVal); - if (this.element.hasClass(css_infinite)) { - this.element.removeClass(css_infinite); + if (hasClass(this.element, css_infinite)) { + removeClass(this.element, css_infinite); } - if (this.element.hasClass(css_done)) { - this.element.removeClass(css_done); + if (hasClass(this.element, css_done)) { + removeClass(this.element, css_done); } - if (!this.element.hasClass(css_active)) { - this.element.addClass(css_active); + if (!hasClass(this.element, css_active)) { + addClass(this.element, css_active); } - if (!this.element.hasClass(css_discrete)) { - this.element.addClass(css_discrete); + if (!hasClass(this.element, css_discrete)) { + addClass(this.element, css_discrete); } this.bit.style.width = 100 * (this.workedVal / this.totalWork) + '%'; @@ -213,19 +201,22 @@ export class ProgressBar extends Disposable { } getContainer(): HTMLElement { - return this.element.getHTMLElement(); + return this.element; } show(delay?: number): void { + this.showDelayedScheduler.cancel(); + if (typeof delay === 'number') { - this.element.showDelayed(delay); + this.showDelayedScheduler.schedule(delay); } else { - this.element.show(); + show(this.element); } } hide(): void { - this.element.hide(); + hide(this.element); + this.showDelayedScheduler.cancel(); } style(styles: IProgressBarStyles): void { diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 5c7a28190a3..40a4d7c9a35 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -14,6 +14,7 @@ import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { SelectBoxNative } from 'vs/base/browser/ui/selectBox/selectBoxNative'; import { SelectBoxList } from 'vs/base/browser/ui/selectBox/selectBoxCustom'; import { isMacintosh } from 'vs/base/common/platform'; +import { IContentActionHandler } from 'vs/base/browser/htmlContentRenderer'; // Public SelectBox interface - Calls routed to appropriate select implementation class @@ -24,6 +25,7 @@ export interface ISelectBoxDelegate { setOptions(options: string[], selected?: number, disabled?: number): void; select(index: number): void; setAriaLabel(label: string); + setDetailsProvider(provider: (index: number) => { details: string, isMarkdown: boolean }); focus(): void; blur(): void; dispose(): void; @@ -37,6 +39,8 @@ export interface ISelectBoxDelegate { export interface ISelectBoxOptions { ariaLabel?: string; minBottomMargin?: number; + hasDetails?: boolean; + markdownActionHandler?: IContentActionHandler; } export interface ISelectBoxStyles extends IListStyles { @@ -44,6 +48,7 @@ export interface ISelectBoxStyles extends IListStyles { selectListBackground?: Color; selectForeground?: Color; selectBorder?: Color; + selectListBorder?: Color; focusBorder?: Color; } @@ -68,7 +73,7 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { mixin(this.styles, defaultStyles, false); // Instantiate select implementation based on platform - if (isMacintosh) { + if (isMacintosh && !(selectBoxOptions && selectBoxOptions.hasDetails)) { this.selectBoxDelegate = new SelectBoxNative(options, selected, styles, selectBoxOptions); } else { this.selectBoxDelegate = new SelectBoxList(options, selected, contextViewProvider, styles, selectBoxOptions); @@ -95,6 +100,10 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { this.selectBoxDelegate.setAriaLabel(label); } + public setDetailsProvider(provider: (index: number) => { details: string, isMarkdown: boolean }): void { + this.selectBoxDelegate.setDetailsProvider(provider); + } + public focus(): void { this.selectBoxDelegate.focus(); } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css index dcf29613a97..72901ceaaaa 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css @@ -16,8 +16,28 @@ .monaco-select-box-dropdown-container { display: none; + -webkit-box-sizing: border-box; + -o-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; } +.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown * { + margin: 0; +} + +.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown code { + line-height: 15px; /** For some reason, this is needed, otherwise <code> will take up 20px height */ + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; +} + + .monaco-select-box-dropdown-container.visible { display: flex; flex-direction: column; @@ -42,6 +62,10 @@ box-sizing: border-box; } +.monaco-select-box-dropdown-container > .select-box-details-pane { + padding: 5px; +} + .hc-black .monaco-select-box-dropdown-container > .select-box-dropdown-list-container { padding-top: var(--dropdown-padding-top); padding-bottom: var(--dropdown-padding-bottom); diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 2efed5c5dcf..d0817b49ba0 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -14,11 +14,12 @@ import * as dom from 'vs/base/browser/dom'; import * as arrays from 'vs/base/common/arrays'; import { IContextViewProvider, AnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; import { List } from 'vs/base/browser/ui/list/listWidget'; -import { IVirtualDelegate, IRenderer } from 'vs/base/browser/ui/list/list'; +import { IVirtualDelegate, IRenderer, IListEvent } from 'vs/base/browser/ui/list/list'; import { domEvent } from 'vs/base/browser/event'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISelectBoxDelegate, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { isMacintosh } from 'vs/base/common/platform'; +import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; const $ = dom.$; @@ -58,6 +59,9 @@ class SelectListRenderer implements IRenderer<ISelectOptionItem, ISelectListTemp data.optionText.textContent = optionText; data.root.setAttribute('aria-label', nls.localize('selectAriaOption', "{0}", optionText)); + // Workaround for list labels + data.root.setAttribute('aria-selected', 'true'); + // pseudo-select disabled option if (optionDisabled) { dom.addClass((<HTMLElement>data.root), 'option-disabled'); @@ -79,7 +83,7 @@ class SelectListRenderer implements IRenderer<ISelectOptionItem, ISelectListTemp export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISelectOptionItem> { private static readonly DEFAULT_DROPDOWN_MINIMUM_BOTTOM_MARGIN = 32; - private static readonly DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN = 42; + private static readonly DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN = 2; private static readonly DEFAULT_MINIMUM_VISIBLE_OPTIONS = 3; private _isVisible: boolean; @@ -100,6 +104,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele private widthControlElement: HTMLElement; private _currentSelection: number; private _dropDownPosition: AnchorPosition; + private detailsProvider: (index: number) => { details: string, isMarkdown: boolean }; + private selectionDetailsPane: HTMLElement; + + + private _sticky: boolean = false; // for dev purposes only constructor(options: string[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { @@ -150,6 +159,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele dom.addClass(this.selectDropDownContainer, 'monaco-select-box-dropdown-padding'); // Setup list for drop-down select this.createSelectList(this.selectDropDownContainer); + this.selectionDetailsPane = dom.append(this.selectDropDownContainer, $('.select-box-details-pane')); // Create span flex box item/div we can measure and control let widthControlOuterDiv = dom.append(this.selectDropDownContainer, $('.select-box-dropdown-container-width-control')); @@ -282,6 +292,10 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele this.selectList.getHTMLElement().setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } + public setDetailsProvider(provider: (index: number) => { details: string, isMarkdown: boolean }): void { + this.detailsProvider = provider; + } + public focus(): void { if (this.selectElement) { this.selectElement.focus(); @@ -317,6 +331,15 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row.focused:not(:hover) { color: ${this.styles.listFocusForeground} !important; }`); } + if (!this.styles.selectBorder.equals(this.styles.selectBackground)) { + content.push(`.monaco-select-box-dropdown-container { border: 1px solid ${this.styles.selectBorder} } `); + content.push(`.monaco-select-box-dropdown-container > .select-box-details-pane { border-top: 1px solid ${this.styles.selectBorder} } `); + } + else if (this.styles.selectListBorder) { + content.push(`.monaco-select-box-dropdown-container { border: 1px solid ${this.styles.selectListBorder} } `); + content.push(`.monaco-select-box-dropdown-container > .select-box-details-pane { border-top: 1px solid ${this.styles.selectListBorder} } `); + } + // Hover foreground - ignore for disabled options if (this.styles.listHoverForeground) { content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row:hover { color: ${this.styles.listHoverForeground} !important; }`); @@ -367,6 +390,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele let listBackground = this.styles.selectListBackground ? this.styles.selectListBackground.toString() : background; this.selectDropDownListContainer.style.backgroundColor = listBackground; + this.selectionDetailsPane.style.backgroundColor = listBackground; const optionsBorder = this.styles.focusBorder ? this.styles.focusBorder.toString() : null; this.selectDropDownContainer.style.outlineColor = optionsBorder; this.selectDropDownContainer.style.outlineOffset = '-1px'; @@ -390,9 +414,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele } // Set drop-down position above/below from required height and margins - this.layoutSelectDropDown(true); + // If pre-layout cannot fit at least one option do not show drop-down + if (!this.layoutSelectDropDown(true)) { + return; + } - this._isVisible = true; this.cloneElementFont(this.selectElement, this.selectDropDownContainer); this.contextViewProvider.showContextView({ @@ -408,6 +434,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele // Track initial selection the case user escape, blur this._currentSelection = this.selected; + this._isVisible = true; } private hideSelectDropDown(focusSelect: boolean) { @@ -420,6 +447,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele if (focusSelect) { this.selectElement.focus(); } + this.contextViewProvider.hideContextView(); } @@ -440,7 +468,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele }; } - private layoutSelectDropDown(preLayoutPosition?: boolean) { + private layoutSelectDropDown(preLayoutPosition?: boolean): boolean { // Layout ContextView drop down select list and container // Have to manage our vertical overflow, sizing, position below or above @@ -451,30 +479,54 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele const selectPosition = dom.getDomNodePagePosition(this.selectElement); const styles = getComputedStyle(this.selectElement); const verticalPadding = parseFloat(styles.getPropertyValue('--dropdown-padding-top')) + parseFloat(styles.getPropertyValue('--dropdown-padding-bottom')); - let maxSelectDropDownHeight = 0; - maxSelectDropDownHeight = (window.innerHeight - selectPosition.top - selectPosition.height - this.selectBoxOptions.minBottomMargin); + const maxSelectDropDownHeightBelow = (window.innerHeight - selectPosition.top - selectPosition.height - this.selectBoxOptions.minBottomMargin); + const maxSelectDropDownHeightAbove = (selectPosition.top - SelectBoxList.DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN); + // Get initial list height and determine space above and below this.selectList.layout(); let listHeight = this.selectList.contentHeight; + const minRequiredDropDownHeight = listHeight + verticalPadding; + const maxVisibleOptionsBelow = ((Math.floor((maxSelectDropDownHeightBelow - verticalPadding) / this.getHeight()))); + const maxVisibleOptionsAbove = ((Math.floor((maxSelectDropDownHeightAbove - verticalPadding) / this.getHeight()))); // If we are only doing pre-layout check/adjust position only // Calculate vertical space available, flip up if insufficient - // Use reflected padding on parent select, ContextView style properties not available before DOM attachment + // Use reflected padding on parent select, ContextView style + // properties not available before DOM attachment + if (preLayoutPosition) { + // Check if select moved out of viewport , do not open + // If at least one option cannot be shown, don't open the drop-down or hide/remove if open + if ((selectPosition.top + selectPosition.height) > (window.innerHeight - 22) + || selectPosition.top < SelectBoxList.DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN + || ((maxVisibleOptionsBelow < 1) && (maxVisibleOptionsAbove < 1))) { + // Indicate we cannot open + return false; + } + + // Determine if we have to flip up // Always show complete list items - never more than Max available vertical height - if (listHeight + verticalPadding > maxSelectDropDownHeight) { - const maxVisibleOptions = ((Math.floor((maxSelectDropDownHeight - verticalPadding) / this.getHeight()))); - - // Check if we can at least show min items otherwise flip above - if (maxVisibleOptions < SelectBoxList.DEFAULT_MINIMUM_VISIBLE_OPTIONS) { - this._dropDownPosition = AnchorPosition.ABOVE; - } else { - this._dropDownPosition = AnchorPosition.BELOW; - } + if (maxVisibleOptionsBelow < SelectBoxList.DEFAULT_MINIMUM_VISIBLE_OPTIONS + && maxVisibleOptionsAbove > maxVisibleOptionsBelow + && this.options.length > maxVisibleOptionsBelow + ) { + this._dropDownPosition = AnchorPosition.ABOVE; + } else { + this._dropDownPosition = AnchorPosition.BELOW; } // Do full layout on showSelectDropDown only - return; + return true; + } + + // Check if select out of viewport or cutting into status bar + if ((selectPosition.top + selectPosition.height) > (window.innerHeight - 22) + || selectPosition.top < SelectBoxList.DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN + || (this._dropDownPosition === AnchorPosition.BELOW && maxVisibleOptionsBelow < 1) + || (this._dropDownPosition === AnchorPosition.ABOVE && maxVisibleOptionsAbove < 1)) { + // Cannot properly layout, close and hide + this.hideSelectDropDown(true); + return false; } // Make visible to enable measurements @@ -483,15 +535,22 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele // SetUp list dimensions and layout - account for container padding // Use position to check above or below available space if (this._dropDownPosition === AnchorPosition.BELOW) { + if (this._isVisible && maxVisibleOptionsBelow + maxVisibleOptionsAbove < 1) { + // If drop-down is visible, must be doing a DOM re-layout, hide since we don't fit + // Hide drop-down, hide contextview, focus on parent select + this.hideSelectDropDown(true); + return false; + } + // Set container height to max from select bottom to margin (default/minBottomMargin) - if (listHeight + verticalPadding > maxSelectDropDownHeight) { - listHeight = ((Math.floor((maxSelectDropDownHeight - verticalPadding) / this.getHeight())) * this.getHeight()); + if (minRequiredDropDownHeight > maxSelectDropDownHeightBelow) { + listHeight = (maxVisibleOptionsBelow * this.getHeight() + verticalPadding); } } else { // Set container height to max from select top to margin (default/minTopMargin) - maxSelectDropDownHeight = (selectPosition.top - SelectBoxList.DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN); - if (listHeight + verticalPadding > maxSelectDropDownHeight) { - listHeight = ((Math.floor((maxSelectDropDownHeight - SelectBoxList.DEFAULT_DROPDOWN_MINIMUM_TOP_MARGIN) / this.getHeight())) * this.getHeight()); + if (minRequiredDropDownHeight > maxSelectDropDownHeightAbove) { + // listHeight = ((Math.floor((maxSelectDropDownHeightBelow - verticalPadding) / this.getHeight())) * this.getHeight()); + listHeight = (maxVisibleOptionsAbove * this.getHeight() + verticalPadding); } } @@ -505,9 +564,6 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele this.selectList.reveal(this.selectList.getFocus()[0] || 0); } - // Set final container height after adjustments - this.selectDropDownContainer.style.height = (listHeight + verticalPadding) + 'px'; - // Determine optimal width - min(longest option), opt(parent select, excluding margins), max(ContextView controlled) const selectWidth = this.selectElement.offsetWidth; const selectMinWidth = this.setWidthControlElement(this.widthControlElement); @@ -518,7 +574,9 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele // Maintain focus outline on parent select as well as list container - tabindex for focus this.selectDropDownListContainer.setAttribute('tabindex', '0'); dom.toggleClass(this.selectElement, 'synthetic-focus', true); - dom.toggleClass(this.selectDropDownContainer, 'synthetic-focus', true); + return true; + } else { + return false; } } @@ -585,7 +643,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele .filter(() => this.selectList.length > 0) .on(e => this.onMouseUp(e), this, this.toDispose); - this.toDispose.push(this.selectList.onDidBlur(e => this.onListBlur())); + this.toDispose.push( + this.selectList.onDidBlur(e => this.onListBlur()), + this.selectList.onMouseOver(e => this.selectList.setFocus([e.index])), + this.selectList.onFocusChange(e => this.onListFocus(e)) + ); this.selectList.getHTMLElement().setAttribute('aria-expanded', 'true'); } @@ -631,7 +693,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele // List Exit - passive - implicit no selection change, hide drop-down private onListBlur(): void { - + if (this._sticky) { return; } if (this.selected !== this._currentSelection) { // Reset selected to current if no change this.select(this._currentSelection); @@ -640,6 +702,48 @@ export class SelectBoxList implements ISelectBoxDelegate, IVirtualDelegate<ISele this.hideSelectDropDown(false); } + + private renderDescriptionMarkdown(text: string): HTMLElement { + const cleanRenderedMarkdown = (element: Node) => { + for (let i = 0; i < element.childNodes.length; i++) { + const child = element.childNodes.item(i); + + const tagName = (<Element>child).tagName && (<Element>child).tagName.toLowerCase(); + if (tagName === 'img') { + element.removeChild(child); + } else { + cleanRenderedMarkdown(child); + } + } + }; + + const renderedMarkdown = renderMarkdown({ value: text }, { + actionHandler: this.selectBoxOptions.markdownActionHandler + }); + + renderedMarkdown.classList.add('select-box-description-markdown'); + cleanRenderedMarkdown(renderedMarkdown); + + return renderedMarkdown; + } + + // List Focus Change - passive - update details pane with newly focused element's data + private onListFocus(e: IListEvent<ISelectOptionItem>) { + this.selectionDetailsPane.innerText = ''; + const selectedIndex = e.indexes[0]; + let description = this.detailsProvider ? this.detailsProvider(selectedIndex) : { details: '', isMarkdown: false }; + if (description.details) { + if (description.isMarkdown) { + this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description.details)); + } else { + this.selectionDetailsPane.innerText = description.details; + } + this.selectionDetailsPane.style.display = 'block'; + } else { + this.selectionDetailsPane.style.display = 'none'; + } + } + // List keyboard controller // List exit - active - hide ContextView dropdown, reset selection, return focus to parent select diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index ecd7fcca737..c748d0c1e57 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -113,6 +113,10 @@ export class SelectBoxNative implements ISelectBoxDelegate { this.selectElement.setAttribute('aria-label', label); } + public setDetailsProvider(provider: any): void { + console.error('details are not available for native select boxes'); + } + public focus(): void { if (this.selectElement) { this.selectElement.focus(); diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 31ad491bac6..c5143e3007b 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -11,7 +11,7 @@ import { Event, Emitter, chain } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { $, append, addClass, removeClass, toggleClass, trackFocus } from 'vs/base/browser/dom'; +import { $, append, addClass, removeClass, toggleClass, trackFocus, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { firstIndex } from 'vs/base/common/arrays'; import { Color, RGBA } from 'vs/base/common/color'; import { SplitView, IView } from './splitview'; @@ -386,7 +386,14 @@ export class PanelView implements IDisposable { addPanel(panel: Panel, size: number, index = this.splitview.length): void { const disposables: IDisposable[] = []; - panel.onDidChange(this.setupAnimation, this, disposables); + disposables.push( + // fix https://github.com/Microsoft/vscode/issues/37129 by delaying the listener + // for changes to animate them. lots of views cause a onDidChange during their + // initial creation and this causes the view to animate even though it shows + // for the first time. animation should only be used to indicate new elements + // are added or existing ones removed in a view that is already showing + scheduleAtNextAnimationFrame(() => panel.onDidChange(this.setupAnimation, this, disposables)) + ); const panelItem = { panel, disposable: combinedDisposable(disposables) }; this.panelItems.splice(index, 0, panelItem); diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 18a45d2c74c..1b020fee6e7 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -60,6 +60,7 @@ class TreeDelegate<T> implements IVirtualDelegate<ITreeNode<T>> { interface ITreeListTemplateData<T> { twistie: HTMLElement; + count: HTMLElement; templateData: T; } @@ -89,9 +90,10 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis const el = append(container, $('.monaco-tl-row')); const twistie = append(el, $('.tl-twistie')); const contents = append(el, $('.tl-contents')); + const count = append(el, $('.tl-count')); const templateData = this.renderer.renderTemplate(contents); - return { twistie, templateData }; + return { twistie, count, templateData }; } renderElement(node: ITreeNode<T>, index: number, templateData: ITreeListTemplateData<TTemplateData>): void { @@ -99,6 +101,7 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis templateData.twistie.style.width = `${10 + node.depth * 10}px`; renderTwistie(node, templateData.twistie); + templateData.count.textContent = `${node.visibleCount}`; this.renderer.renderElement(node.element, index, templateData.templateData); } @@ -119,6 +122,7 @@ class TreeRenderer<T, TTemplateData> implements IRenderer<ITreeNode<T>, ITreeLis } renderTwistie(node, templateData.twistie); + templateData.count.textContent = `${node.visibleCount}`; } dispose(): void { @@ -200,6 +204,8 @@ export class Tree<T> implements IDisposable { const [parentLocation] = tail2(location); const parentListIndex = this.model.getListIndex(parentLocation); + + this.view.reveal(parentListIndex); this.view.setFocus([parentListIndex]); } } @@ -224,7 +230,10 @@ export class Tree<T> implements IDisposable { } const [focusedIndex] = this.view.getFocus(); - this.view.setFocus([focusedIndex + 1]); + const firstChildIndex = focusedIndex + 1; + + this.view.reveal(firstChildIndex); + this.view.setFocus([firstChildIndex]); } } diff --git a/src/vs/base/browser/ui/tree/treeModel.ts b/src/vs/base/browser/ui/tree/treeModel.ts index 1a30ff92292..7d8e2b70c98 100644 --- a/src/vs/base/browser/ui/tree/treeModel.ts +++ b/src/vs/base/browser/ui/tree/treeModel.ts @@ -39,13 +39,35 @@ function getVisibleCount<T>(nodes: IMutableTreeNode<T>[]): number { return nodes.reduce(visibleCountReducer, 0); } -function getVisibleNodes<T>(nodes: IMutableTreeNode<T>[], result: ITreeNode<T>[] = []): ITreeNode<T>[] { - for (const node of nodes) { +/** + * Recursively updates the visibleCount of a subtree, while collecting + * all the visible nodes in an array. + */ +function updateVisibleCount<T>(node: IMutableTreeNode<T>): ITreeNode<T>[] { + const previousVisibleCount = node.visibleCount; + const result: ITreeNode<T>[] = []; + + function _updateVisibleCount(node: IMutableTreeNode<T>): number { result.push(node); + node.visibleCount = 1; if (!node.collapsed) { - getVisibleNodes(node.children, result); + for (const child of node.children) { + node.visibleCount += _updateVisibleCount(child); + } } + + return node.visibleCount; + } + + _updateVisibleCount(node); + + const visibleCountDiff = result.length - previousVisibleCount; + node = node.parent; + + while (node) { + node.visibleCount += visibleCountDiff; + node = node.parent; } return result; @@ -108,6 +130,7 @@ export class TreeModel<T> { visibleCount: 1 }; + // TODO@joao can't we do without this? private _onDidChangeCollapseState = new Emitter<ITreeNode<T>>(); readonly onDidChangeCollapseState: Event<ITreeNode<T>> = this._onDidChangeCollapseState.event; @@ -165,30 +188,14 @@ export class TreeModel<T> { node.collapsed = collapsed; if (visible) { - this._onDidChangeCollapseState.fire(node); + const previousVisibleCount = node.visibleCount; + const toInsert = updateVisibleCount(node); - let visibleCountDiff: number; - - if (collapsed) { - const deleteCount = getVisibleCount(node.children); - - this.list.splice(listIndex + 1, deleteCount, []); - visibleCountDiff = -deleteCount; - } else { - const toInsert = getVisibleNodes(node.children); - - this.list.splice(listIndex + 1, 0, toInsert); - visibleCountDiff = toInsert.length; - } - - let mutableNode = node; - - while (mutableNode) { - mutableNode.visibleCount += visibleCountDiff; - mutableNode = mutableNode.parent; - } + this.list.splice(listIndex + 1, previousVisibleCount - 1, toInsert.slice(1)); } + this._onDidChangeCollapseState.fire(node); + return true; } diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index a6d78296e69..d2cca21d5ed 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -7,6 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; +import { isThenable } from 'vs/base/common/async'; export interface ITelemetryData { from?: string; @@ -225,7 +226,7 @@ export class ActionRunner implements IActionRunner { protected runAction(action: IAction, context?: any): TPromise<any> { const res = context ? action.run(context) : action.run(); - if (TPromise.is(res)) { + if (isThenable(res)) { return res; } 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..5a05080631c 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -4,15 +4,17 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; import { ISplice } from 'vs/base/common/sequence'; +import { TPromise } from 'vs/base/common/winjs.base'; /** * Returns the last element of an array. * @param array The array. * @param n Which element from the end (default is zero). */ -export function tail<T>(array: T[], n: number = 0): T { +export function tail<T>(array: ArrayLike<T>, n: number = 0): T { return array[array.length - (1 + n)]; } @@ -252,11 +254,11 @@ export function top<T>(array: T[], compare: (a: T, b: T) => number, n: number): * @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<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number): TPromise<T[]> { +export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: number, batch: number, token?: CancellationToken): TPromise<T[]> { if (n === 0) { return TPromise.as([]); } - let canceled = false; + return new TPromise((resolve, reject) => { (async () => { const o = array.length; @@ -265,16 +267,14 @@ export function topAsync<T>(array: T[], compare: (a: T, b: T) => number, n: numb if (i > n) { await new Promise(resolve => setTimeout(resolve)); // nextTick() would starve I/O. } - if (canceled) { - throw new Error('canceled'); + if (token && token.isCancellationRequested) { + throw canceled(); } topStep(array, compare, result, i, m); } return result; })() .then(resolve, reject); - }, () => { - canceled = true; }); } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 4c06af4e2f5..f1a9a5c4919 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<T>(callback: (token: CancellationToken) export function asWinJsPromise<T>(callback: (token: CancellationToken) => T | TPromise<T> | Thenable<T>): TPromise<T> { let source = new CancellationTokenSource(); - return new TPromise<T>((resolve, reject, progress) => { + return new TPromise<T>((resolve, reject) => { let item = callback(source.token); if (item instanceof TPromise) { item.then(result => { @@ -77,7 +77,7 @@ export function asWinJsPromise<T>(callback: (token: CancellationToken) => T | TP }, err => { source.dispose(); reject(err); - }, progress); + }); } else if (isThenable<T>(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<T> extends TPromise<T> { constructor(outer: TPromise<T>) { 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); } } @@ -410,8 +408,12 @@ export function timeout(n: number): CancelablePromise<void> { }); } -function isWinJSPromise(candidate: any): candidate is TPromise { - return TPromise.is(candidate) && typeof (<TPromise>candidate).done === 'function'; +/** + * + * @returns `true` if candidate is a `WinJS.Promise` + */ +export function isWinJSPromise(candidate: any): candidate is TPromise { + return isThenable(candidate) && typeof (<TPromise>candidate).done === 'function'; } /** @@ -425,7 +427,7 @@ export function always<T>(thenable: TPromise<T>, f: Function): TPromise<T>; export function always<T>(promise: Thenable<T>, f: Function): Thenable<T>; export function always<T>(winjsPromiseOrThenable: Thenable<T> | TPromise<T>, f: Function): TPromise<T> | Thenable<T> { if (isWinJSPromise(winjsPromiseOrThenable)) { - return new TPromise<T>((c, e, p) => { + return new TPromise<T>((c, e) => { winjsPromiseOrThenable.done((result) => { try { f(result); @@ -440,8 +442,6 @@ export function always<T>(winjsPromiseOrThenable: Thenable<T> | TPromise<T>, f: errors.onUnexpectedError(e1); } e(err); - }, (progress) => { - p(progress); }); }, () => { winjsPromiseOrThenable.cancel(); @@ -534,7 +534,6 @@ interface ILimitedTaskFactory { factory: ITask<TPromise>; c: ValueCallback; e: ErrorCallback; - p: ProgressCallback; } /** @@ -563,14 +562,9 @@ export class Limiter<T> { } queue(promiseFactory: ITask<TPromise>): TPromise; - queue(promiseFactory: ITask<TPromise<T>>): TPromise<T> { - return new TPromise<T>((c, e, p) => { - this.outstandingPromises.push({ - factory: promiseFactory, - c: c, - e: e, - p: p - }); + queue(factory: ITask<TPromise<T>>): TPromise<T> { + return new TPromise<T>((c, e) => { + this.outstandingPromises.push({ factory, c, e }); this.consume(); }); @@ -582,7 +576,7 @@ export class Limiter<T> { 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/cache.ts b/src/vs/base/common/cache.ts index 65c468842ce..68dbbfe3845 100644 --- a/src/vs/base/common/cache.ts +++ b/src/vs/base/common/cache.ts @@ -5,25 +5,33 @@ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { CancelablePromise } from 'vs/base/common/async'; + +export interface CacheResult<T> { + promise: Thenable<T>; + dispose(): void; +} export default class Cache<T> { - private promise: TPromise<T> = null; - constructor(private task: () => TPromise<T>) { } + private result: CacheResult<T> = null; + constructor(private task: () => CancelablePromise<T>) { } - get(): TPromise<T> { - if (this.promise) { - return this.promise; + get(): CacheResult<T> { + if (this.result) { + return this.result; } const promise = this.task(); - this.promise = new TPromise<T>((c, e) => promise.done(c, e), () => { - this.promise = null; - promise.cancel(); - }); + this.result = { + promise, + dispose: () => { + this.result = null; + promise.cancel(); + } + }; - return this.promise; + return this.result; } } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 37887a5318f..957011262c0 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -277,7 +277,7 @@ export function fromCallback<T>(fn: (handler: (e: T) => void) => IDisposable): E return emitter.event; } -export function fromPromise<T =any>(promise: TPromise<T>): Event<T> { +export function fromPromise<T =any>(promise: Thenable<T>): Event<T> { const emitter = new Emitter<T>(); let shouldEmit = false; @@ -295,7 +295,7 @@ export function fromPromise<T =any>(promise: TPromise<T>): Event<T> { return emitter.event; } -export function toPromise<T>(event: Event<T>): TPromise<T> { +export function toPromise<T>(event: Event<T>): Thenable<T> { return new TPromise(complete => { const sub = event(e => { sub.dispose(); diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 5f052e2e02a..da02dae8613 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as strings from 'vs/base/common/strings'; -import { LRUCache } from 'vs/base/common/map'; import { CharCode } from 'vs/base/common/charCode'; +import { LRUCache } from 'vs/base/common/map'; +import * as strings from 'vs/base/common/strings'; export interface IFilter { // Returns null if word doesn't match. @@ -362,13 +362,19 @@ export function anyScore(pattern: string, word: string, patternMaxWhitespaceIgno //#region --- fuzzyScore --- -export function createMatches(position: number[]): IMatch[] { +export function createMatches(offsetOrScore: number[] | FuzzyScore): IMatch[] { let ret: IMatch[] = []; - if (!position) { + if (!offsetOrScore) { return ret; } + let offsets: number[]; + if (Array.isArray(offsetOrScore[1])) { + offsets = (offsetOrScore as FuzzyScore)[1]; + } else { + offsets = offsetOrScore as number[]; + } let last: IMatch; - for (const pos of position) { + for (const pos of offsets) { if (last && last.end === pos) { last.end += 1; } else { diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index 93365a9fb6a..6ec8e429211 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -10,6 +10,7 @@ import * as paths from 'vs/base/common/paths'; import { LRUCache } from 'vs/base/common/map'; import { CharCode } from 'vs/base/common/charCode'; import { TPromise } from 'vs/base/common/winjs.base'; +import { isThenable } from 'vs/base/common/async'; export interface IExpression { [pattern: string]: boolean | SiblingClause | any; @@ -646,7 +647,7 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio const clausePattern = when.replace('$(basename)', name); const matched = hasSibling(clausePattern); - return TPromise.is(matched) ? + return isThenable(matched) ? matched.then(m => m ? pattern : null) : matched ? pattern : null; }; @@ -699,4 +700,4 @@ function aggregateBasenameMatches(parsedPatterns: (ParsedStringPattern | ParsedE const aggregatedPatterns = parsedPatterns.filter(parsedPattern => !(<ParsedStringPattern>parsedPattern).basenames); aggregatedPatterns.push(aggregate); return aggregatedPatterns; -} \ No newline at end of file +} 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 15d9a56d269..3e50bf58495 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 LabelService instead */ export function getPathLabel(resource: URI | string, userHomeProvider: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string { if (!resource) { diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index 64cc685b4c2..92e7aaa8b53 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -1242,7 +1242,7 @@ Parser.prototype.tok = function() { return this.renderer.listitem(body); } case 'html': { - // TODO parse inline content if parameter markdown=1 + // TODO@matt parse inline content if parameter markdown=1 return this.renderer.html(this.token.text); } case 'paragraph': { diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index fa4932e9ec7..9c31e269491 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -11,4 +11,12 @@ export function clamp(value: number, min: number, max: number): number { export function rot(index: number, modulo: number): number { return (modulo + (index % modulo)) % modulo; -} \ No newline at end of file +} + +export class Counter { + private _next = 0; + + getNext(): number { + return this._next++; + } +} diff --git a/src/vs/base/common/paging.ts b/src/vs/base/common/paging.ts index 286fa69d08f..93e1e4dfa52 100644 --- a/src/vs/base/common/paging.ts +++ b/src/vs/base/common/paging.ts @@ -7,6 +7,9 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { isArray } from 'vs/base/common/types'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; +import { range } from 'vs/base/common/arrays'; /** * A Pager is a stateless abstraction over a paged collection. @@ -15,16 +18,27 @@ export interface IPager<T> { firstPage: T[]; total: number; pageSize: number; - getPage(pageIndex: number): TPromise<T[]>; + getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable<T[]>; } interface IPage<T> { isResolved: boolean; - promise: TPromise<any>; + promise: Thenable<void>; + cts: CancellationTokenSource; promiseIndexes: Set<number>; elements: T[]; } +function createPage<T>(elements?: T[]): IPage<T> { + return { + isResolved: !!elements, + promise: null, + cts: null, + promiseIndexes: new Set<number>(), + elements: elements || [] + }; +} + /** * A PagedModel is a stateful model over an abstracted paged collection. */ @@ -32,7 +46,7 @@ export interface IPagedModel<T> { length: number; isResolved(index: number): boolean; get(index: number): T; - resolve(index: number): TPromise<T>; + resolve(index: number, cancellationToken: CancellationToken): Thenable<T>; } export function singlePagePager<T>(elements: T[]): IPager<T> { @@ -51,21 +65,21 @@ export class PagedModel<T> implements IPagedModel<T> { get length(): number { return this.pager.total; } - constructor(arg: IPager<T> | T[], private pageTimeout: number = 500) { + constructor(arg: IPager<T> | T[]) { this.pager = isArray(arg) ? singlePagePager<T>(arg) : arg; - this.pages = [{ isResolved: true, promise: null, promiseIndexes: new Set<number>(), elements: this.pager.firstPage.slice() }]; - const totalPages = Math.ceil(this.pager.total / this.pager.pageSize); - for (let i = 0, len = totalPages - 1; i < len; i++) { - this.pages.push({ isResolved: false, promise: null, promiseIndexes: new Set<number>(), elements: [] }); - } + this.pages = [ + createPage(this.pager.firstPage.slice()), + ...range(totalPages - 1).map(() => createPage<T>()) + ]; } isResolved(index: number): boolean { const pageIndex = Math.floor(index / this.pager.pageSize); const page = this.pages[pageIndex]; + return !!page.isResolved; } @@ -77,7 +91,11 @@ export class PagedModel<T> implements IPagedModel<T> { return page.elements[indexInPage]; } - resolve(index: number): TPromise<T> { + resolve(index: number, cancellationToken: CancellationToken): Thenable<T> { + if (cancellationToken.isCancellationRequested) { + return TPromise.wrapError(canceled()); + } + const pageIndex = Math.floor(index / this.pager.pageSize); const indexInPage = index % this.pager.pageSize; const page = this.pages[pageIndex]; @@ -87,33 +105,74 @@ export class PagedModel<T> implements IPagedModel<T> { } if (!page.promise) { - page.promise = TPromise.timeout(this.pageTimeout) - .then(() => this.pager.getPage(pageIndex)) + page.cts = new CancellationTokenSource(); + page.promise = this.pager.getPage(pageIndex, page.cts.token) .then(elements => { page.elements = elements; page.isResolved = true; page.promise = null; + page.cts = null; }, err => { page.isResolved = false; page.promise = null; + page.cts = null; return TPromise.wrapError(err); }); } - return new TPromise<T>((c, e) => { - page.promiseIndexes.add(index); - page.promise.done(() => c(page.elements[indexInPage])); - }, () => { - if (!page.promise) { + cancellationToken.onCancellationRequested(() => { + if (!page.cts) { return; } page.promiseIndexes.delete(index); if (page.promiseIndexes.size === 0) { - page.promise.cancel(); + page.cts.cancel(); } }); + + page.promiseIndexes.add(index); + + return page.promise.then(() => page.elements[indexInPage]); + } +} + +export class DelayedPagedModel<T> implements IPagedModel<T> { + + get length(): number { return this.model.length; } + + constructor(private model: IPagedModel<T>, private timeout: number = 500) { } + + isResolved(index: number): boolean { + return this.model.isResolved(index); + } + + get(index: number): T { + return this.model.get(index); + } + + resolve(index: number, cancellationToken: CancellationToken): Thenable<T> { + return new TPromise((c, e) => { + if (cancellationToken.isCancellationRequested) { + return e(canceled()); + } + + const timer = setTimeout(() => { + if (cancellationToken.isCancellationRequested) { + return e(canceled()); + } + + timeoutCancellation.dispose(); + this.model.resolve(index, cancellationToken).then(c, e); + }, this.timeout); + + const timeoutCancellation = cancellationToken.onCancellationRequested(() => { + clearTimeout(timer); + timeoutCancellation.dispose(); + e(canceled()); + }); + }); } } @@ -126,7 +185,7 @@ export function mapPager<T, R>(pager: IPager<T>, fn: (t: T) => R): IPager<R> { firstPage: pager.firstPage.map(fn), total: pager.total, pageSize: pager.pageSize, - getPage: pageIndex => pager.getPage(pageIndex).then(r => r.map(fn)) + getPage: (pageIndex, token) => pager.getPage(pageIndex, token).then(r => r.map(fn)) }; } @@ -138,8 +197,8 @@ export function mergePagers<T>(one: IPager<T>, other: IPager<T>): IPager<T> { firstPage: [...one.firstPage, ...other.firstPage], total: one.total + other.total, pageSize: one.pageSize + other.pageSize, - getPage(pageIndex: number): TPromise<T[]> { - return TPromise.join([one.getPage(pageIndex), other.getPage(pageIndex)]) + getPage(pageIndex: number, token): Thenable<T[]> { + return TPromise.join([one.getPage(pageIndex, token), other.getPage(pageIndex, token)]) .then(([onePage, otherPage]) => [...onePage, ...otherPage]); } }; diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index 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..5590211c6d5 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], }; } } @@ -74,25 +76,26 @@ define([], function () { function getDuration(from, to) { const entries = global._performanceEntries; - let name = from; - let startTime = 0; - for (let i = 0; i < entries.length; i += 4) { - if (entries[i + 1] === name) { - if (name === from) { - // found `from` (start of interval) - name = to; - startTime = entries[i + 2]; + let target = to; + let endTime = 0; + for (let i = entries.length - 1; i >= 0; i -= 5) { + if (entries[i - 3] === target) { + if (target === to) { + // found `to` (end of interval) + endTime = entries[i - 2]; + target = from; } else { - // from `to` (end of interval) - return entries[i + 2] - startTime; + return endTime - entries[i - 2]; } } } 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 +124,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 141b17598b2..ac6103d1ae0 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -5,38 +5,48 @@ '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 } from 'vs/base/common/platform'; +import { isLinux, isWindows } from 'vs/base/common/platform'; +import { CharCode } from 'vs/base/common/charCode'; -export function getComparisonKey(resource: uri): string { +export function getComparisonKey(resource: URI): string { return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString(); } -export function hasToIgnoreCase(resource: uri): boolean { +export function hasToIgnoreCase(resource: URI): 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.scheme === Schemas.file ? !isLinux : true; + return resource && resource.scheme === Schemas.file ? !isLinux : true; } -export function basenameOrAuthority(resource: uri): string { - return paths.basename(resource.path) || resource.authority; +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') { - return paths.isEqualOrParent(resource.fsPath, candidate.fsPath, ignoreCase); +/** + * Tests wheter a `candidate` URI is a parent or equal of a given `base` URI. + * @param base A uri which is "longer" + * @param parentCandidate A uri which is "shorter" then `base` + */ +export function isEqualOrParent(base: URI, parentCandidate: URI, ignoreCase = hasToIgnoreCase(base)): boolean { + if (base.scheme === parentCandidate.scheme) { + if (base.scheme === Schemas.file) { + return paths.isEqualOrParent(fsPath(base), fsPath(parentCandidate), ignoreCase); + } + if (isEqualAuthority(base.authority, parentCandidate.authority, ignoreCase)) { + return paths.isEqualOrParent(base.path, parentCandidate.path, ignoreCase, '/'); } - - return paths.isEqualOrParent(resource.path, candidate.path, ignoreCase); } - return false; } -export function isEqual(first: uri, second: uri, ignoreCase?: boolean): boolean { +function isEqualAuthority(a1: string, a2: string, ignoreCase?: boolean) { + return a1 === a2 || ignoreCase && a1 && a2 && equalsIgnoreCase(a1, a2); +} + +export function isEqual(first: URI, second: URI, ignoreCase = hasToIgnoreCase(first)): boolean { const identityEquals = (first === second); if (identityEquals) { return true; @@ -53,25 +63,97 @@ 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(fsPath(resource), pathFragment)).path; + } else { + joinedPath = paths.join(resource.path, pathFragment); + } return resource.with({ path: joinedPath }); } -export function distinctParents<T>(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(fsPath(resource))).path; + } else { + normalizedPath = paths.normalize(resource.path); + } + return resource.with({ + path: normalizedPath + }); +} + +/** + * Returns the fsPath of an URI where the drive letter is not normalized. + * See #56403. + */ +function fsPath(uri: URI): string { + let value: string; + if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { + // unc path: file://shares/c$/far/boo + value = `//${uri.authority}${uri.path}`; + } else if ( + isWindows + && uri.path.charCodeAt(0) === CharCode.Slash + && (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z) + && uri.path.charCodeAt(2) === CharCode.Colon + ) { + value = uri.path.substr(1); + } else { + // other path + value = uri.path; + } + if (isWindows) { + value = value.replace(/\//g, '\\'); + } + return value; +} + +/** + * Returns true if the URI path is absolute. + */ +export function isAbsolutePath(resource: URI): boolean { + return paths.isAbsolute(resource.path); +} + +export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => URI): T[] { const distinctParents: T[] = []; for (let i = 0; i < items.length; i++) { const candidateResource = resourceAccessor(items[i]); @@ -90,3 +172,18 @@ export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => ur return distinctParents; } + +/** + * Tests wheter the given URL is a file URI created by `URI.parse` instead of `URI.file`. + * Such URI have no scheme or scheme that consist of a single letter (windows drive letter) + * @param candidate The URI to test + * @returns A corrected, real file URI if the input seems to be malformed. + * Undefined is returned if the input URI looks fine. + */ +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/strings.ts b/src/vs/base/common/strings.ts index 31dff819977..5cb6ea76754 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -685,3 +685,7 @@ export function containsUppercaseCharacter(target: string, ignoreEscapedChars = return target.toLowerCase() !== target; } + +export function uppercaseFirstLetter(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); +} \ No newline at end of file diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 412efc17388..971bd2f13b0 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -12,6 +12,13 @@ const _singleSlashStart = /^\//; const _doubleSlashStart = /^\/\//; function _validateUri(ret: URI): void { + + // scheme, must be set + if (!ret.scheme) { + // throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`); + console.warn(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`); + } + // scheme, https://tools.ietf.org/html/rfc3986#section-3.1 // ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) if (ret.scheme && !_schemePattern.test(ret.scheme)) { @@ -63,7 +70,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; /** * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986. - * This class is a simple parser which creates the basic component paths + * This class is a simple parser which creates the basic component parts * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation * and encoding. * @@ -74,8 +81,6 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; * | _____________________|__ * / \ / \ * urn:example:animal:ferret:nose - * - * */ export default class URI implements UriComponents { @@ -159,17 +164,38 @@ export default class URI implements UriComponents { /** * Returns a string representing the corresponding file system path of this URI. - * Will handle UNC paths and normalize windows drive letters to lower-case. Also - * uses the platform specific path separator. Will *not* validate the path for - * invalid characters and semantics. Will *not* look at the scheme of this URI. + * Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the + * platform specific path separator. + * + * * Will *not* validate the path for invalid characters and semantics. + * * Will *not* look at the scheme of this URI. + * * The result shall *not* be used for display purposes but for accessing a file on disk. + * + * + * The *difference* to `URI#path` is the use of the platform specific separator and the handling + * of UNC paths. See the below sample of a file-uri with an authority (UNC path). + * + * ```ts + const u = URI.parse('file://server/c$/folder/file.txt') + u.authority === 'server' + u.path === '/shares/c$/file.txt' + u.fsPath === '\\server\c$\folder\file.txt' + ``` + * + * Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path, + * namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working + * with URIs that represent files on disk (`file` scheme). */ get fsPath(): string { + // if (this.scheme !== 'file') { + // console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`); + // } return _makeFsPath(this); } // ---- modify to new ------------------------- - public with(change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { + public with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI { if (!change) { return this; @@ -216,6 +242,12 @@ export default class URI implements UriComponents { // ---- parse & validate ------------------------ + /** + * Creates a new URI from a string, e.g. `http://www.msft.com/some/path`, + * `file:///usr/home`, or `scheme:with/path`. + * + * @param value A string which represents an URI (see `URI#toString`). + */ public static parse(value: string): URI { const match = _regexp.exec(value); if (!match) { @@ -230,6 +262,27 @@ export default class URI implements UriComponents { ); } + /** + * Creates a new URI from a file system path, e.g. `c:\my\files`, + * `/usr/home`, or `\\server\share\some\path`. + * + * The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument + * as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as** + * `URI.parse('file://' + path)` because the path might contain characters that are + * interpreted (# and ?). See the following sample: + * ```ts + const good = URI.file('/coding/c#/project1'); + good.scheme === 'file'; + good.path === '/coding/c#/project1'; + good.fragment === ''; + const bad = URI.parse('file://' + '/coding/c#/project1'); + bad.scheme === 'file'; + bad.path === '/coding/c'; // path is now broken + bad.fragment === '/project1'; + ``` + * + * @param path A file system path (see `URI#fsPath`) + */ public static file(path: string): URI { let authority = _empty; @@ -257,7 +310,7 @@ export default class URI implements UriComponents { return new _URI('file', authority, path, _empty, _empty); } - public static from(components: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { + public static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { return new _URI( components.scheme, components.authority, @@ -270,6 +323,13 @@ export default class URI implements UriComponents { // ---- printing/externalize --------------------------- /** + * Creates a string presentation for this URI. It's guardeed that calling + * `URI.parse` with the result of this function creates an URI which is equal + * to this URI. + * + * * The result shall *not* be used for display purposes but for externalization or transport. + * * The result will be encoded using the percentage encoding and encoding happens mostly + * ignore the scheme-specific encoding rules. * * @param skipEncoding Do not encode the result, default is `false` */ diff --git a/src/vs/base/common/winjs.base.d.ts b/src/vs/base/common/winjs.base.d.ts index f5b941a8873..7cf7a842c11 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<TProgress = any> = (progress: TProgress) => void; -export declare class Promise<T = any, TProgress = any> { +export class Promise<T = any> { constructor( executor: ( resolve: (value: T | PromiseLike<T>) => void, - reject: (reason: any) => void, - progress: (progress: TProgress) => void) => void, + reject: (reason: any) => void) => void, oncancel?: () => void); public then<TResult1 = T, TResult2 = never>( onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null, - onprogress?: (progress: TProgress) => void): Promise<TResult1 | TResult2, TProgress>; + onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>; public done( onfulfilled?: (value: T) => void, - onrejected?: (reason: any) => void, - onprogress?: (progress: TProgress) => void): void; + onrejected?: (reason: any) => void): void; public cancel(): void; @@ -33,8 +29,6 @@ export declare class Promise<T = any, TProgress = any> { public static as<T, SomePromise extends PromiseLike<T>>(value: SomePromise): SomePromise; public static as<T>(value: T): Promise<T>; - public static is(value: any): value is PromiseLike<any>; - public static timeout(delay: number): Promise<void>; public static join<T1, T2>(promises: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>; @@ -56,9 +50,7 @@ export type TValueCallback<T = any> = (value: T | PromiseLike<T>) => 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/common/winjs.polyfill.promise.ts b/src/vs/base/common/winjs.polyfill.promise.ts index 6ebd06ea511..e1e094d6521 100644 --- a/src/vs/base/common/winjs.polyfill.promise.ts +++ b/src/vs/base/common/winjs.polyfill.promise.ts @@ -5,6 +5,7 @@ import { Promise as WinJSPromise } from './winjs.base'; import * as platform from 'vs/base/common/platform'; +import { isWinJSPromise } from 'vs/base/common/async'; /** * A polyfill for the native promises. The implementation is based on @@ -45,7 +46,7 @@ export class PolyfillPromise<T = any> implements Promise<T> { constructor(callback: (resolve: (value?: T) => void, reject: (err?: any) => void) => any); constructor(initOrPromise: WinJSPromise | ((resolve: (value?: T) => void, reject: (err?: any) => void) => any)) { - if (WinJSPromise.is(initOrPromise)) { + if (isWinJSPromise(initOrPromise)) { this._winjsPromise = initOrPromise; } else { this._winjsPromise = new WinJSPromise((resolve, reject) => { diff --git a/src/vs/base/node/crypto.ts b/src/vs/base/node/crypto.ts index b658da126b6..4951a5dcc09 100644 --- a/src/vs/base/node/crypto.ts +++ b/src/vs/base/node/crypto.ts @@ -32,7 +32,7 @@ export function checksum(path: string, sha1hash: string): TPromise<void> { input.once('error', done); input.once('end', done); hashStream.once('error', done); - hashStream.once('data', (data: NodeBuffer) => done(null, data.toString('hex'))); + hashStream.once('data', (data: Buffer) => done(null, data.toString('hex'))); }); return promise.then(hash => { diff --git a/src/vs/base/node/decoder.ts b/src/vs/base/node/decoder.ts index de145fc29b2..9cfb1094995 100644 --- a/src/vs/base/node/decoder.ts +++ b/src/vs/base/node/decoder.ts @@ -25,7 +25,7 @@ export class LineDecoder { this.remaining = null; } - public write(buffer: NodeBuffer): string[] { + public write(buffer: Buffer): string[] { let result: string[] = []; let value = this.remaining ? this.remaining + this.stringDecoder.write(buffer) diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 2c68f89b6ba..f5dc3ab5ffa 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -119,11 +119,11 @@ export function bomLength(encoding: string): number { return 0; } -export function decode(buffer: NodeBuffer, encoding: string): string { +export function decode(buffer: Buffer, encoding: string): string { return iconv.decode(buffer, toNodeEncoding(encoding)); } -export function encode(content: string | NodeBuffer, encoding: string, options?: { addBOM?: boolean }): NodeBuffer { +export function encode(content: string | Buffer, encoding: string, options?: { addBOM?: boolean }): Buffer { return iconv.encode(content, toNodeEncoding(encoding), options); } @@ -147,7 +147,7 @@ function toNodeEncoding(enc: string): string { return enc; } -export function detectEncodingByBOMFromBuffer(buffer: NodeBuffer, bytesRead: number): string { +export function detectEncodingByBOMFromBuffer(buffer: Buffer, bytesRead: number): string { if (!buffer || bytesRead < 2) { return null; } @@ -193,7 +193,7 @@ const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; /** * Guesses the encoding from buffer. */ -export function guessEncodingByBuffer(buffer: NodeBuffer): TPromise<string> { +export function guessEncodingByBuffer(buffer: Buffer): TPromise<string> { return toWinJsPromise(import('jschardet')).then(jschardet => { jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts index 82e1cfd0fde..3b0b3866f3c 100644 --- a/src/vs/base/node/extfs.ts +++ b/src/vs/base/node/extfs.ts @@ -15,6 +15,7 @@ import * as uuid from 'vs/base/common/uuid'; import { TPromise } from 'vs/base/common/winjs.base'; import { encode, encodeStream } from 'vs/base/node/encoding'; import * as flow from 'vs/base/node/flow'; +import { CancellationToken } from 'vs/base/common/cancellation'; const loop = flow.loop; @@ -129,7 +130,7 @@ function doCopyFile(source: string, target: string, mode: number, callback: (err reader.pipe(writer); } -export function mkdirp(path: string, mode?: number): TPromise<boolean> { +export function mkdirp(path: string, mode?: number, token?: CancellationToken): TPromise<boolean> { const mkdir = () => { return nfcall(fs.mkdir, path, mode).then(null, (mkdirErr: NodeJS.ErrnoException) => { @@ -160,6 +161,11 @@ export function mkdirp(path: string, mode?: number): TPromise<boolean> { // recursively mkdir return mkdir().then(null, (err: NodeJS.ErrnoException) => { + // Respect cancellation + if (token && token.isCancellationRequested) { + return TPromise.as(false); + } + // ENOENT: a parent folder does not exist yet, continue // to create the parent folder and then try again. if (err.code === 'ENOENT') { @@ -365,7 +371,7 @@ export interface IWriteFileOptions { } let canFlush = true; -export function writeFileAndFlush(path: string, data: string | NodeBuffer | NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void { +export function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void { options = ensureOptions(options); if (typeof data === 'string' || Buffer.isBuffer(data)) { @@ -466,7 +472,7 @@ function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, // not in some cache. // // See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194 -function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: IWriteFileOptions, callback: (error?: Error) => void): void { +function doWriteFileAndFlush(path: string, data: string | Buffer, options: IWriteFileOptions, callback: (error?: Error) => void): void { if (options.encoding) { data = encode(data, options.encoding.charset, { addBOM: options.encoding.addBOM }); } @@ -503,7 +509,7 @@ function doWriteFileAndFlush(path: string, data: string | NodeBuffer, options: I }); } -export function writeFileAndFlushSync(path: string, data: string | NodeBuffer, options?: IWriteFileOptions): void { +export function writeFileAndFlushSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void { options = ensureOptions(options); if (options.encoding) { 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<IPaths>(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/pfs.ts b/src/vs/base/node/pfs.ts index 6172b9a30ad..5678be80081 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -98,7 +98,7 @@ export function readFile(path: string, encoding?: string): TPromise<Buffer | str const writeFilePathQueue: { [path: string]: Queue<void> } = Object.create(null); export function writeFile(path: string, data: string, options?: extfs.IWriteFileOptions): TPromise<void>; -export function writeFile(path: string, data: NodeBuffer, options?: extfs.IWriteFileOptions): TPromise<void>; +export function writeFile(path: string, data: Buffer, options?: extfs.IWriteFileOptions): TPromise<void>; export function writeFile(path: string, data: Uint8Array, options?: extfs.IWriteFileOptions): TPromise<void>; export function writeFile(path: string, data: NodeJS.ReadableStream, options?: extfs.IWriteFileOptions): TPromise<void>; export function writeFile(path: string, data: any, options?: extfs.IWriteFileOptions): TPromise<void> { diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index ebe0dff4cc4..53f90f70954 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -6,17 +6,16 @@ import * as path from 'path'; import * as cp from 'child_process'; -import { fork } from 'vs/base/node/stdFork'; 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<T> = (progress: T) => void; @@ -54,7 +53,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 }; @@ -74,7 +73,6 @@ export function getWindowsShell(): string { export abstract class AbstractProcess<TProgressData> { private cmd: string; - private module: string; private args: string[]; private options: CommandOptions | ForkOptions; protected shell: boolean; @@ -107,18 +105,12 @@ export abstract class AbstractProcess<TProgressData> { public constructor(executable: Executable); public constructor(cmd: string, args: string[], shell: boolean, options: CommandOptions); - public constructor(module: string, args: string[], options: ForkOptions); - public constructor(arg1: string | Executable, arg2?: string[], arg3?: boolean | ForkOptions, arg4?: CommandOptions) { - if (arg4) { + public constructor(arg1: string | Executable, arg2?: string[], arg3?: boolean, arg4?: CommandOptions) { + if (arg2 !== void 0 && arg3 !== void 0 && arg4 !== void 0) { this.cmd = <string>arg1; this.args = arg2; - this.shell = <boolean>arg3; + this.shell = arg3; this.options = arg4; - } else if (arg3 && arg2) { - this.module = <string>arg1; - this.args = arg2; - this.shell = false; - this.options = <ForkOptions>arg3; } else { let executable = <Executable>arg1; this.cmd = executable.command; @@ -233,24 +225,6 @@ export abstract class AbstractProcess<TProgressData> { } else { if (this.cmd) { childProcess = cp.spawn(this.cmd, this.args, this.options); - } else if (this.module) { - this.childProcessPromise = new TPromise<cp.ChildProcess>((c, e) => { - fork(this.module, this.args, <ForkOptions>this.options, (error: any, childProcess: cp.ChildProcess) => { - if (error) { - e(error); - ee({ terminated: this.terminateRequested, error: error }); - return; - } - this.childProcess = childProcess; - if (this.pidResolve) { - this.pidResolve(Types.isNumber(childProcess.pid) ? childProcess.pid : -1); - this.pidResolve = undefined; - } - this.childProcess.on('close', closeHandler); - this.handleSpawn(childProcess, cc, pp, ee, false); - c(childProcess); - }); - }); } } if (childProcess) { @@ -345,7 +319,6 @@ export class LineProcess extends AbstractProcess<LineData> { public constructor(executable: Executable); public constructor(cmd: string, args: string[], shell: boolean, options: CommandOptions); - public constructor(module: string, args: string[], options: ForkOptions); public constructor(arg1: string | Executable, arg2?: string[], arg3?: boolean | ForkOptions, arg4?: CommandOptions) { super(<any>arg1, arg2, <any>arg3, arg4); } 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<ProcessItem> { // 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 deleted file mode 100644 index c7c36559465..00000000000 --- a/src/vs/base/node/stdFork.ts +++ /dev/null @@ -1,140 +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 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'; - -export interface IForkOpts { - cwd?: string; - env?: any; - encoding?: string; - execArgv?: string[]; -} - -function makeRandomHexString(length: number): string { - let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; - let result = ''; - for (let i = 0; i < length; i++) { - let idx = Math.floor(chars.length * Math.random()); - result += chars[idx]; - } - return result; -} - -function generatePipeName(): string { - let randomName = 'vscode-std-' + makeRandomHexString(40); - if (process.platform === 'win32') { - return '\\\\.\\pipe\\' + randomName + '-sock'; - } - - // Mac/Unix: use socket file - return path.join(os.tmpdir(), randomName + '.sock'); -} - -function generatePatchedEnv(env: any, stdInPipeName: string, stdOutPipeName: string, stdErrPipeName: string): any { - // Set the two unique pipe names and the electron flag as process env - - let newEnv: any = {}; - for (let key in env) { - newEnv[key] = env[key]; - } - - newEnv['STDIN_PIPE_NAME'] = stdInPipeName; - newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName; - newEnv['STDERR_PIPE_NAME'] = stdErrPipeName; - newEnv['ELECTRON_RUN_AS_NODE'] = '1'; - - return newEnv; -} - -export function fork(modulePath: string, args: string[], options: IForkOpts, callback: (error: any, cp: cp.ChildProcess) => void): void { - - let callbackCalled = false; - let resolve = (result: cp.ChildProcess) => { - if (callbackCalled) { - return; - } - callbackCalled = true; - callback(null, result); - }; - let reject = (err: any) => { - if (callbackCalled) { - return; - } - callbackCalled = true; - callback(err, null); - }; - - // Generate three unique pipe names - let stdInPipeName = generatePipeName(); - let stdOutPipeName = generatePipeName(); - let stdErrPipeName = generatePipeName(); - - let newEnv = generatePatchedEnv(options.env || process.env, stdInPipeName, stdOutPipeName, stdErrPipeName); - - let childProcess: cp.ChildProcess; - - // Begin listening to stderr pipe - let stdErrServer = net.createServer((stdErrStream) => { - // From now on the childProcess.stderr is available for reading - childProcess.stderr = stdErrStream; - }); - stdErrServer.listen(stdErrPipeName); - - // Begin listening to stdout pipe - let stdOutServer = net.createServer((stdOutStream) => { - // The child process will write exactly one chunk with content `ready` when it has installed a listener to the stdin pipe - - stdOutStream.once('data', (chunk: Buffer) => { - // The child process is sending me the `ready` chunk, time to connect to the stdin pipe - childProcess.stdin = <any>net.connect(stdInPipeName); - - // From now on the childProcess.stdout is available for reading - childProcess.stdout = stdOutStream; - - resolve(childProcess); - }); - }); - stdOutServer.listen(stdOutPipeName); - - let serverClosed = false; - let closeServer = () => { - if (serverClosed) { - return; - } - - serverClosed = true; - process.removeListener('exit', closeServer); - stdOutServer.close(); - stdErrServer.close(); - }; - - // Create the process - let bootstrapperPath = (uri.parse(require.toUrl('./stdForkStart.js')).fsPath); - childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), { - silent: true, - cwd: options.cwd, - env: newEnv, - execArgv: options.execArgv - }); - - childProcess.once('error', (err: Error) => { - closeServer(); - reject(err); - }); - - childProcess.once('exit', (err: Error) => { - closeServer(); - reject(err); - }); - - // On vscode exit still close server #7758 - process.once('exit', closeServer); -} diff --git a/src/vs/base/node/stdForkStart.js b/src/vs/base/node/stdForkStart.js deleted file mode 100644 index 42226f9a08d..00000000000 --- a/src/vs/base/node/stdForkStart.js +++ /dev/null @@ -1,197 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const net = require('net'); -const fs = require('fs'); -// const stream = require('stream'); -// const util = require('util'); - -var ENABLE_LOGGING = false; - -var log = (function () { - if (!ENABLE_LOGGING) { - return function () { }; - } - var isFirst = true; - var LOG_LOCATION = 'C:\\stdFork.log'; - return function log(str) { - if (isFirst) { - isFirst = false; - fs.writeFileSync(LOG_LOCATION, str + '\n'); - return; - } - fs.appendFileSync(LOG_LOCATION, str + '\n'); - }; -})(); - -var stdInPipeName = process.env['STDIN_PIPE_NAME']; -var stdOutPipeName = process.env['STDOUT_PIPE_NAME']; -var stdErrPipeName = process.env['STDERR_PIPE_NAME']; - -log('STDIN_PIPE_NAME: ' + stdInPipeName); -log('STDOUT_PIPE_NAME: ' + stdOutPipeName); -log('STDERR_PIPE_NAME: ' + stdErrPipeName); -log('ELECTRON_RUN_AS_NODE: ' + process.env['ELECTRON_RUN_AS_NODE']); - -// stdout redirection to named pipe -(function () { - log('Beginning stdout redirection...'); - - // Create a writing stream to the stdout pipe - var stdOutStream = net.connect(stdOutPipeName); - - // unref stdOutStream to behave like a normal standard out - stdOutStream.unref(); - - // handle process.stdout - process.__defineGetter__('stdout', function () { return stdOutStream; }); - - // Create a writing stream to the stderr pipe - var stdErrStream = net.connect(stdErrPipeName); - - // unref stdErrStream to behave like a normal standard out - stdErrStream.unref(); - - // handle process.stderr - process.__defineGetter__('stderr', function () { return stdErrStream; }); - - var fsWriteSyncString = function (fd, str, position, encoding) { - // fs.writeSync(fd, string[, position[, encoding]]); - var buf = Buffer.from(str, encoding || 'utf8'); - return fsWriteSyncBuffer(fd, buf, 0, buf.length); - }; - - var fsWriteSyncBuffer = function (fd, buffer, off, len/* , position */) { - off = Math.abs(off | 0); - len = Math.abs(len | 0); - - // fs.writeSync(fd, buffer, offset, length[, position]); - var buffer_length = buffer.length; - - if (off > buffer_length) { - throw new Error('offset out of bounds'); - } - if (len > buffer_length) { - throw new Error('length out of bounds'); - } - if (((off + len) | 0) < off) { - throw new Error('off + len overflow'); - } - if (buffer_length - off < len) { - // Asking for more than is left over in the buffer - throw new Error('off + len > buffer.length'); - } - - var slicedBuffer = buffer; - if (off !== 0 || len !== buffer_length) { - slicedBuffer = buffer.slice(off, off + len); - } - - if (fd === 1) { - stdOutStream.write(slicedBuffer); - } else { - stdErrStream.write(slicedBuffer); - } - return slicedBuffer.length; - }; - - // handle fs.writeSync(1, ...) and fs.writeSync(2, ...) - var originalWriteSync = fs.writeSync; - fs.writeSync = function (fd, data/* , position, encoding */) { - if (fd !== 1 && fd !== 2) { - return originalWriteSync.apply(fs, arguments); - } - // usage: - // fs.writeSync(fd, buffer, offset, length[, position]); - // OR - // fs.writeSync(fd, string[, position[, encoding]]); - - if (data instanceof Buffer) { - return fsWriteSyncBuffer.apply(null, arguments); - } - - // For compatibility reasons with fs.writeSync, writing null will write "null", etc - if (typeof data !== 'string') { - data += ''; - } - - return fsWriteSyncString.apply(null, arguments); - }; - - log('Finished defining process.stdout, process.stderr and fs.writeSync'); -})(); - -// stdin redirection to named pipe -(function () { - - // Begin listening to stdin pipe - var server = net.createServer(function (stream) { - // Stop accepting new connections, keep the existing one alive - server.close(); - - log('Parent process has connected to my stdin. All should be good now.'); - - // handle process.stdin - process.__defineGetter__('stdin', function () { - return stream; - }); - - // Remove myself from process.argv - process.argv.splice(1, 1); - - // Load the actual program - var program = process.argv[1]; - log('Loading program: ' + program); - - // Unset the custom environmental variables that should not get inherited - delete process.env['STDIN_PIPE_NAME']; - delete process.env['STDOUT_PIPE_NAME']; - delete process.env['STDERR_PIPE_NAME']; - delete process.env['ELECTRON_RUN_AS_NODE']; - - require(program); - - log('Finished loading program.'); - - var stdinIsReferenced = true; - var timer = setInterval(function () { - var listenerCount = ( - stream.listeners('data').length + - stream.listeners('end').length + - stream.listeners('close').length + - stream.listeners('error').length - ); - // log('listenerCount: ' + listenerCount); - if (listenerCount <= 1) { - // No more "actual" listeners, only internal node - if (stdinIsReferenced) { - stdinIsReferenced = false; - // log('unreferencing stream!!!'); - stream.unref(); - } - } else { - // There are "actual" listeners - if (!stdinIsReferenced) { - stdinIsReferenced = true; - stream.ref(); - } - } - // log( - // '' + stream.listeners('data').length + - // ' ' + stream.listeners('end').length + - // ' ' + stream.listeners('close').length + - // ' ' + stream.listeners('error').length - // ); - }, 1000); - timer.unref(); - }); - - - server.listen(stdInPipeName, function () { - // signal via stdout that the parent process can now begin writing to stdin pipe - process.stdout.write('ready'); - }); - -})(); diff --git a/src/vs/base/node/stream.ts b/src/vs/base/node/stream.ts index a9ba27bb005..a1919336918 100644 --- a/src/vs/base/node/stream.ts +++ b/src/vs/base/node/stream.ts @@ -10,7 +10,7 @@ import * as fs from 'fs'; import { TPromise } from 'vs/base/common/winjs.base'; export interface ReadResult { - buffer: NodeBuffer; + buffer: Buffer; bytesRead: number; } @@ -24,7 +24,7 @@ export function readExactlyByFile(file: string, totalBytes: number): TPromise<Re return error(err); } - function end(err: Error, resultBuffer: NodeBuffer, bytesRead: number): void { + function end(err: Error, resultBuffer: Buffer, bytesRead: number): void { fs.close(fd, closeError => { if (closeError) { return error(closeError); diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index 652fb21c73b..9348104b49a 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -7,11 +7,14 @@ import * as nls from 'vs/nls'; import * as path from 'path'; import { createWriteStream, WriteStream } from 'fs'; import { Readable } from 'stream'; -import { nfcall, ninvoke, SimpleThrottler } from 'vs/base/common/async'; +import { nfcall, ninvoke, SimpleThrottler, createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; import { mkdirp, rimraf } from 'vs/base/node/pfs'; import { TPromise } from 'vs/base/common/winjs.base'; import { open as _openZip, Entry, ZipFile } from 'yauzl'; +import * as yazl from 'yazl'; import { ILogService } from 'vs/platform/log/common/log'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { once } from 'vs/base/common/event'; export interface IExtractOptions { overwrite?: boolean; @@ -69,7 +72,7 @@ function toExtractError(err: Error): ExtractError { return new ExtractError(type, err); } -function extractEntry(stream: Readable, fileName: string, mode: number, targetPath: string, options: IOptions): TPromise<void> { +function extractEntry(stream: Readable, fileName: string, mode: number, targetPath: string, options: IOptions, token: CancellationToken): TPromise<void> { const dirName = path.dirname(fileName); const targetDirName = path.join(targetPath, dirName); if (targetDirName.indexOf(targetPath) !== 0) { @@ -78,72 +81,86 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa const targetFileName = path.join(targetPath, fileName); let istream: WriteStream; - return mkdirp(targetDirName).then(() => new TPromise((c, e) => { + + once(token.onCancellationRequested)(() => { + if (istream) { + istream.close(); + } + }); + + return mkdirp(targetDirName, void 0, token).then(() => new TPromise((c, e) => { + if (token.isCancellationRequested) { + return; + } + istream = createWriteStream(targetFileName, { mode }); istream.once('close', () => c(null)); istream.once('error', e); stream.once('error', e); stream.pipe(istream); - }, () => { - if (istream) { - istream.close(); - } })); } -function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, logService: ILogService): TPromise<void> { - let isCanceled = false; - let last = TPromise.wrap<any>(null); +function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, logService: ILogService): CancelablePromise<void> { + let last = createCancelablePromise(() => Promise.resolve(null)); let extractedEntriesCount = 0; - return new TPromise((c, e) => { - const throttler = new SimpleThrottler(); + return createCancelablePromise(token => { - const readNextEntry = () => { - extractedEntriesCount++; - zipfile.readEntry(); - }; - - zipfile.once('error', e); - zipfile.once('close', () => last.then(() => { - if (isCanceled || zipfile.entryCount === extractedEntriesCount) { - c(null); - } else { - e(new ExtractError('Incomplete', new Error(nls.localize('incompleteExtract', "Incomplete. Found {0} of {1} entries", extractedEntriesCount, zipfile.entryCount)))); - } - }, e)); - zipfile.readEntry(); - zipfile.on('entry', (entry: Entry) => { - - if (isCanceled) { - return; - } - - if (!options.sourcePathRegex.test(entry.fileName)) { - readNextEntry(); - return; - } - - const fileName = entry.fileName.replace(options.sourcePathRegex, ''); - - // directory file names end with '/' - if (/\/$/.test(fileName)) { - const targetFileName = path.join(targetPath, fileName); - last = mkdirp(targetFileName).then(() => readNextEntry()); - return; - } - - const stream = ninvoke(zipfile, zipfile.openReadStream, entry); - const mode = modeFromEntry(entry); - - last = throttler.queue(() => stream.then(stream => extractEntry(stream, fileName, mode, targetPath, options).then(() => readNextEntry()))); + once(token.onCancellationRequested)(() => { + logService.debug(targetPath, 'Cancelled.'); + last.cancel(); + zipfile.close(); }); - }, () => { - logService.debug(targetPath, 'Cancelled.'); - isCanceled = true; - last.cancel(); - zipfile.close(); - }).then(null, err => TPromise.wrapError(toExtractError(err))); + + return new TPromise((c, e) => { + const throttler = new SimpleThrottler(); + + const readNextEntry = (token: CancellationToken) => { + if (token.isCancellationRequested) { + return; + } + + extractedEntriesCount++; + zipfile.readEntry(); + }; + + zipfile.once('error', e); + zipfile.once('close', () => last.then(() => { + if (token.isCancellationRequested || zipfile.entryCount === extractedEntriesCount) { + c(null); + } else { + e(new ExtractError('Incomplete', new Error(nls.localize('incompleteExtract', "Incomplete. Found {0} of {1} entries", extractedEntriesCount, zipfile.entryCount)))); + } + }, e)); + zipfile.readEntry(); + zipfile.on('entry', (entry: Entry) => { + + if (token.isCancellationRequested) { + return; + } + + if (!options.sourcePathRegex.test(entry.fileName)) { + readNextEntry(token); + return; + } + + const fileName = entry.fileName.replace(options.sourcePathRegex, ''); + + // directory file names end with '/' + if (/\/$/.test(fileName)) { + const targetFileName = path.join(targetPath, fileName); + last = createCancelablePromise(token => mkdirp(targetFileName, void 0, token).then(() => readNextEntry(token))); + return; + } + + const stream = ninvoke(zipfile, zipfile.openReadStream, entry); + const mode = modeFromEntry(entry); + + last = createCancelablePromise(token => throttler.queue(() => stream.then(stream => extractEntry(stream, fileName, mode, targetPath, options, token).then(() => readNextEntry(token))))); + }); + }); + }); } function openZip(zipFile: string, lazy: boolean = false): TPromise<ZipFile> { @@ -151,6 +168,27 @@ function openZip(zipFile: string, lazy: boolean = false): TPromise<ZipFile> { .then(null, err => TPromise.wrapError(toExtractError(err))); } +export interface IFile { + path: string; + contents?: Buffer | string; + localPath?: string; +} + +export function zip(zipPath: string, files: IFile[]): TPromise<string> { + return new TPromise<string>((c, e) => { + const zip = new yazl.ZipFile(); + files.forEach(f => f.contents ? zip.addBuffer(typeof f.contents === 'string' ? Buffer.from(f.contents, 'utf8') : f.contents, f.path) : zip.addFile(f.localPath, f.path)); + zip.end(); + + const zipStream = createWriteStream(zipPath); + zip.outputStream.pipe(zipStream); + + zip.outputStream.once('error', e); + zipStream.once('error', e); + zipStream.once('finish', () => c(zipPath)); + }); +} + export function extract(zipPath: string, targetPath: string, options: IExtractOptions = {}, logService: ILogService): TPromise<void> { const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : ''); diff --git a/src/vs/base/parts/contextmenu/common/contextmenu.ts b/src/vs/base/parts/contextmenu/common/contextmenu.ts new file mode 100644 index 00000000000..6998c66916f --- /dev/null +++ b/src/vs/base/parts/contextmenu/common/contextmenu.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export interface ICommonContextMenuItem { + label?: string; + + type?: 'normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'; + + accelerator?: string; + + enabled?: boolean; + visible?: boolean; + checked?: boolean; +} + +export interface ISerializableContextMenuItem extends ICommonContextMenuItem { + id: number; + submenu?: ISerializableContextMenuItem[]; +} + +export interface IContextMenuItem extends ICommonContextMenuItem { + click?: (event: IContextMenuEvent) => void; + submenu?: IContextMenuItem[]; +} + +export interface IContextMenuEvent { + shiftKey?: boolean; + ctrlKey?: boolean; + altKey?: boolean; + metaKey?: boolean; +} + +export interface IPopupOptions { + x?: number; + y?: number; + positioningItem?: number; + onHide?: () => void; +} + +export const CONTEXT_MENU_CHANNEL = 'vscode:contextmenu'; +export const CONTEXT_MENU_CLOSE_CHANNEL = 'vscode:onCloseContextMenu'; \ No newline at end of file diff --git a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts new file mode 100644 index 00000000000..f5e4d2b8dfe --- /dev/null +++ b/src/vs/base/parts/contextmenu/electron-browser/contextmenu.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. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { ipcRenderer, Event } from 'electron'; +import { IContextMenuItem, ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions, IContextMenuEvent } from 'vs/base/parts/contextmenu/common/contextmenu'; + +let onClickChannelIds = 0; + +export function popup(items: IContextMenuItem[], options?: IPopupOptions): void { + const processedItems: IContextMenuItem[] = []; + + const onClickChannel = `vscode:onContextMenu${onClickChannelIds++}`; + const onClickChannelHandler = (event: Event, itemId: number, context: IContextMenuEvent) => processedItems[itemId].click(context); + + ipcRenderer.once(onClickChannel, onClickChannelHandler); + ipcRenderer.once(CONTEXT_MENU_CLOSE_CHANNEL, () => { + ipcRenderer.removeListener(onClickChannel, onClickChannelHandler); + + if (options && options.onHide) { + options.onHide(); + } + }); + + ipcRenderer.send(CONTEXT_MENU_CHANNEL, items.map(item => createItem(item, processedItems)), onClickChannel, options); +} + +function createItem(item: IContextMenuItem, processedItems: IContextMenuItem[]): ISerializableContextMenuItem { + const serializableItem = { + id: processedItems.length, + label: item.label, + type: item.type, + accelerator: item.accelerator, + checked: item.checked, + enabled: typeof item.enabled === 'boolean' ? item.enabled : true, + visible: typeof item.visible === 'boolean' ? item.visible : true + } as ISerializableContextMenuItem; + + processedItems.push(item); + + // Submenu + if (Array.isArray(item.submenu)) { + serializableItem.submenu = item.submenu.map(submenuItem => createItem(submenuItem, processedItems)); + } + + return serializableItem; +} \ No newline at end of file diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts new file mode 100644 index 00000000000..5afeaad9c1c --- /dev/null +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { Menu, MenuItem, BrowserWindow, Event, ipcMain } from 'electron'; +import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions } from 'vs/base/parts/contextmenu/common/contextmenu'; + +export function registerContextMenuListener(): void { + ipcMain.on(CONTEXT_MENU_CHANNEL, (event: Event, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { + const menu = createMenu(event, onClickChannel, items); + + menu.popup({ + window: BrowserWindow.fromWebContents(event.sender), + x: options ? options.x : void 0, + y: options ? options.y : void 0, + positioningItem: options ? options.positioningItem : void 0, + callback: () => { + event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL); + } + }); + }); +} + +function createMenu(event: Event, onClickChannel: string, items: ISerializableContextMenuItem[]): Menu { + const menu = new Menu(); + + items.forEach(item => { + let menuitem: MenuItem; + + // Separator + if (item.type === 'separator') { + menuitem = new MenuItem({ + type: item.type, + }); + } + + // Sub Menu + else if (Array.isArray(item.submenu)) { + menuitem = new MenuItem({ + submenu: createMenu(event, onClickChannel, item.submenu), + label: item.label + }); + } + + // Normal Menu Item + else { + menuitem = new MenuItem({ + label: item.label, + type: item.type, + accelerator: item.accelerator, + checked: item.checked, + enabled: item.enabled, + visible: item.visible, + click: (menuItem, win, contextmenuEvent) => event.sender.send(onClickChannel, item.id, contextmenuEvent) + }); + } + + menu.append(menuitem); + }); + + return menu; +} \ No newline at end of file diff --git a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts index 7e3ce8ddd1e..a61c756d6f0 100644 --- a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts +++ b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { fromNodeEventEmitter } from 'vs/base/common/event'; -import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; -import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; +import { IPCClient } from 'vs/base/parts/ipc/node/ipc'; +import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcRenderer } from 'electron'; export class Client extends IPCClient { private static createProtocol(): Protocol { - const onMessage = fromNodeEventEmitter<string>(ipcRenderer, 'ipc:message', (_, message) => message); + const onMessage = fromNodeEventEmitter<string>(ipcRenderer, 'ipc:message', (_, message: string) => message); ipcRenderer.send('ipc:hello'); return new Protocol(ipcRenderer, onMessage); } diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts index af67f10d6ca..47da2f529f5 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron-main.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Event, filterEvent, mapEvent, fromNodeEventEmitter } from 'vs/base/common/event'; -import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc'; -import { Protocol } from 'vs/base/parts/ipc/common/ipc.electron'; +import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/node/ipc'; +import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcMain } from 'electron'; interface WebContents extends Electron.WebContents { @@ -17,8 +17,8 @@ interface IIPCEvent { message: string; } -function createScopedOnMessageEvent(senderId: number): Event<any> { - const onMessage = fromNodeEventEmitter<IIPCEvent>(ipcMain, 'ipc:message', (event, message) => ({ event, message })); +function createScopedOnMessageEvent(senderId: number): Event<string> { + const onMessage = fromNodeEventEmitter<IIPCEvent>(ipcMain, 'ipc:message', (event, message: string) => ({ event, message })); const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.getId() === senderId); return mapEvent(onMessageFromSender, ({ message }) => message); } diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 3d863aa9e0b..9c709880779 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -10,14 +10,19 @@ import { Delayer } from 'vs/base/common/async'; import { deepClone, assign } from 'vs/base/common/objects'; import { Emitter, fromNodeEventEmitter, Event } from 'vs/base/common/event'; import { createQueuedSender } from 'vs/base/node/processes'; -import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient, IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient, IChannel } from 'vs/base/parts/ipc/node/ipc'; import { isRemoteConsoleLog, log } from 'vs/base/node/console'; +/** + * This implementation doesn't perform well since it uses base64 encoding for buffers. + * We should move all implementations to use named ipc.net, so we stop depending on cp.fork. + */ + export class Server extends IPCServer { constructor() { super({ - send: r => { try { process.send(r); } catch (e) { /* not much to do */ } }, - onMessage: fromNodeEventEmitter(process, 'message', msg => msg) + send: r => { try { process.send(r.toString('base64')); } catch (e) { /* not much to do */ } }, + onMessage: fromNodeEventEmitter(process, 'message', msg => Buffer.from(msg, 'base64')) }); process.once('disconnect', () => this.dispose()); @@ -107,9 +112,8 @@ export class Client implements IChannelClient, IDisposable { const channel = this.channels[channelName] || (this.channels[channelName] = this.client.getChannel(channelName)); const request: TPromise<void> = channel.call(name, arg); - // Progress doesn't propagate across 'then', we need to create a promise wrapper - const result = new TPromise<void>((c, e, p) => { - request.then(c, e, p).done(() => { + const result = new TPromise<void>((c, e) => { + request.then(c, e).done(() => { if (!this.activeRequests) { return; } @@ -186,7 +190,7 @@ export class Client implements IChannelClient, IDisposable { this.child = fork(this.modulePath, args, forkOpts); - const onMessageEmitter = new Emitter<any>(); + const onMessageEmitter = new Emitter<Buffer>(); const onRawMessage = fromNodeEventEmitter(this.child, 'message', msg => msg); onRawMessage(msg => { @@ -198,11 +202,11 @@ export class Client implements IChannelClient, IDisposable { } // Anything else goes to the outside - onMessageEmitter.fire(msg); + onMessageEmitter.fire(Buffer.from(msg, 'base64')); }); const sender = this.options.useQueue ? createQueuedSender(this.child) : this.child; - const send = r => this.child && this.child.connected && sender.send(r); + const send = (r: Buffer) => this.child && this.child.connected && sender.send(r.toString('base64')); const onMessage = onMessageEmitter.event; const protocol = { send, onMessage }; diff --git a/src/vs/base/parts/ipc/common/ipc.electron.ts b/src/vs/base/parts/ipc/node/ipc.electron.ts similarity index 52% rename from src/vs/base/parts/ipc/common/ipc.electron.ts rename to src/vs/base/parts/ipc/node/ipc.electron.ts index 43422887433..56236c39d1c 100644 --- a/src/vs/base/parts/ipc/common/ipc.electron.ts +++ b/src/vs/base/parts/ipc/node/ipc.electron.ts @@ -4,29 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { Event, Emitter } from 'vs/base/common/event'; +/** + * This implementation doesn't perform well since it uses base64 encoding for buffers. + * Electron 3.0 should have suport for buffers in IPC: https://github.com/electron/electron/pull/13055 + */ + export interface Sender { - send(channel: string, ...args: any[]): void; + send(channel: string, msg: string): void; } export class Protocol implements IMessagePassingProtocol { private listener: IDisposable; - private _onMessage: Event<any>; - get onMessage(): Event<any> { return this._onMessage; } + private _onMessage = new Emitter<Buffer>(); + get onMessage(): Event<Buffer> { return this._onMessage.event; } - constructor(private sender: Sender, onMessageEvent: Event<any>) { - const emitter = new Emitter<any>(); - onMessageEvent(msg => emitter.fire(msg)); - this._onMessage = emitter.event; + constructor(private sender: Sender, onMessageEvent: Event<string>) { + onMessageEvent(msg => this._onMessage.fire(Buffer.from(msg, 'base64'))); } - send(message: any): void { + send(message: Buffer): void { try { - this.sender.send('ipc:message', message); + this.sender.send('ipc:message', message.toString('base64')); } catch (e) { // systems are going down } diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index e82503e1eb0..cd6474e039c 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -8,7 +8,7 @@ import { Socket, Server as NetServer, createConnection, createServer } from 'net'; import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter, once, mapEvent, fromNodeEventEmitter } from 'vs/base/common/event'; -import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { join } from 'path'; import { tmpdir } from 'os'; import { generateUuid } from 'vs/base/common/uuid'; @@ -25,9 +25,17 @@ export function generateRandomPipeName(): string { } } +/** + * A message has the following format: + * + * [bodyLen|message] + * [header^|data^^^] + * [u32be^^|buffer^] + */ + export class Protocol implements IDisposable, IMessagePassingProtocol { - private static readonly _headerLen = 5; + private static readonly _headerLen = 4; private _isDisposed: boolean; private _chunks: Buffer[]; @@ -37,8 +45,8 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { private _socketEndListener: () => void; private _socketCloseListener: () => void; - private _onMessage = new Emitter<any>(); - readonly onMessage: Event<any> = this._onMessage.event; + private _onMessage = new Emitter<Buffer>(); + readonly onMessage: Event<Buffer> = this._onMessage.event; private _onClose = new Emitter<void>(); readonly onClose: Event<void> = this._onClose.event; @@ -51,7 +59,6 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { const state = { readHead: true, - bodyIsJson: false, bodyLen: -1, }; @@ -68,8 +75,7 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { if (totalLength >= Protocol._headerLen) { const all = Buffer.concat(this._chunks); - state.bodyIsJson = all.readInt8(0) === 1; - state.bodyLen = all.readInt32BE(1); + state.bodyLen = all.readUInt32BE(0); state.readHead = false; const rest = all.slice(Protocol._headerLen); @@ -87,21 +93,17 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { if (totalLength >= state.bodyLen) { const all = Buffer.concat(this._chunks); - let message = all.toString('utf8', 0, state.bodyLen); - if (state.bodyIsJson) { - message = JSON.parse(message); - } + const buffer = all.slice(0, state.bodyLen); - // ensure the public getBuffer returns a valid value if invoked from the event listeners + // ensure the getBuffer returns a valid value if invoked from the event listeners const rest = all.slice(state.bodyLen); totalLength = rest.length; this._chunks = [rest]; - state.bodyIsJson = false; state.bodyLen = -1; state.readHead = true; - this._onMessage.fire(message); + this._onMessage.fire(buffer); if (this._isDisposed) { // check if an event listener lead to our disposal @@ -145,7 +147,7 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { _socket.once('close', this._socketCloseListener); } - public dispose(): void { + dispose(): void { this._isDisposed = true; this._firstChunkTimer.dispose(); this._socket.removeListener('data', this._socketDataListener); @@ -153,30 +155,18 @@ export class Protocol implements IDisposable, IMessagePassingProtocol { this._socket.removeListener('close', this._socketCloseListener); } - public end(): void { + end(): void { this._socket.end(); } - public getBuffer(): Buffer { + getBuffer(): Buffer { return Buffer.concat(this._chunks); } - public send(message: any): void { - - // [bodyIsJson|bodyLen|message] - // |^header^^^^^^^^^^^|^data^^] - - const header = Buffer.alloc(Protocol._headerLen); - - // ensure string - if (typeof message !== 'string') { - message = JSON.stringify(message); - header.writeInt8(1, 0, true); - } - const data = Buffer.from(message); - header.writeInt32BE(data.length, 1, true); - - this._writeSoon(header, data); + send(buffer: Buffer): void { + const header = Buffer.allocUnsafe(Protocol._headerLen); + header.writeUInt32BE(buffer.length, 0, true); + this._writeSoon(header, buffer); } private _writeBuffer = new class { @@ -241,7 +231,7 @@ export class Server extends IPCServer { export class Client extends IPCClient { - public static fromSocket(socket: Socket, id: string): Client { + static fromSocket(socket: Socket, id: string): Client { return new Client(new Protocol(socket), id); } diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts similarity index 63% rename from src/vs/base/parts/ipc/common/ipc.ts rename to src/vs/base/parts/ipc/node/ipc.ts index 67376daaf3d..0c17d1414c5 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/node/ipc.ts @@ -9,51 +9,46 @@ import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter, once, filterEvent, toPromise, Relay } from 'vs/base/common/event'; -enum MessageType { - RequestPromise, - RequestPromiseCancel, - ResponseInitialize, - ResponsePromiseSuccess, - ResponsePromiseProgress, - ResponsePromiseError, - ResponsePromiseErrorObj, - - RequestEventListen, - RequestEventDispose, - ResponseEventFire, +export enum RequestType { + Promise = 100, + PromiseCancel = 101, + EventListen = 102, + EventDispose = 103 } -function isResponse(messageType: MessageType): boolean { - return messageType >= MessageType.ResponseInitialize; -} - -interface IRawMessage { - id: number; - type: MessageType; -} - -interface IRawRequest extends IRawMessage { - channelName?: string; - name?: string; - arg?: any; -} +type IRawPromiseRequest = { type: RequestType.Promise; id: number; channelName: string; name: string; arg: any; }; +type IRawPromiseCancelRequest = { type: RequestType.PromiseCancel, id: number }; +type IRawEventListenRequest = { type: RequestType.EventListen; id: number; channelName: string; name: string; arg: any; }; +type IRawEventDisposeRequest = { type: RequestType.EventDispose, id: number }; +type IRawRequest = IRawPromiseRequest | IRawPromiseCancelRequest | IRawEventListenRequest | IRawEventDisposeRequest; interface IRequest { raw: IRawRequest; flush?: () => void; } -interface IRawResponse extends IRawMessage { - data: any; +export enum ResponseType { + Initialize = 200, + PromiseSuccess = 201, + PromiseError = 202, + PromiseErrorObj = 203, + EventFire = 204 } +type IRawInitializeResponse = { type: ResponseType.Initialize }; +type IRawPromiseSuccessResponse = { type: ResponseType.PromiseSuccess; id: number; data: any }; +type IRawPromiseErrorResponse = { type: ResponseType.PromiseError; id: number; data: { message: string, name: string, stack: string[] | undefined } }; +type IRawPromiseErrorObjResponse = { type: ResponseType.PromiseErrorObj; id: number; data: any }; +type IRawEventFireResponse = { type: ResponseType.EventFire; id: number; data: any }; +type IRawResponse = IRawInitializeResponse | IRawPromiseSuccessResponse | IRawPromiseErrorResponse | IRawPromiseErrorObjResponse | IRawEventFireResponse; + interface IHandler { (response: IRawResponse): void; } export interface IMessagePassingProtocol { - send(request: any): void; - onMessage: Event<any>; + send(buffer: Buffer): void; + onMessage: Event<Buffer>; } enum State { @@ -109,7 +104,54 @@ export interface IRoutingChannelClient { getChannel<T extends IChannel>(channelName: string, router: IClientRouter): T; } -// TODO@joao cleanup this mess! +enum BodyType { + Undefined, + String, + Buffer, + Object +} + +const empty = Buffer.allocUnsafe(0); + +function serializeBody(body: any): { buffer: Buffer, type: BodyType } { + if (typeof body === 'undefined') { + return { buffer: empty, type: BodyType.Undefined }; + } else if (typeof body === 'string') { + return { buffer: Buffer.from(body), type: BodyType.String }; + } else if (Buffer.isBuffer(body)) { + return { buffer: body, type: BodyType.Buffer }; + } else { + return { buffer: Buffer.from(JSON.stringify(body)), type: BodyType.Object }; + } +} + +function serialize(header: any, body: any = undefined): Buffer { + const headerSizeBuffer = Buffer.allocUnsafe(4); + const { buffer: bodyBuffer, type: bodyType } = serializeBody(body); + const headerBuffer = Buffer.from(JSON.stringify([header, bodyType])); + headerSizeBuffer.writeUInt32BE(headerBuffer.byteLength, 0); + + return Buffer.concat([headerSizeBuffer, headerBuffer, bodyBuffer]); +} + +function deserializeBody(bodyBuffer: Buffer, bodyType: BodyType): any { + switch (bodyType) { + case BodyType.Undefined: return undefined; + case BodyType.String: return bodyBuffer.toString(); + case BodyType.Buffer: return bodyBuffer; + case BodyType.Object: return JSON.parse(bodyBuffer.toString()); + } +} + +function deserialize(buffer: Buffer): { header: any, body: any } { + const headerSize = buffer.readUInt32BE(0); + const headerBuffer = buffer.slice(4, 4 + headerSize); + const bodyBuffer = buffer.slice(4 + headerSize); + const [header, bodyType] = JSON.parse(headerBuffer.toString()); + const body = deserializeBody(bodyBuffer, bodyType); + + return { header, body }; +} export class ChannelServer implements IChannelServer, IDisposable { @@ -118,32 +160,15 @@ export class ChannelServer implements IChannelServer, IDisposable { private protocolListener: IDisposable; constructor(private protocol: IMessagePassingProtocol) { - this.protocolListener = this.protocol.onMessage(r => this.onMessage(r)); - this.protocol.send(<IRawResponse>{ type: MessageType.ResponseInitialize }); + this.protocolListener = this.protocol.onMessage(msg => this.onRawMessage(msg)); + this.sendResponse({ type: ResponseType.Initialize }); } registerChannel(channelName: string, channel: IChannel): void { this.channels[channelName] = channel; } - private onMessage(request: IRawRequest): void { - switch (request.type) { - case MessageType.RequestPromise: - this.onPromise(request); - break; - - case MessageType.RequestEventListen: - this.onEventListen(request); - break; - - case MessageType.RequestPromiseCancel: - case MessageType.RequestEventDispose: - this.disposeActiveRequest(request); - break; - } - } - - private onPromise(request: IRawRequest): void { + private onPromise(request: IRawPromiseRequest): void { const channel = this.channels[request.channelName]; let promise: Promise; @@ -156,35 +181,33 @@ export class ChannelServer implements IChannelServer, IDisposable { const id = request.id; const requestPromise = promise.then(data => { - this.protocol.send(<IRawResponse>{ id, data, type: MessageType.ResponsePromiseSuccess }); + this.sendResponse(<IRawResponse>{ id, data, type: ResponseType.PromiseSuccess }); delete this.activeRequests[request.id]; }, data => { if (data instanceof Error) { - this.protocol.send(<IRawResponse>{ + this.sendResponse(<IRawResponse>{ id, data: { message: data.message, name: data.name, stack: data.stack ? (data.stack.split ? data.stack.split('\n') : data.stack) : void 0 - }, type: MessageType.ResponsePromiseError + }, type: ResponseType.PromiseError }); } else { - this.protocol.send(<IRawResponse>{ id, data, type: MessageType.ResponsePromiseErrorObj }); + this.sendResponse(<IRawResponse>{ id, data, type: ResponseType.PromiseErrorObj }); } delete this.activeRequests[request.id]; - }, data => { - this.protocol.send(<IRawResponse>{ id, data, type: MessageType.ResponsePromiseProgress }); }); this.activeRequests[request.id] = toDisposable(() => requestPromise.cancel()); } - private onEventListen(request: IRawRequest): void { + private onEventListen(request: IRawEventListenRequest): void { const channel = this.channels[request.channelName]; const id = request.id; const event = channel.listen(request.name, request.arg); - const disposable = event(data => this.protocol.send(<IRawResponse>{ id, data, type: MessageType.ResponseEventFire })); + const disposable = event(data => this.sendResponse(<IRawResponse>{ id, data, type: ResponseType.EventFire })); this.activeRequests[request.id] = disposable; } @@ -198,6 +221,70 @@ export class ChannelServer implements IChannelServer, IDisposable { } } + private onRawMessage(message: Buffer): void { + const { header, body } = deserialize(message); + const type: RequestType = header[0]; + let request: IRawRequest; + + switch (type) { + case RequestType.Promise: + case RequestType.EventListen: + request = { type: header[0], id: header[1], channelName: header[2], name: header[3], arg: body }; + break; + case RequestType.PromiseCancel: + case RequestType.EventDispose: + request = { type: header[0], id: header[1] }; + break; + default: + return; + } + + this.onRequest(request); + } + + private onRequest(request: IRawRequest): void { + switch (request.type) { + case RequestType.Promise: + this.onPromise(request); + break; + + case RequestType.EventListen: + this.onEventListen(request); + break; + + case RequestType.PromiseCancel: + case RequestType.EventDispose: + this.disposeActiveRequest(request); + break; + } + } + + private sendResponse(response: IRawResponse) { + let buffer: Buffer; + + switch (response.type) { + case ResponseType.Initialize: + buffer = serialize([response.type]); + break; + case ResponseType.PromiseSuccess: + case ResponseType.PromiseError: + case ResponseType.EventFire: + case ResponseType.PromiseErrorObj: + buffer = serialize([response.type, response.id], response.data); + break; + } + + this.sendRawMessage(buffer); + } + + private sendRawMessage(message: Buffer) { + try { + this.protocol.send(message); + } catch (err) { + // noop + } + } + public dispose(): void { this.protocolListener.dispose(); this.protocolListener = null; @@ -223,7 +310,7 @@ export class ChannelClient implements IChannelClient, IDisposable { readonly onDidInitialize = this._onDidInitialize.event; constructor(private protocol: IMessagePassingProtocol) { - this.protocolListener = this.protocol.onMessage(r => this.onMessage(r)); + this.protocolListener = this.protocol.onMessage(msg => this.onRawMessage(msg)); } getChannel<T extends IChannel>(channelName: string): T { @@ -235,8 +322,8 @@ export class ChannelClient implements IChannelClient, IDisposable { private requestPromise(channelName: string, name: string, arg: any): TPromise<any> { const id = this.lastRequestId++; - const type = MessageType.RequestPromise; - const request = { raw: { id, type, channelName, name, arg } }; + const type = RequestType.Promise; + const request: IRequest = { raw: { id, type, channelName, name, arg } }; const activeRequest = this.state === State.Uninitialized ? this.bufferRequest(request) @@ -254,8 +341,9 @@ export class ChannelClient implements IChannelClient, IDisposable { private requestEvent(channelName: string, name: string, arg: any): Event<any> { const id = this.lastRequestId++; - const type = MessageType.RequestEventListen; - const request = { raw: { id, type, channelName, name, arg } }; + const type = RequestType.EventListen; + const raw: IRawRequest = { id, type, channelName, name, arg }; + const request: IRequest = { raw }; let uninitializedPromise: TPromise<any> | null = null; const emitter = new Emitter<any>({ @@ -263,7 +351,7 @@ export class ChannelClient implements IChannelClient, IDisposable { uninitializedPromise = this.whenInitialized(); uninitializedPromise.then(() => { uninitializedPromise = null; - this.send(request.raw); + this.sendRequest(request.raw); }); }, onLastListenerRemove: () => { @@ -271,27 +359,27 @@ export class ChannelClient implements IChannelClient, IDisposable { uninitializedPromise.cancel(); uninitializedPromise = null; } else { - this.send({ id, type: MessageType.RequestEventDispose }); + this.sendRequest({ id, type: RequestType.EventDispose }); } } }); - this.handlers[id] = response => emitter.fire(response.data); + this.handlers[id] = (response: IRawEventFireResponse) => emitter.fire(response.data); return emitter.event; } 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: + case ResponseType.PromiseSuccess: delete this.handlers[id]; c(response.data); break; - case MessageType.ResponsePromiseError: + case ResponseType.PromiseError: delete this.handlers[id]; const error = new Error(response.data.message); (<any>error).stack = response.data.stack; @@ -299,31 +387,27 @@ export class ChannelClient implements IChannelClient, IDisposable { e(error); break; - case MessageType.ResponsePromiseErrorObj: + case ResponseType.PromiseErrorObj: delete this.handlers[id]; e(response.data); break; - - case MessageType.ResponsePromiseProgress: - p(response.data); - break; } }; - this.send(request.raw); + this.sendRequest(request.raw); }, - () => this.send({ id, type: MessageType.RequestPromiseCancel })); + () => this.sendRequest({ id, type: RequestType.PromiseCancel })); } 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; @@ -347,12 +431,30 @@ export class ChannelClient implements IChannelClient, IDisposable { }); } - private onMessage(response: IRawResponse): void { - if (!isResponse(response.type)) { - return; + private onRawMessage(message: Buffer): void { + const { header, body } = deserialize(message); + const type: ResponseType = header[0]; + let response: IRawResponse; + + switch (type) { + case ResponseType.Initialize: + response = { type: header[0] }; + break; + case ResponseType.PromiseSuccess: + case ResponseType.PromiseError: + case ResponseType.EventFire: + case ResponseType.PromiseErrorObj: + response = { type: header[0], id: header[1], data: body }; + break; + default: + return; } - if (this.state === State.Uninitialized && response.type === MessageType.ResponseInitialize) { + this.onResponse(response); + } + + private onResponse(response: IRawResponse): void { + if (response.type === ResponseType.Initialize) { this.state = State.Idle; this._onDidInitialize.fire(); this.bufferedRequests.forEach(r => r.flush && r.flush()); @@ -366,9 +468,26 @@ export class ChannelClient implements IChannelClient, IDisposable { } } - private send(raw: IRawRequest) { + private sendRequest(request: IRawRequest) { + let buffer: Buffer; + + switch (request.type) { + case RequestType.Promise: + case RequestType.EventListen: + buffer = serialize([request.type, request.id, request.channelName, request.name], request.arg); + break; + case RequestType.PromiseCancel: + case RequestType.EventDispose: + buffer = serialize([request.type, request.id]); + break; + } + + this.sendRawMessage(buffer); + } + + private sendRawMessage(message: Buffer) { try { - this.protocol.send(raw); + this.protocol.send(message); } catch (err) { // noop } @@ -378,7 +497,7 @@ export class ChannelClient implements IChannelClient, IDisposable { if (this.state === State.Idle) { return TPromise.as(null); } else { - return toPromise(this.onDidInitialize); + return TPromise.wrap(toPromise(this.onDidInitialize)); } } @@ -413,13 +532,14 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos onDidClientConnect(({ protocol, onDidClientDisconnect }) => { const onFirstMessage = once(protocol.onMessage); - onFirstMessage(id => { + onFirstMessage(rawId => { const channelServer = new ChannelServer(protocol); const channelClient = new ChannelClient(protocol); Object.keys(this.channels) .forEach(name => channelServer.registerChannel(name, this.channels[name])); + const id = rawId.toString(); this.channelClients[id] = channelClient; this.onClientAdded.fire(id); @@ -495,7 +615,7 @@ export class IPCClient implements IChannelClient, IChannelServer, IDisposable { private channelServer: ChannelServer; constructor(protocol: IMessagePassingProtocol, id: string) { - protocol.send(id); + protocol.send(Buffer.from(id)); this.channelClient = new ChannelClient(protocol); this.channelServer = new ChannelServer(protocol); } 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 eb6ddffba02..8647efd5b5b 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 @@ -49,18 +49,20 @@ suite('IPC, Socket Protocol', () => { return new TPromise(resolve => { const sub = b.onMessage(data => { sub.dispose(); - assert.equal(data, 'foobarfarboo'); + assert.equal(data.toString(), 'foobarfarboo'); resolve(null); }); - a.send('foobarfarboo'); + a.send(Buffer.from('foobarfarboo')); }).then(() => { return new TPromise(resolve => { const sub = b.onMessage(data => { sub.dispose(); - assert.equal(data, 123); + assert.equal(data.readInt8(0), 123); resolve(null); }); - a.send(123); + const buffer = Buffer.allocUnsafe(1); + buffer.writeInt8(123, 0); + a.send(buffer); }); }); }); @@ -78,11 +80,11 @@ suite('IPC, Socket Protocol', () => { data: 'Hello World'.split('') }; - a.send(data); + a.send(Buffer.from(JSON.stringify(data))); return new TPromise(resolve => { b.onMessage(msg => { - assert.deepEqual(msg, data); + assert.deepEqual(JSON.parse(msg.toString()), data); resolve(null); }); }); @@ -100,7 +102,7 @@ suite('IPC, Socket Protocol', () => { assert.equal(stream.listenerCount('end'), 2); receiver1.onMessage((msg) => { - assert.equal(msg.value, 1); + assert.equal(JSON.parse(msg.toString()).value, 1); let buffer = receiver1.getBuffer(); receiver1.dispose(); @@ -110,15 +112,15 @@ suite('IPC, Socket Protocol', () => { const receiver2 = new Protocol(stream, buffer); receiver2.onMessage((msg) => { - assert.equal(msg.value, 2); + assert.equal(JSON.parse(msg.toString()).value, 2); resolve(void 0); }); }); const msg1 = { value: 1 }; const msg2 = { value: 2 }; - sender.send(msg1); - sender.send(msg2); + sender.send(Buffer.from(JSON.stringify(msg1))); + sender.send(Buffer.from(JSON.stringify(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<ITestChannel>('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<ITestChannel>('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..51506dc67bd 100644 --- a/src/vs/base/parts/ipc/test/node/testService.ts +++ b/src/vs/base/parts/ipc/test/node/testService.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise, PPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { Event, Emitter } from 'vs/base/common/event'; export interface IMarcoPoloEvent { @@ -17,7 +17,6 @@ export interface ITestService { marco(): TPromise<string>; pong(ping: string): TPromise<{ incoming: string, outgoing: string }>; cancelMe(): TPromise<boolean>; - batchPerf(batches: number, size: number, dataSize: number): PPromise<any, any[]>; } export class TestService implements ITestService { @@ -25,8 +24,6 @@ export class TestService implements ITestService { private _onMarco = new Emitter<IMarcoPoloEvent>(); onMarco: Event<IMarcoPoloEvent> = this._onMarco.event; - private _data = 'abcdefghijklmnopqrstuvwxyz'; - marco(): TPromise<string> { this._onMarco.fire({ answer: 'polo' }); return TPromise.as('polo'); @@ -39,32 +36,6 @@ export class TestService implements ITestService { cancelMe(): TPromise<boolean> { return TPromise.timeout(100).then(() => true); } - - batchPerf(batches: number, size: number, dataSize: number): PPromise<any, any[]> { - while (this._data.length < dataSize) { - this._data += this._data; - } - const self = this; - return new PPromise<any, any[]>((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<any>; call(command: 'pong', ping: string): TPromise<any>; call(command: 'cancelMe'): TPromise<any>; - call(command: 'batchPerf', args: { batches: number; size: number; dataSize: number; }): PPromise<any, any[]>; call(command: string, ...args: any[]): TPromise<any>; } @@ -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<boolean> { return this.channel.call('cancelMe'); } - - batchPerf(batches: number, size: number, dataSize: number): PPromise<any, any[]> { - 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 d19657a3f92..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(', '); } /** diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index 8341fd31256..fdcbaf9ffdf 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -6,7 +6,6 @@ import 'vs/css!./quickopen'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as platform from 'vs/base/common/platform'; import * as types from 'vs/base/common/types'; import * as errors from 'vs/base/common/errors'; @@ -103,6 +102,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; @@ -168,7 +168,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { .on(DOM.EventType.BLUR, (e: FocusEvent) => this.loosingFocus(e), null, true); // Progress Bar - this.progressBar = this._register(new ProgressBar(div.clone(), { progressBarBackground: this.styles.progressBarBackground })); + this.progressBar = this._register(new ProgressBar(div.getHTMLElement(), { progressBarBackground: this.styles.progressBarBackground })); this.progressBar.hide(); // Input Field @@ -232,6 +232,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' @@ -628,9 +634,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); } @@ -643,8 +652,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { return input.filter.isVisible(e); } - private autoFocus(input: IModel<any>, autoFocus: IAutoFocus = {}): void { - const entries = input.entries.filter(e => this.isElementVisible(input, e)); + private autoFocus(input: IModel<any>, entries: any[], autoFocus: IAutoFocus = {}): void { // First check for auto focus of prefix matches if (autoFocus.autoFocusPrefixMatch) { @@ -725,11 +733,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); @@ -769,6 +779,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; @@ -965,11 +979,8 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } this.isLoosingFocus = true; - TPromise.timeout(0).then(() => { - if (!this.isLoosingFocus) { - return; - } - if (this.isDisposed) { + setTimeout(() => { + if (!this.isLoosingFocus || this.isDisposed) { return; } @@ -977,7 +988,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { if (!veto) { this.hide(HideReason.FOCUS_LOST); } - }); + }, 0); } dispose(): void { 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 12df5ccd031..64e2f7152f0 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -364,6 +364,10 @@ export interface IDataSource { /** * Returns the unique identifier of the given element. * No more than one element may use a given identifier. + * + * You should not attempt to "move" an element to a different + * parent by keeping its ID. The idea here is to have tree location + * related IDs (eg. full file path, in the Explorer example). */ getId(tree: ITree, element: any): string; diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index 10b350737ec..dc008d1eede 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -211,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 c2323599e0b..38abf8eca45 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -360,6 +360,10 @@ export class Item { } var result = this.lock.run(this, () => { + if (this.isExpanded() || !this.doesHaveChildren) { + return WinJS.TPromise.as(false); + } + var eventData: IItemExpandEvent = { item: this }; var result: WinJS.Promise; this._onExpand.fire(eventData); 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 5d56011feac..0eb524c5247 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -25,7 +25,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Event, Emitter } from 'vs/base/common/event'; import { DataTransfers } from 'vs/base/browser/dnd'; import { DefaultTreestyler } from './treeDefaults'; -import { Delayer } from 'vs/base/common/async'; +import { Delayer, CancelablePromise, createCancelablePromise, wireCancellationToken } from 'vs/base/common/async'; export interface IRow { element: HTMLElement; @@ -59,10 +59,19 @@ export class RowCache implements Lifecycle.IDisposable { var row = document.createElement('div'); row.appendChild(content); + let templateData: any = null; + + try { + templateData = this.context.renderer.renderTemplate(this.context.tree, templateId, content); + } catch (err) { + console.error('Tree usage error: exception while rendering template'); + console.error(err); + } + result = { element: row, templateId: templateId, - templateData: this.context.renderer.renderTemplate(this.context.tree, templateId, content) + templateData }; } @@ -260,7 +269,12 @@ export class ViewItem implements IViewItem { this.element.style.width = 'fit-content'; } - this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData); + try { + this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData); + } catch (err) { + console.error('Tree usage error: exception while rendering element'); + console.error(err); + } if (this.context.horizontalScrolling) { this.width = DOM.getContentWidth(this.element) + paddingLeft; @@ -429,7 +443,7 @@ export class TreeView extends HeightMap { private currentDropTarget: ViewItem; private shouldInvalidateDropReaction: boolean; private currentDropTargets: ViewItem[]; - private currentDropPromise: WinJS.Promise; + private currentDropPromise: CancelablePromise<any>; private dragAndDropScrollInterval: number; private dragAndDropScrollTimeout: number; private dragAndDropMouseY: number; @@ -658,6 +672,11 @@ export class TreeView extends HeightMap { return item && item.model.getElement(); } + public getLastVisibleElement(): any { + const item = this.itemAtIndex(this.indexAt(this.lastRenderTop + this.lastRenderHeight - 1)); + return item && item.model.getElement(); + } + private render(scrollTop: number, viewHeight: number, scrollLeft: number, viewWidth: number, scrollWidth: number): void { var i: number; var stop: number; @@ -1296,15 +1315,15 @@ export class TreeView extends HeightMap { this.didJustPressContextMenuKey = event.keyCode === KeyCode.ContextMenu || (event.shiftKey && event.keyCode === KeyCode.F10); + if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { + return; // Ignore event if target is a form input field (avoids browser specific issues) + } + if (this.didJustPressContextMenuKey) { event.preventDefault(); event.stopPropagation(); } - if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { - return; // Ignore event if target is a form input field (avoids browser specific issues) - } - this.context.controller.onKeyDown(this.context.tree, event); } @@ -1525,9 +1544,11 @@ export class TreeView extends HeightMap { } if (reaction.autoExpand) { - this.currentDropPromise = WinJS.TPromise.timeout(500) + const promise = WinJS.TPromise.timeout(500) .then(() => this.context.tree.expand(this.currentDropElement)) .then(() => this.shouldInvalidateDropReaction = true); + + this.currentDropPromise = createCancelablePromise(token => wireCancellationToken(token, promise)); } } } diff --git a/src/vs/base/test/browser/builder.test.ts b/src/vs/base/test/browser/builder.test.ts index d46dff04c97..f592174bd50 100644 --- a/src/vs/base/test/browser/builder.test.ts +++ b/src/vs/base/test/browser/builder.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { Builder, MultiBuilder, $, bindElement, withElement, setPropertyOnElement, getPropertyFromElement } from 'vs/base/browser/builder'; +import { Builder, MultiBuilder, $, _bindElement, _withElement, _setPropertyOnElement, _getPropertyFromElement } from 'vs/base/browser/builder'; import * as Types from 'vs/base/common/types'; import * as DomUtils from 'vs/base/browser/dom'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -48,7 +48,7 @@ function select(builder: Builder, selector: string, offdom?: boolean): MultiBuil let builders: Builder[] = []; for (let i = 0; i < elements.length; i++) { - builders.push(withElement(<HTMLElement>elements.item(i), offdom)); + builders.push(_withElement(<HTMLElement>elements.item(i), offdom)); } return new MultiBuilder(builders); @@ -75,17 +75,17 @@ suite('Builder', () => { assert(element); // Properties - setPropertyOnElement(element, 'foo', 'bar'); - assert.strictEqual(getPropertyFromElement(element, 'foo'), 'bar'); + _setPropertyOnElement(element, 'foo', 'bar'); + assert.strictEqual(_getPropertyFromElement(element, 'foo'), 'bar'); - setPropertyOnElement(element, 'foo', { foo: 'bar' }); - assert.deepEqual(getPropertyFromElement(element, 'foo'), { foo: 'bar' }); + _setPropertyOnElement(element, 'foo', { foo: 'bar' }); + assert.deepEqual(_getPropertyFromElement(element, 'foo'), { foo: 'bar' }); - setPropertyOnElement(element, 'bar', 'bar'); - assert.strictEqual(getPropertyFromElement(element, 'bar'), 'bar'); + _setPropertyOnElement(element, 'bar', 'bar'); + assert.strictEqual(_getPropertyFromElement(element, 'bar'), 'bar'); - setPropertyOnElement(element, 'bar', { foo: 'bar' }); - assert.deepEqual(getPropertyFromElement(element, 'bar'), { foo: 'bar' }); + _setPropertyOnElement(element, 'bar', { foo: 'bar' }); + assert.deepEqual(_getPropertyFromElement(element, 'bar'), { foo: 'bar' }); }); test('Select', function () { @@ -801,7 +801,7 @@ suite('Builder', () => { let counter7 = 0; b.div(function (div: Builder) { - bindElement(div.getHTMLElement(), 'Foo Bar'); + _bindElement(div.getHTMLElement(), 'Foo Bar'); div.setProperty('Foo', 'Bar'); bindings.push(div.clone()); @@ -814,7 +814,7 @@ suite('Builder', () => { inputs.push(div.clone()); div.p(function (p: Builder) { - bindElement(p.getHTMLElement(), 'Foo Bar'); + _bindElement(p.getHTMLElement(), 'Foo Bar'); p.setProperty('Foo', 'Bar'); bindings.push(p.clone()); @@ -827,7 +827,7 @@ suite('Builder', () => { inputs.push(p.clone()); p.ul(function (ul: Builder) { - bindElement(ul.getHTMLElement(), 'Foo Bar'); + _bindElement(ul.getHTMLElement(), 'Foo Bar'); ul.setProperty('Foo', 'Bar'); bindings.push(ul.clone()); @@ -840,7 +840,7 @@ suite('Builder', () => { inputs.push(ul.clone()); ul.li(function (li: Builder) { - bindElement(li.getHTMLElement(), 'Foo Bar'); + _bindElement(li.getHTMLElement(), 'Foo Bar'); li.setProperty('Foo', 'Bar'); bindings.push(li.clone()); @@ -856,7 +856,7 @@ suite('Builder', () => { id: 'builderspan', innerHtml: 'Foo Bar' }, function (span) { - bindElement(span.getHTMLElement(), 'Foo Bar'); + _bindElement(span.getHTMLElement(), 'Foo Bar'); span.setProperty('Foo', 'Bar'); bindings.push(span.clone()); @@ -873,7 +873,7 @@ suite('Builder', () => { id: 'builderimg', src: '#' }, function (img) { - bindElement(img.getHTMLElement(), 'Foo Bar'); + _bindElement(img.getHTMLElement(), 'Foo Bar'); img.setProperty('Foo', 'Bar'); bindings.push(img.clone()); @@ -891,7 +891,7 @@ suite('Builder', () => { href: '#', innerHtml: 'Link' }, function (a) { - bindElement(a.getHTMLElement(), 'Foo Bar'); + _bindElement(a.getHTMLElement(), 'Foo Bar'); a.setProperty('Foo', 'Bar'); bindings.push(a.clone()); @@ -986,7 +986,7 @@ suite('Builder', () => { let counter7 = 0; b.div(function (div: Builder) { - bindElement(div.getHTMLElement(), 'Foo Bar'); + _bindElement(div.getHTMLElement(), 'Foo Bar'); div.setProperty('Foo', 'Bar'); bindings.push(div.clone()); @@ -999,7 +999,7 @@ suite('Builder', () => { inputs.push(div.clone()); div.p(function (p: Builder) { - bindElement(p.getHTMLElement(), 'Foo Bar'); + _bindElement(p.getHTMLElement(), 'Foo Bar'); p.setProperty('Foo', 'Bar'); bindings.push(p.clone()); @@ -1012,7 +1012,7 @@ suite('Builder', () => { inputs.push(p.clone()); p.ul(function (ul: Builder) { - bindElement(ul.getHTMLElement(), 'Foo Bar'); + _bindElement(ul.getHTMLElement(), 'Foo Bar'); ul.setProperty('Foo', 'Bar'); bindings.push(ul.clone()); @@ -1025,7 +1025,7 @@ suite('Builder', () => { inputs.push(ul.clone()); ul.li(function (li: Builder) { - bindElement(li.getHTMLElement(), 'Foo Bar'); + _bindElement(li.getHTMLElement(), 'Foo Bar'); li.setProperty('Foo', 'Bar'); bindings.push(li.clone()); @@ -1041,7 +1041,7 @@ suite('Builder', () => { id: 'builderspan', innerHtml: 'Foo Bar' }, function (span) { - bindElement(span.getHTMLElement(), 'Foo Bar'); + _bindElement(span.getHTMLElement(), 'Foo Bar'); span.setProperty('Foo', 'Bar'); bindings.push(span.clone()); @@ -1058,7 +1058,7 @@ suite('Builder', () => { id: 'builderimg', src: '#' }, function (img) { - bindElement(img.getHTMLElement(), 'Foo Bar'); + _bindElement(img.getHTMLElement(), 'Foo Bar'); img.setProperty('Foo', 'Bar'); bindings.push(img.clone()); @@ -1076,7 +1076,7 @@ suite('Builder', () => { href: '#', innerHtml: 'Link' }, function (a) { - bindElement(a.getHTMLElement(), 'Foo Bar'); + _bindElement(a.getHTMLElement(), 'Foo Bar'); a.setProperty('Foo', 'Bar'); bindings.push(a.clone()); 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/browser/ui/tree/treeModel.test.ts b/src/vs/base/test/browser/ui/tree/treeModel.test.ts index f348b38a9d9..71d44e275e2 100644 --- a/src/vs/base/test/browser/ui/tree/treeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/treeModel.test.ts @@ -259,45 +259,45 @@ suite('TreeModel2', function () { assert.deepEqual(list[2].depth, 1); }); - test('expand', function () { - const list = [] as ITreeNode<number>[]; - const model = new TreeModel<number>(toSpliceable(list)); + // test('expand', function () { + // const list = [] as ITreeNode<number>[]; + // const model = new TreeModel<number>(toSpliceable(list)); - model.splice([0], 0, Iterator.iterate([ - { - element: 0, collapsed: true, children: Iterator.iterate([ - { element: 10 }, - { element: 11 }, - { element: 12 }, - ]) - }, - { element: 1 }, - { element: 2 } - ])); + // model.splice([0], 0, Iterator.iterate([ + // { + // element: 0, collapsed: true, children: Iterator.iterate([ + // { element: 10 }, + // { element: 11 }, + // { element: 12 }, + // ]) + // }, + // { element: 1 }, + // { element: 2 } + // ])); - assert.deepEqual(list.length, 3); + // assert.deepEqual(list.length, 3); - model.setCollapsed([0], false); - assert.deepEqual(list.length, 6); - assert.deepEqual(list[0].element, 0); - assert.deepEqual(list[0].collapsed, false); - assert.deepEqual(list[0].depth, 1); - assert.deepEqual(list[1].element, 10); - assert.deepEqual(list[1].collapsed, false); - assert.deepEqual(list[1].depth, 2); - assert.deepEqual(list[2].element, 11); - assert.deepEqual(list[2].collapsed, false); - assert.deepEqual(list[2].depth, 2); - assert.deepEqual(list[3].element, 12); - assert.deepEqual(list[3].collapsed, false); - assert.deepEqual(list[3].depth, 2); - assert.deepEqual(list[4].element, 1); - assert.deepEqual(list[4].collapsed, false); - assert.deepEqual(list[4].depth, 1); - assert.deepEqual(list[5].element, 2); - assert.deepEqual(list[5].collapsed, false); - assert.deepEqual(list[5].depth, 1); - }); + // model.setCollapsed([0], false); + // assert.deepEqual(list.length, 6); + // assert.deepEqual(list[0].element, 0); + // assert.deepEqual(list[0].collapsed, false); + // assert.deepEqual(list[0].depth, 1); + // assert.deepEqual(list[1].element, 10); + // assert.deepEqual(list[1].collapsed, false); + // assert.deepEqual(list[1].depth, 2); + // assert.deepEqual(list[2].element, 11); + // assert.deepEqual(list[2].collapsed, false); + // assert.deepEqual(list[2].depth, 2); + // assert.deepEqual(list[3].element, 12); + // assert.deepEqual(list[3].collapsed, false); + // assert.deepEqual(list[3].depth, 2); + // assert.deepEqual(list[4].element, 1); + // assert.deepEqual(list[4].collapsed, false); + // assert.deepEqual(list[4].depth, 1); + // assert.deepEqual(list[5].element, 2); + // assert.deepEqual(list[5].collapsed, false); + // assert.deepEqual(list[5].depth, 1); + // }); test('collapse should recursively adjust visible count', function () { const list = [] as ITreeNode<number>[]; 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/cache.test.ts b/src/vs/base/test/common/cache.test.ts index c7bab09abd0..f27a4253a0e 100644 --- a/src/vs/base/test/common/cache.test.ts +++ b/src/vs/base/test/common/cache.test.ts @@ -8,26 +8,27 @@ import * as assert from 'assert'; import Cache from 'vs/base/common/cache'; import { TPromise } from 'vs/base/common/winjs.base'; +import { createCancelablePromise, wireCancellationToken } from 'vs/base/common/async'; suite('Cache', () => { test('simple value', () => { let counter = 0; - const cache = new Cache(() => TPromise.as(counter++)); + const cache = new Cache(() => createCancelablePromise(_ => TPromise.as(counter++))); - return cache.get() + return cache.get().promise .then(c => assert.equal(c, 0), () => assert.fail('Unexpected assertion error')) - .then(() => cache.get()) + .then(() => cache.get().promise) .then(c => assert.equal(c, 0), () => assert.fail('Unexpected assertion error')); }); test('simple error', () => { let counter = 0; - const cache = new Cache(() => TPromise.wrapError(new Error(String(counter++)))); + const cache = new Cache(() => createCancelablePromise(_ => TPromise.wrapError(new Error(String(counter++))))); - return cache.get() + return cache.get().promise .then(() => assert.fail('Unexpected assertion error'), err => assert.equal(err.message, 0)) - .then(() => cache.get()) + .then(() => cache.get().promise) .then(() => assert.fail('Unexpected assertion error'), err => assert.equal(err.message, 0)); }); @@ -36,28 +37,29 @@ suite('Cache', () => { const cache = new Cache(() => { counter1++; - return TPromise.timeout(1).then(() => counter2++); + return createCancelablePromise(token => wireCancellationToken(token, TPromise.timeout(1).then(() => counter2++))); }); assert.equal(counter1, 0); assert.equal(counter2, 0); - let promise = cache.get(); + let result = cache.get(); assert.equal(counter1, 1); assert.equal(counter2, 0); - promise.cancel(); + result.promise.then(null, () => assert(true)); + result.dispose(); assert.equal(counter1, 1); assert.equal(counter2, 0); - promise = cache.get(); + result = cache.get(); assert.equal(counter1, 2); assert.equal(counter2, 0); - return promise + return result.promise .then(c => { assert.equal(counter1, 2); assert.equal(counter2, 1); }) - .then(() => cache.get()) + .then(() => cache.get().promise) .then(c => { assert.equal(counter1, 2); assert.equal(counter2, 1); 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/paging.test.ts b/src/vs/base/test/common/paging.test.ts index 04aed69dda4..b7a4cd076a6 100644 --- a/src/vs/base/test/common/paging.test.ts +++ b/src/vs/base/test/common/paging.test.ts @@ -8,23 +8,35 @@ import * as assert from 'assert'; import { IPager, PagedModel } from 'vs/base/common/paging'; import { TPromise } from 'vs/base/common/winjs.base'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { isPromiseCanceledError, canceled } from 'vs/base/common/errors'; + +function getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable<number[]> { + if (cancellationToken.isCancellationRequested) { + return TPromise.wrapError(canceled()); + } + + return TPromise.as([0, 1, 2, 3, 4].map(i => i + (pageIndex * 5))); +} + +class TestPager implements IPager<number> { + + readonly firstPage = [0, 1, 2, 3, 4]; + readonly pageSize = 5; + readonly total = 100; + readonly getPage: (pageIndex: number, cancellationToken: CancellationToken) => Thenable<number[]>; + + constructor(getPageFn?: (pageIndex: number, cancellationToken: CancellationToken) => Thenable<number[]>) { + this.getPage = getPageFn || getPage; + } +} suite('PagedModel', () => { - let model: PagedModel<number>; - - setup(() => { - const pager: IPager<number> = { - firstPage: [0, 1, 2, 3, 4], - pageSize: 5, - total: 100, - getPage: pageIndex => TPromise.as([0, 1, 2, 3, 4].map(i => i + (pageIndex * 5))) - }; - - model = new PagedModel(pager, 0); - }); - test('isResolved', () => { + const pager = new TestPager(); + const model = new PagedModel(pager); + assert(model.isResolved(0)); assert(model.isResolved(1)); assert(model.isResolved(2)); @@ -40,14 +52,20 @@ suite('PagedModel', () => { }); test('resolve single', () => { + const pager = new TestPager(); + const model = new PagedModel(pager); + assert(!model.isResolved(5)); - return model.resolve(5).then(() => { + return model.resolve(5, CancellationToken.None).then(() => { assert(model.isResolved(5)); }); }); test('resolve page', () => { + const pager = new TestPager(); + const model = new PagedModel(pager); + assert(!model.isResolved(5)); assert(!model.isResolved(6)); assert(!model.isResolved(7)); @@ -55,7 +73,7 @@ suite('PagedModel', () => { assert(!model.isResolved(9)); assert(!model.isResolved(10)); - return model.resolve(5).then(() => { + return model.resolve(5, CancellationToken.None).then(() => { assert(model.isResolved(5)); assert(model.isResolved(6)); assert(model.isResolved(7)); @@ -66,6 +84,9 @@ suite('PagedModel', () => { }); test('resolve page 2', () => { + const pager = new TestPager(); + const model = new PagedModel(pager); + assert(!model.isResolved(5)); assert(!model.isResolved(6)); assert(!model.isResolved(7)); @@ -73,7 +94,7 @@ suite('PagedModel', () => { assert(!model.isResolved(9)); assert(!model.isResolved(10)); - return model.resolve(10).then(() => { + return model.resolve(10, CancellationToken.None).then(() => { assert(!model.isResolved(5)); assert(!model.isResolved(6)); assert(!model.isResolved(7)); @@ -82,4 +103,85 @@ suite('PagedModel', () => { assert(model.isResolved(10)); }); }); + + test('preemptive cancellation works', function () { + const pager = new TestPager(() => { + assert(false); + return TPromise.wrap([]); + }); + + const model = new PagedModel(pager); + + return model.resolve(5, CancellationToken.Cancelled).then( + () => assert(false), + err => assert(isPromiseCanceledError(err)) + ); + }); + + test('cancellation works', function () { + const pager = new TestPager((_, token) => new TPromise((_, e) => { + token.onCancellationRequested(() => e(canceled())); + })); + + const model = new PagedModel(pager); + const tokenSource = new CancellationTokenSource(); + + const promise = model.resolve(5, tokenSource.token).then( + () => assert(false), + err => assert(isPromiseCanceledError(err)) + ); + + setTimeout(() => tokenSource.cancel(), 10); + + return promise; + }); + + test('same page cancellation works', function () { + let state = 'idle'; + + const pager = new TestPager((pageIndex, token) => { + state = 'resolving'; + + return new TPromise((_, e) => { + token.onCancellationRequested(() => { + state = 'idle'; + e(canceled()); + }); + }); + }); + + const model = new PagedModel(pager); + + assert.equal(state, 'idle'); + + const tokenSource1 = new CancellationTokenSource(); + const promise1 = model.resolve(5, tokenSource1.token).then( + () => assert(false), + err => assert(isPromiseCanceledError(err)) + ); + + assert.equal(state, 'resolving'); + + const tokenSource2 = new CancellationTokenSource(); + const promise2 = model.resolve(6, tokenSource2.token).then( + () => assert(false), + err => assert(isPromiseCanceledError(err)) + ); + + assert.equal(state, 'resolving'); + + setTimeout(() => { + assert.equal(state, 'resolving'); + tokenSource1.cancel(); + assert.equal(state, 'resolving'); + + setTimeout(() => { + assert.equal(state, 'resolving'); + tokenSource2.cancel(); + assert.equal(state, 'idle'); + }, 10); + }, 10); + + return TPromise.join([promise1, promise2]); + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 6a613730db3..d8e29039546 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,188 @@ 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/'); + 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'); + 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('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 = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); + let fileURI2 = isWindows ? URI.file('C:\\foo\\Bar') : 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('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/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 295c3adb50a..8d7c6c705da 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -381,4 +381,16 @@ suite('Strings', () => { assert.equal(strings.containsUppercaseCharacter(<string>str, true), result, `Wrong result for ${str}`); }); }); + + test('uppercaseFirstLetter', () => { + [ + ['', ''], + ['foo', 'Foo'], + ['f', 'F'], + ['123', '123'], + ['.a', '.a'], + ].forEach(([inStr, result]) => { + assert.equal(strings.uppercaseFirstLetter(inStr), result, `Wrong result for ${inStr}`); + }); + }); }); 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/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..aa45c33cc84 100644 --- a/src/vs/base/test/node/extfs/extfs.test.ts +++ b/src/vs/base/test/node/extfs/extfs.test.ts @@ -14,8 +14,8 @@ 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'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; const ignore = () => { }; @@ -169,7 +169,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 +320,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 +453,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'); @@ -563,4 +563,21 @@ suite('Extfs', () => { extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); + + test('mkdirp cancellation', (done) => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extfs', id); + + const source = new CancellationTokenSource(); + + const mkdirpPromise = extfs.mkdirp(newDir, 493, source.token); + source.cancel(); + + return mkdirpPromise.then(res => { + assert.equal(res, false); + + extfs.del(parentDir, os.tmpdir(), done, ignore); + }); + }); }); 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.data.txt b/src/vs/base/test/node/uri.test.data.txt index bb0b5b62957..22e9869aaea 100644 --- a/src/vs/base/test/node/uri.test.data.txt +++ b/src/vs/base/test/node/uri.test.data.txt @@ -2299,8 +2299,8 @@ /users/foo/src/vs/base/parts/ipc/test/node/ipc.test.ts /users/foo/src/vs/base/parts/ipc/test/node/testService.ts /users/foo/src/vs/base/parts/ipc/common -/users/foo/src/vs/base/parts/ipc/common/ipc.electron.ts -/users/foo/src/vs/base/parts/ipc/common/ipc.ts +/users/foo/src/vs/base/parts/ipc/node/ipc.electron.ts +/users/foo/src/vs/base/parts/ipc/node/ipc.ts /users/foo/src/vs/base/parts/ipc/electron-browser /users/foo/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts /users/foo/src/vs/base/parts/ipc/node 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-main/contributions.ts b/src/vs/code/code.main.ts similarity index 100% rename from src/vs/code/electron-main/contributions.ts rename to src/vs/code/code.main.ts diff --git a/src/vs/code/electron-browser/issue/issueReporter.js b/src/vs/code/electron-browser/issue/issueReporter.js index 53abd748d7b..62c33c672d3 100644 --- a/src/vs/code/electron-browser/issue/issueReporter.js +++ b/src/vs/code/electron-browser/issue/issueReporter.js @@ -7,7 +7,7 @@ const path = require('path'); const fs = require('fs'); -const remote = require('electron').remote; +const ipc = require('electron').ipcRenderer; function assign(destination, source) { return Object.keys(source) @@ -30,7 +30,7 @@ function uriFromPath(_path) { pathName = '/' + pathName; } - return encodeURI('file://' + pathName); + return encodeURI('file://' + pathName).replace(/#/g, '%23'); } function readFile(file) { @@ -97,9 +97,9 @@ function main() { window.addEventListener('keydown', function (e) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB) { - remote.getCurrentWebContents().toggleDevTools(); + ipc.send('vscode:toggleDevTools'); } else if (key === RELOAD_KB) { - remote.getCurrentWindow().reload(); + ipc.send('vscode:reloadWindow'); } }); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index a642157cdca..cd5c7bb1251 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -6,7 +6,7 @@ 'use strict'; import 'vs/css!./media/issueReporter'; -import { shell, ipcRenderer, webFrame, remote, clipboard } from 'electron'; +import { shell, ipcRenderer, webFrame, clipboard } from 'electron'; import { localize } from 'vs/nls'; import { $ } from 'vs/base/browser/dom'; import * as collections from 'vs/base/common/collections'; @@ -19,25 +19,25 @@ import { debounce } from 'vs/base/common/decorators'; import * as platform from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; -import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows'; import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; +import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures } from 'vs/platform/issue/common/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; +import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; @@ -91,7 +91,7 @@ export class IssueReporter extends Disposable { this.previewButton = new Button(document.getElementById('issue-reporter')); - ipcRenderer.on('issuePerformanceInfoResponse', (event, info) => { + ipcRenderer.on('vscode:issuePerformanceInfoResponse', (event, info) => { this.logService.trace('issueReporter: Received performance data'); this.issueReporterModel.update(info); this.receivedPerformanceInfo = true; @@ -102,7 +102,7 @@ export class IssueReporter extends Disposable { this.updatePreviewButtonState(); }); - ipcRenderer.on('issueSystemInfoResponse', (event, info) => { + ipcRenderer.on('vscode:issueSystemInfoResponse', (event, info) => { this.logService.trace('issueReporter: Received system data'); this.issueReporterModel.update({ systemInfo: info }); this.receivedSystemInfo = true; @@ -111,9 +111,9 @@ export class IssueReporter extends Disposable { this.updatePreviewButtonState(); }); - ipcRenderer.send('issueSystemInfoRequest'); + ipcRenderer.send('vscode:issueSystemInfoRequest'); if (configuration.data.issueType === IssueType.PerformanceIssue) { - ipcRenderer.send('issuePerformanceInfoRequest'); + ipcRenderer.send('vscode:issuePerformanceInfoRequest'); } this.logService.trace('issueReporter: Sent data requests'); @@ -308,7 +308,7 @@ export class IssueReporter extends Disposable { const issueType = parseInt((<HTMLInputElement>event.target).value); this.issueReporterModel.update({ issueType: issueType }); if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) { - ipcRenderer.send('issuePerformanceInfoRequest'); + ipcRenderer.send('vscode:issuePerformanceInfoRequest'); } this.updatePreviewButtonState(); this.render(); @@ -384,14 +384,14 @@ export class IssueReporter extends Disposable { this.previewButton.onDidClick(() => this.createIssue()); this.addEventListener('disableExtensions', 'click', () => { - ipcRenderer.send('workbenchCommand', 'workbench.action.reloadWindowWithExtensionsDisabled'); + ipcRenderer.send('vscode:workbenchCommand', 'workbench.action.reloadWindowWithExtensionsDisabled'); }); this.addEventListener('disableExtensions', 'keydown', (e: KeyboardEvent) => { e.stopPropagation(); if (e.keyCode === 13 || e.keyCode === 32) { - ipcRenderer.send('workbenchCommand', 'workbench.extensions.action.disableAll'); - ipcRenderer.send('workbenchCommand', 'workbench.action.reloadWindow'); + ipcRenderer.send('vscode:workbenchCommand', 'workbench.extensions.action.disableAll'); + ipcRenderer.send('vscode:workbenchCommand', 'workbench.action.reloadWindow'); } }); @@ -400,7 +400,7 @@ export class IssueReporter extends Disposable { // Cmd/Ctrl+Enter previews issue and closes window if (cmdOrCtrlKey && e.keyCode === 13) { if (this.createIssue()) { - remote.getCurrentWindow().close(); + ipcRenderer.send('vscode:closeIssueReporter'); } } @@ -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) { + (<HTMLInputElement>e.target).select(); + } + } + } }; } diff --git a/src/vs/code/electron-browser/processExplorer/processExplorer.js b/src/vs/code/electron-browser/processExplorer/processExplorer.js index 0a798fb876d..d351b0ff848 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorer.js +++ b/src/vs/code/electron-browser/processExplorer/processExplorer.js @@ -7,7 +7,7 @@ const path = require('path'); const fs = require('fs'); -const remote = require('electron').remote; +const ipc = require('electron').ipcRenderer; function assign(destination, source) { return Object.keys(source) @@ -30,7 +30,7 @@ function uriFromPath(_path) { pathName = '/' + pathName; } - return encodeURI('file://' + pathName); + return encodeURI('file://' + pathName).replace(/#/g, '%23'); } function readFile(file) { @@ -141,9 +141,9 @@ function main() { window.addEventListener('keydown', function (e) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB) { - remote.getCurrentWebContents().toggleDevTools(); + ipc.send('vscode:toggleDevTools'); } else if (key === RELOAD_KB) { - remote.getCurrentWindow().reload(); + ipc.send('vscode:reloadWindow'); } }); diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index 9c491c0dd95..645bf682472 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/processExplorer'; import { listProcesses, ProcessItem } from 'vs/base/node/ps'; -import { remote, webFrame, ipcRenderer, clipboard } from 'electron'; +import { webFrame, ipcRenderer, clipboard } from 'electron'; import { repeat } from 'vs/base/common/strings'; import { totalmem } from 'os'; import product from 'vs/platform/node/product'; @@ -15,6 +15,8 @@ import { localize } from 'vs/nls'; import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/common/issue'; import * as browser from 'vs/base/browser/browser'; import * as platform from 'vs/base/common/platform'; +import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; +import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu'; let processList: any[]; let mapPidToWindowTitle = new Map<number, string>(); @@ -137,29 +139,29 @@ function applyZoom(zoomLevel: number): void { function showContextMenu(e) { e.preventDefault(); - const menu = new remote.Menu(); + const items: IContextMenuItem[] = []; const pid = parseInt(e.currentTarget.id); if (pid && typeof pid === 'number') { - menu.append(new remote.MenuItem({ + items.push({ label: localize('killProcess', "Kill Process"), click() { process.kill(pid, 'SIGTERM'); } - })); + }); - menu.append(new remote.MenuItem({ + items.push({ label: localize('forceKillProcess', "Force Kill Process"), click() { process.kill(pid, 'SIGKILL'); } - })); + }); - menu.append(new remote.MenuItem({ + items.push({ type: 'separator' - })); + }); - menu.append(new remote.MenuItem({ + items.push({ label: localize('copy', "Copy"), click() { const row = document.getElementById(pid.toString()); @@ -167,9 +169,9 @@ function showContextMenu(e) { clipboard.writeText(row.innerText); } } - })); + }); - menu.append(new remote.MenuItem({ + items.push({ label: localize('copyAll', "Copy All"), click() { const processList = document.getElementById('process-list'); @@ -177,9 +179,9 @@ function showContextMenu(e) { clipboard.writeText(processList.innerText); } } - })); + }); } else { - menu.append(new remote.MenuItem({ + items.push({ label: localize('copyAll', "Copy All"), click() { const processList = document.getElementById('process-list'); @@ -187,10 +189,10 @@ function showContextMenu(e) { clipboard.writeText(processList.innerText); } } - })); + }); } - menu.popup(remote.getCurrentWindow()); + popup(items); } export function startup(data: ProcessExplorerData): void { @@ -198,7 +200,7 @@ export function startup(data: ProcessExplorerData): void { applyZoom(data.zoomLevel); // Map window process pids to titles, annotate process names with this when rendering to distinguish between them - ipcRenderer.on('windowsInfoResponse', (event, windows) => { + ipcRenderer.on('vscode:windowsInfoResponse', (event, windows) => { mapPidToWindowTitle = new Map<number, string>(); windows.forEach(window => mapPidToWindowTitle.set(window.pid, window.title)); }); @@ -206,7 +208,7 @@ export function startup(data: ProcessExplorerData): void { setInterval(() => { ipcRenderer.send('windowsInfoRequest'); - listProcesses(remote.process.pid).then(processes => { + listProcesses(data.pid).then(processes => { processList = getProcessList(processes); updateProcessInfo(processList); diff --git a/src/vs/code/electron-browser/proxy/auth.html b/src/vs/code/electron-browser/proxy/auth.html index e27ec2fc02e..f01e2b70601 100644 --- a/src/vs/code/electron-browser/proxy/auth.html +++ b/src/vs/code/electron-browser/proxy/auth.html @@ -79,11 +79,6 @@ </body> <script> - const electron = require('electron'); - const shell = electron.shell; - const ipc = electron.ipcRenderer; - const remote = electron.remote; - const currentWindow = remote.getCurrentWindow(); function promptForCredentials(data) { return new Promise((c, e) => { diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js index d7dd08b4e3d..06fa3ea00a8 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js @@ -38,12 +38,12 @@ function uriFromPath(_path) { pathName = '/' + pathName; } - return encodeURI('file://' + pathName); + return encodeURI('file://' + pathName).replace(/#/g, '%23'); } 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; @@ -102,7 +102,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/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 547d1bd99d6..8d33d957398 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -16,7 +16,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; @@ -27,22 +27,25 @@ import { RequestService } from 'vs/platform/request/electron-browser/requestServ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { combinedAppender, NullTelemetryService, ITelemetryAppender, NullAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc'; +import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; import { IWindowsService, ActiveWindowManager } from 'vs/platform/windows/common/windows'; -import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; +import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; import { ipcRenderer } from 'electron'; import { createSharedProcessContributions } from 'vs/code/electron-browser/sharedProcess/contrib/contributions'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; +import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsChannel } from 'vs/platform/localizations/common/localizationsIpc'; -import { DialogChannelClient } from 'vs/platform/dialogs/common/dialogIpc'; +import { LocalizationsChannel } from 'vs/platform/localizations/node/localizationsIpc'; +import { DialogChannelClient } from 'vs/platform/dialogs/node/dialogIpc'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { DownloadServiceChannelClient } from 'vs/platform/download/node/downloadIpc'; +import { DefaultURITransformer } from 'vs/base/common/uriIpc'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -84,15 +87,19 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I const activeWindowManager = new ActiveWindowManager(windowsService); const route = () => activeWindowManager.getActiveClientId(); + const dialogChannel = server.getChannel('dialog', { routeCall: route, routeEvent: route }); services.set(IDialogService, new DialogChannelClient(dialogChannel)); + const downloadChannel = server.getChannel('download', { routeCall: route, routeEvent: route }); + services.set(IDownloadService, new DownloadServiceChannelClient(downloadChannel, DefaultURITransformer)); + const instantiationService = new InstantiationService(services); instantiationService.invokeFunction(accessor => { const services = new ServiceCollection(); const environmentService = accessor.get(IEnvironmentService); - const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSourcePath } = environmentService; + const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; const telemetryLogService = new FollowerLogService(logLevelClient, createSpdLogService('telemetry', initData.logLevel, environmentService.logsPath)); let appInsightsAppender: ITelemetryAppender = NullAppender; @@ -102,7 +109,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I } server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); - if (!extensionDevelopmentPath && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { + if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { const config: ITelemetryServiceConfig = { appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), commonProperties: resolveCommonProperties(product.commit, pkg.version, configuration.machineId, installSourcePath), diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index a16543734c3..bb199729fac 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -5,16 +5,16 @@ 'use strict'; -import { app, ipcMain as ipc, systemPreferences } from 'electron'; +import { app, ipcMain as ipc, systemPreferences, shell, Event } from 'electron'; import * as platform from 'vs/base/common/platform'; import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows'; -import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; +import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { getShellEnvironment } from 'vs/code/node/shellEnv'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { UpdateChannel } from 'vs/platform/update/common/updateIpc'; +import { UpdateChannel } from 'vs/platform/update/node/updateIpc'; import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; import { Server, connect, Client } from 'vs/base/parts/ipc/node/ipc.net'; import { SharedProcess } from 'vs/code/electron-main/sharedProcess'; @@ -28,13 +28,13 @@ import { IStateService } from 'vs/platform/state/common/state'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IURLService } from 'vs/platform/url/common/url'; -import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/common/urlIpc'; +import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/node/urlIpc'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; import { ProxyAuthHandler } from './auth'; @@ -47,24 +47,27 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; import { CodeWindow } from 'vs/code/electron-main/window'; import { KeyboardLayoutMonitor } from 'vs/code/electron-main/keyboard'; import URI from 'vs/base/common/uri'; -import { WorkspacesChannel } from 'vs/platform/workspaces/common/workspacesIpc'; +import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc'; import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import { getMachineId } from 'vs/base/node/id'; import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32'; import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux'; import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin'; import { IIssueService } from 'vs/platform/issue/common/issue'; -import { IssueChannel } from 'vs/platform/issue/common/issueIpc'; +import { IssueChannel } from 'vs/platform/issue/node/issueIpc'; import { IssueService } from 'vs/platform/issue/electron-main/issueService'; -import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; +import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; import * as errors from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { 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 { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; +import { ILabelService } from 'vs/platform/label/common/label'; import { CodeMenu } from 'vs/code/electron-main/menus'; +import { hasArgs } from 'vs/platform/environment/node/argv'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; export class CodeApplication { @@ -87,7 +90,8 @@ export class CodeApplication { @ILifecycleService private lifecycleService: ILifecycleService, @IConfigurationService private configurationService: ConfigurationService, @IStateService private stateService: IStateService, - @IHistoryMainService private historyMainService: IHistoryMainService + @IHistoryMainService private historyMainService: IHistoryMainService, + @ILabelService private labelService: ILabelService ) { this.toDispose = [mainIpcServer, configurationService]; @@ -101,6 +105,9 @@ export class CodeApplication { process.on('uncaughtException', err => this.onUnexpectedError(err)); process.on('unhandledRejection', (reason: any, promise: Promise<any>) => errors.onUnexpectedError(reason)); + // Contextmenu via IPC support + registerContextMenuListener(); + app.on('will-quit', () => { this.logService.trace('App#will-quit: disposing resources'); @@ -122,36 +129,39 @@ export class CodeApplication { } }); - const isValidWebviewSource = (source: string): boolean => { - if (!source) { - return false; - } - if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') { - return true; - } - const srcUri: any = URI.parse(source.toLowerCase()).toString(); - return srcUri.startsWith(URI.file(this.environmentService.appRoot.toLowerCase()).toString()); - }; - + // Security related measures (https://electronjs.org/docs/tutorial/security) + // DO NOT CHANGE without consulting the documentation app.on('web-contents-created', (event: any, contents) => { contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => { + + // Ensure defaults delete webPreferences.preload; webPreferences.nodeIntegration = false; // Verify URLs being loaded - if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) { + if (this.isValidWebviewSource(params.src) && this.isValidWebviewSource(webPreferences.preloadURL)) { return; } + delete webPreferences.preloadUrl; + // Otherwise prevent loading this.logService.error('webContents#web-contents-created: Prevented webview attach'); + event.preventDefault(); }); contents.on('will-navigate', event => { this.logService.error('webContents#will-navigate: Prevented webcontent navigation'); + event.preventDefault(); }); + + contents.on('new-window', (event: Event, url: string) => { + event.preventDefault(); // prevent code that wants to open links + + shell.openExternal(url); + }); }); let macOpenFileURIs: URI[] = []; @@ -188,15 +198,15 @@ export class CodeApplication { this.windowsMainService.openNewWindow(OpenContext.DESKTOP); //macOS native tab "+" button }); - ipc.on('vscode:exit', (event: any, code: number) => { + ipc.on('vscode:exit', (event: Event, code: number) => { this.logService.trace('IPC#vscode:exit', code); this.dispose(); this.lifecycleService.kill(code); }); - ipc.on('vscode:fetchShellEnv', event => { - const webContents = event.sender.webContents; + ipc.on('vscode:fetchShellEnv', (event: Event) => { + const webContents = event.sender; getShellEnvironment().then(shellEnv => { if (!webContents.isDestroyed()) { webContents.send('vscode:acceptShellEnv', shellEnv); @@ -210,7 +220,7 @@ export class CodeApplication { }); }); - ipc.on('vscode:broadcast', (event: any, windowId: number, broadcast: { channel: string; payload: any; }) => { + ipc.on('vscode:broadcast', (event: Event, windowId: number, broadcast: { channel: string; payload: any; }) => { if (this.windowsMainService && broadcast.channel && !isUndefinedOrNull(broadcast.payload)) { this.logService.trace('IPC#vscode:broadcast', broadcast.channel, broadcast.payload); @@ -222,6 +232,22 @@ export class CodeApplication { } }); + ipc.on('vscode:labelRegisterFormater', (event: any, { scheme, formater }) => { + this.labelService.registerFormatter(scheme, formater); + }); + + ipc.on('vscode:toggleDevTools', (event: Event) => { + event.sender.toggleDevTools(); + }); + + ipc.on('vscode:openDevTools', (event: Event) => { + event.sender.openDevTools(); + }); + + ipc.on('vscode:reloadWindow', (event: Event) => { + event.sender.reload(); + }); + // Keyboard layout changes KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => { if (this.windowsMainService) { @@ -230,6 +256,20 @@ export class CodeApplication { }); } + private isValidWebviewSource(source: string): boolean { + if (!source) { + return false; + } + + if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') { + return true; + } + + const srcUri: any = URI.parse(source.toLowerCase()).toString(); + + return srcUri.startsWith(URI.file(this.environmentService.appRoot.toLowerCase()).toString()); + } + private onUnexpectedError(err: Error): void { if (err) { @@ -257,7 +297,7 @@ export class CodeApplication { if (event === 'vscode:changeColorTheme' && typeof payload === 'string') { let data = JSON.parse(payload); - this.stateService.setItem(CodeWindow.themeStorageKey, data.id); + this.stateService.setItem(CodeWindow.themeStorageKey, data.baseTheme); this.stateService.setItem(CodeWindow.themeBackgroundStorageKey, data.background); } } @@ -462,12 +502,16 @@ export class CodeApplication { // Open our first window const macOpenFiles = (<any>global).macOpenFiles as string[]; const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; - if (args['new-window'] && args._.length === 0 && (args['folder-uri'] || []).length === 0) { + const hasCliArgs = hasArgs(args._); + const hasFolderURIs = hasArgs(args['folder-uri']); + const hasFileURIs = hasArgs(args['file-uri']); + + if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { 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 || !args['folder-uri'] || !args['folder-uri'].length)) { + } else if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { 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 + this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli } } @@ -511,7 +555,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); @@ -524,7 +568,9 @@ export class CodeApplication { this.historyMainService.onRecentlyOpenedChange(() => this.historyMainService.updateWindowsJumpList()); // Start shared process after a while - TPromise.timeout(3000).then(() => this.sharedProcess.spawn()); + const sharedProcess = new RunOnceScheduler(() => this.sharedProcess.spawn(), 3000); + sharedProcess.schedule(); + this.toDispose.push(sharedProcess); } private dispose(): void { diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index 2cad34a8d84..352720bb914 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -6,7 +6,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { ILogService } from 'vs/platform/log/common/log'; import { IURLService } from 'vs/platform/url/common/url'; import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; @@ -20,6 +20,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import URI, { UriComponents } from 'vs/base/common/uri'; import { BrowserWindow } from 'electron'; import { Event } from 'vs/base/common/event'; +import { hasArgs } from 'vs/platform/environment/node/argv'; export const ID = 'launchService'; export const ILaunchService = createDecorator<ILaunchService>(ID); @@ -178,7 +179,7 @@ export class LaunchService implements ILaunchService { } // Start without file/folder arguments - else if (args._.length === 0 && (args['folder-uri'] || []).length === 0) { + else if (!hasArgs(args._) && !hasArgs(args['folder-uri']) && !hasArgs(args['file-uri'])) { let openNewWindow = false; // Force new window diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 134f4379fb3..683164272f8 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -5,7 +5,7 @@ 'use strict'; -import 'vs/code/electron-main/contributions'; +import 'vs/code/code.main'; import { app, dialog } from 'electron'; import { assign } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; @@ -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 { ILabelService, LabelService } from 'vs/platform/label/common/label'; 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 labelService = new LabelService(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(ILabelService, labelService); 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 84c15cd5cf0..1e883015480 100644 --- a/src/vs/code/electron-main/menubar.ts +++ b/src/vs/code/electron-main/menubar.ts @@ -16,47 +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, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { IMenubarData, IMenubarMenuItemAction, IMenubarMenuItemSeparator } from 'vs/platform/menubar/common/menubar'; +import { IWorkspaceIdentifier, 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'; - -// interface IExtensionViewlet { -// id: string; -// label: string; -// } +import { ILabelService } from 'vs/platform/label/common/label'; 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, @@ -65,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, + @ILabelService private labelService: ILabelService ) { - // 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(); @@ -86,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()); @@ -112,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 { @@ -134,8 +115,14 @@ export class Menubar { return enableNativeTabs; } - updateMenu(menus: IMenubarData, windowId: number) { + updateMenu(menus: IMenubarData, windowId: number, additionalKeybindings?: Array<IMenubarKeybinding>) { this.menubarMenus = menus; + if (additionalKeybindings) { + additionalKeybindings.forEach(keybinding => { + this.keybindings[keybinding.id] = keybinding; + }); + } + this.scheduleUpdateMenu(); } @@ -165,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(); } @@ -229,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 }); @@ -278,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 }); @@ -291,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); @@ -309,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 @@ -334,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' }); @@ -355,6 +352,14 @@ export class Menubar { const actions = [about]; actions.push(...checkForUpdates); + + if (preferences) { + actions.push(...[ + __separator__(), + preferences + ]); + } + actions.push(...[ __separator__(), services, @@ -370,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 { @@ -395,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; @@ -481,49 +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<MenubarMenuItem>) { + items.forEach((item: MenubarMenuItem) => { + if (isMenubarMenuItemSeparator(item)) { menu.append(__separator__()); - } else { - let menuItem: Electron.MenuItem; - let action: IMenubarMenuItemAction = <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 createOpenRecentMenuItem(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): Electron.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(workspaceOrFile: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, commandId: string, isFile: boolean): Electron.MenuItem { let label: string; let uri: URI; - if (isSingleFolderWorkspaceIdentifier(workspace)) { - label = unmnemonicLabel(getWorkspaceLabel(workspace, this.environmentService, { verbose: true })); - uri = workspace; - } else if (isWorkspaceIdentifier(workspace)) { - label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); - uri = URI.file(workspace.configPath); + if (isSingleFolderWorkspaceIdentifier(workspaceOrFile) && !isFile) { + label = unmnemonicLabel(this.labelService.getWorkspaceLabel(workspaceOrFile, { verbose: true })); + uri = workspaceOrFile; + } else if (isWorkspaceIdentifier(workspaceOrFile)) { + label = this.labelService.getWorkspaceLabel(workspaceOrFile, { verbose: true }); + uri = URI.file(workspaceOrFile.configPath); } else { - label = unmnemonicLabel(getPathLabel(workspace, this.environmentService, null)); - uri = URI.file(workspace); + label = unmnemonicLabel(this.labelService.getUriLabel(workspaceOrFile)); + uri = workspaceOrFile; } return new MenuItem(this.likeAction(commandId, { @@ -539,7 +586,7 @@ export class Menubar { }).length > 0; if (!success) { - this.historyMainService.removeFromRecentlyOpened([workspace]); + this.historyMainService.removeFromRecentlyOpened([workspaceOrFile]); } } }, false)); @@ -560,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)); @@ -665,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)); } @@ -679,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 54cf3480930..118192b01b4 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -18,12 +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, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import URI from 'vs/base/common/uri'; +import { ILabelService } from 'vs/platform/label/common/label'; interface IMenuItemClickHandler { inDevTools: (contents: Electron.WebContents) => void; @@ -66,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, + @ILabelService private labelService: ILabelService ) { this.nativeTabMenuItems = []; @@ -240,16 +242,16 @@ export class CodeMenu { const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu }); this.setGotoMenu(gotoMenu); - // Terminal - const terminalMenu = new Menu(); - const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "Ter&&minal")), submenu: terminalMenu }); - this.setTerminalMenu(terminalMenu); - // Debug const debugMenu = new Menu(); const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu }); this.setDebugMenu(debugMenu); + // Terminal + const terminalMenu = new Menu(); + const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal")), submenu: terminalMenu }); + this.setTerminalMenu(terminalMenu); + // Mac: Window let macWindowMenuItem: Electron.MenuItem; if (isMacintosh) { @@ -263,10 +265,6 @@ export class CodeMenu { const helpMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help")), submenu: helpMenu, role: 'help' }); this.setHelpMenu(helpMenu); - // Tasks - const taskMenu = new Menu(); - const taskMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTask', comment: ['&& denotes a mnemonic'] }, "&&Tasks")), submenu: taskMenu }); - this.setTaskMenu(taskMenu); // Menu Structure if (macApplicationMenuItem) { @@ -278,9 +276,8 @@ export class CodeMenu { menubar.append(selectionMenuItem); menubar.append(viewMenuItem); menubar.append(gotoMenuItem); - menubar.append(terminalMenuItem); menubar.append(debugMenuItem); - menubar.append(taskMenuItem); + menubar.append(terminalMenuItem); if (macWindowMenuItem) { menubar.append(macWindowMenuItem); @@ -436,7 +433,8 @@ export class CodeMenu { } private getPreferencesMenu(): Electron.MenuItem { - const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openSettings2'); + const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openSettings'); + const extensions = this.createMenuItem(nls.localize({ key: 'miOpenExtensions', comment: ['&& denotes a mnemonic'] }, '&&Extensions'), 'workbench.view.extensions'); const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings'); const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions'); const snippetsSettings = this.createMenuItem(nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), 'workbench.action.openSnippets'); @@ -445,6 +443,7 @@ export class CodeMenu { const preferencesMenu = new Menu(); preferencesMenu.append(settings); + preferencesMenu.append(extensions); preferencesMenu.append(__separator__()); preferencesMenu.append(kebindingSettings); preferencesMenu.append(keymapExtensions); @@ -492,14 +491,14 @@ export class CodeMenu { let label: string; let uri: URI; if (isSingleFolderWorkspaceIdentifier(workspace)) { - label = unmnemonicLabel(getWorkspaceLabel(workspace, this.environmentService, { verbose: true })); + label = unmnemonicLabel(this.labelService.getWorkspaceLabel(workspace, { verbose: true })); uri = workspace; } else if (isWorkspaceIdentifier(workspace)) { - label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); + label = this.labelService.getWorkspaceLabel(workspace, { verbose: true }); uri = URI.file(workspace.configPath); } else { - label = unmnemonicLabel(getPathLabel(workspace, this.environmentService, null)); uri = URI.file(workspace); + label = unmnemonicLabel(this.labelService.getUriLabel(uri)); } return new MenuItem(this.likeAction(commandId, { @@ -509,7 +508,8 @@ export class CodeMenu { const success = this.windowsMainService.open({ context: OpenContext.MENU, cli: this.environmentService.args, - urisToOpen: [uri], forceNewWindow: openInNewWindow, + urisToOpen: [uri], + forceNewWindow: openInNewWindow, forceOpenWorkspaceAsFile: isFile }).length > 0; @@ -744,7 +744,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, @@ -880,28 +880,33 @@ export class CodeMenu { private setTerminalMenu(terminalMenu: Electron.Menu): void { const newTerminal = this.createMenuItem(nls.localize({ key: 'miNewTerminal', comment: ['&& denotes a mnemonic'] }, "&&New Terminal"), 'workbench.action.terminal.new'); const splitTerminal = this.createMenuItem(nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal"), 'workbench.action.terminal.split'); - const killTerminal = this.createMenuItem(nls.localize({ key: 'miKillTerminal', comment: ['&& denotes a mnemonic'] }, "&&Kill Terminal"), 'workbench.action.terminal.kill'); - const clear = this.createMenuItem(nls.localize({ key: 'miClear', comment: ['&& denotes a mnemonic'] }, "&&Clear"), 'workbench.action.terminal.clear'); + const runActiveFile = this.createMenuItem(nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File"), 'workbench.action.terminal.runActiveFile'); const runSelectedText = this.createMenuItem(nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text"), 'workbench.action.terminal.runSelectedText'); - const scrollToPreviousCommand = this.createMenuItem(nls.localize({ key: 'miScrollToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Previous Command"), 'workbench.action.terminal.scrollToPreviousCommand'); - const scrollToNextCommand = this.createMenuItem(nls.localize({ key: 'miScrollToNextCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Next Command"), 'workbench.action.terminal.scrollToNextCommand'); - const selectToPreviousCommand = this.createMenuItem(nls.localize({ key: 'miSelectToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Select To Previous Command"), 'workbench.action.terminal.selectToPreviousCommand'); - const selectToNextCommand = this.createMenuItem(nls.localize({ key: 'miSelectToNextCommand', comment: ['&& denotes a mnemonic'] }, "Select To Next Command"), 'workbench.action.terminal.selectToNextCommand'); + + const runTask = this.createMenuItem(nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task..."), 'workbench.action.tasks.runTask'); + const buildTask = this.createMenuItem(nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task..."), 'workbench.action.tasks.build'); + const showTasks = this.createMenuItem(nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks..."), 'workbench.action.tasks.showTasks'); + const restartTask = this.createMenuItem(nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task..."), 'workbench.action.tasks.restartTask'); + const terminateTask = this.createMenuItem(nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task..."), 'workbench.action.tasks.terminate'); + const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks..."), 'workbench.action.tasks.configureTaskRunner'); + const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task..."), 'workbench.action.tasks.configureDefaultBuildTask'); const menuItems: MenuItem[] = [ newTerminal, splitTerminal, - killTerminal, __separator__(), - clear, + runTask, + buildTask, runActiveFile, runSelectedText, __separator__(), - scrollToPreviousCommand, - scrollToNextCommand, - selectToPreviousCommand, - selectToNextCommand + terminateTask, + restartTask, + showTasks, + __separator__(), + configureTask, + configureBuildTask ]; menuItems.forEach(item => terminalMenu.append(item)); @@ -968,6 +973,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)); @@ -1081,29 +1087,6 @@ export class CodeMenu { } } - private setTaskMenu(taskMenu: Electron.Menu): void { - const runTask = this.createMenuItem(nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task..."), 'workbench.action.tasks.runTask'); - const buildTask = this.createMenuItem(nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task..."), 'workbench.action.tasks.build'); - const showTasks = this.createMenuItem(nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks..."), 'workbench.action.tasks.showTasks'); - const restartTask = this.createMenuItem(nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task..."), 'workbench.action.tasks.restartTask'); - const terminateTask = this.createMenuItem(nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task..."), 'workbench.action.tasks.terminate'); - const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks..."), 'workbench.action.tasks.configureTaskRunner'); - const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task..."), 'workbench.action.tasks.configureDefaultBuildTask'); - - [ - //__separator__(), - runTask, - buildTask, - __separator__(), - terminateTask, - restartTask, - showTasks, - __separator__(), - configureTask, - configureBuildTask - ].forEach(item => taskMenu.append(item)); - } - private openAccessibilityOptions(): void { const win = new BrowserWindow({ alwaysOnTop: true, diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 11099c63863..5f135d7e4e2 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -10,7 +10,7 @@ import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; import URI from 'vs/base/common/uri'; import { IStateService } from 'vs/platform/state/common/state'; -import { shell, screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage } from 'electron'; +import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage } from 'electron'; import { TPromise, TValueCallback } from 'vs/base/common/winjs.base'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; @@ -24,7 +24,7 @@ import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/elect import { IWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { mark, exportEntries } from 'vs/base/common/performance'; +import * as perf from 'vs/base/common/performance'; import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/node/extensionGalleryService'; export interface IWindowCreationOptions { @@ -140,7 +140,11 @@ export class CodeWindow implements ICodeWindow { show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { - 'backgroundThrottling': false, // by default if Code is in the background, intervals and timeouts get throttled, + // By default if Code is in the background, intervals and timeouts get throttled, so we + // want to enforce that Code stays in the foreground. This triggers a disable_hidden_ + // flag that Electron provides via patch: + // https://github.com/electron/libchromiumcontent/blob/master/patches/common/chromium/disable_hidden.patch + 'backgroundThrottling': false, disableBlinkFeatures: 'Auxclick' // disable auxclick events (see https://developers.google.com/web/updates/2016/10/auxclick) } }; @@ -193,23 +197,6 @@ export class CodeWindow implements ICodeWindow { this._win = new BrowserWindow(options); this._id = this._win.id; - // Bug in Electron (https://github.com/electron/electron/issues/10862). On multi-monitor setups, - // it can happen that the position we set to the window is not the correct one on the display. - // To workaround, we ask the window for its position and set it again if not matching. - // This only applies if the window is not fullscreen or maximized and multiple monitors are used. - if (isWindows && !isFullscreenOrMaximized) { - try { - if (screen.getAllDisplays().length > 1) { - const [x, y] = this._win.getPosition(); - if (x !== this.windowState.x || y !== this.windowState.y) { - this._win.setPosition(this.windowState.x, this.windowState.y, false); - } - } - } catch (err) { - this.logService.warn(`Unexpected error fixing window position on windows with multiple windows: ${err}\n${err.stack}`); - } - } - if (useCustomTitleStyle) { this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any } @@ -388,13 +375,6 @@ export class CodeWindow implements ICodeWindow { // App commands support this.registerNavigationListenerOn('app-command', 'browser-backward', 'browser-forward', false); - // Handle code that wants to open links - this._win.webContents.on('new-window', (event: Event, url: string) => { - event.preventDefault(); - - shell.openExternal(url); - }); - // Window Focus this._win.on('focus', () => { this._lastFocusTime = Date.now(); @@ -431,16 +411,6 @@ export class CodeWindow implements ICodeWindow { this.logService.warn('[electron event]: fail to load, ', errorDescription); }); - // Prevent any kind of navigation triggered by the user! - // But do not touch this in dev version because it will prevent "Reload" from dev tools - if (this.environmentService.isBuilt) { - this._win.webContents.on('will-navigate', (event: Event) => { - if (event) { - event.preventDefault(); - } - }); - } - // Handle configuration changes this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated())); @@ -517,6 +487,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 @@ -558,7 +534,7 @@ export class CodeWindow implements ICodeWindow { } // Load URL - mark('main:loadWindow'); + perf.mark('main:loadWindow'); this._win.loadURL(this.getUrl(configuration)); // Make window visible if it did not open in N seconds because this indicates an error @@ -628,18 +604,12 @@ export class CodeWindow implements ICodeWindow { windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme(); windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled(); - // Theme - windowConfiguration.baseTheme = this.getBaseTheme(); - windowConfiguration.backgroundColor = this.getBackgroundColor(); - // Title style related windowConfiguration.maximized = this._win.isMaximized(); windowConfiguration.frameless = this.hasHiddenTitleBarStyle() && !isMacintosh; - // Perf Counters - windowConfiguration.perfEntries = exportEntries(); - windowConfiguration.perfStartTime = (<any>global).perfStartTime; - windowConfiguration.perfWindowLoadTime = Date.now(); + // Dump Perf Counters + windowConfiguration.perfEntries = perf.exportEntries(); // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv); @@ -987,11 +957,6 @@ export class CodeWindow implements ICodeWindow { this.touchBarGroups.push(groupTouchBar); } - // Ugly workaround for native crash on macOS 10.12.1. We are not - // leveraging the API for changing the ESC touch bar item. - // See https://github.com/electron/electron/issues/10442 - (<any>this._win)._setEscapeTouchBarItem = () => { }; - this._win.setTouchBar(new TouchBar({ items: this.touchBarGroups })); } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 482676324a4..55d5cf9e53c 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -14,6 +14,7 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { IStateService } from 'vs/platform/state/common/state'; import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; +import { hasArgs, asArray } from 'vs/platform/environment/node/argv'; import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron'; import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths'; import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; @@ -36,7 +37,8 @@ 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'; +import { getComparisonKey, isEqual, normalizePath } from 'vs/base/common/resources'; +import { endsWith } from 'vs/base/common/strings'; enum WindowError { UNRESPONSIVE, @@ -81,6 +83,7 @@ interface IOpenBrowserWindowOptions { filesToWait?: IPathsToWaitFor; forceNewWindow?: boolean; + forceNewTabbedWindow?: boolean; windowToUse?: ICodeWindow; emptyWindowBackupFolder?: string; @@ -325,7 +328,7 @@ export class WindowsManager implements IWindowsMainService { 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.openedFolderUri && o.folderUri && isEqual(o.folderUri, win.openedFolderUri, hasToIgnoreCase(o.folderUri)); + const sameFolder = win.openedFolderUri && o.folderUri && isEqual(o.folderUri, win.openedFolderUri); if (sameWorkspace || sameFolder) { o.uiState = state.uiState; @@ -365,8 +368,8 @@ export class WindowsManager implements IWindowsMainService { pathsToOpen = pathsToOpen.filter(path => !path.folderUri); } - let filesToOpen = pathsToOpen.filter(path => !!path.filePath && !path.createFilePath); - let filesToCreate = pathsToOpen.filter(path => !!path.filePath && path.createFilePath); + let filesToOpen = pathsToOpen.filter(path => !!path.fileUri && !path.createFilePath); + let filesToCreate = pathsToOpen.filter(path => !!path.fileUri && path.createFilePath); // When run with --diff, take the files to open as files to diff // if there are exactly two files provided. @@ -391,7 +394,7 @@ 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.folderUri && !win.filePath).map(win => win.folderUri), folder => getComparisonKey(folder)); // prevent duplicates + const foldersToOpen = arrays.distinct(pathsToOpen.filter(win => win.folderUri && !win.fileUri).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!) @@ -413,7 +416,7 @@ export class WindowsManager implements IWindowsMainService { // // These are empty windows to open // - const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderUri && !win.filePath && !win.backupPath).length; + const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderUri && !win.fileUri && !win.backupPath).length; // Open based on config const usedWindows = this.doOpen(openConfig, workspacesToOpen, workspacesToRestore, foldersToOpen, foldersToRestore, emptyToRestore, emptyToOpen, filesToOpen, filesToCreate, filesToDiff, filesToWait, foldersToAdd); @@ -421,7 +424,7 @@ export class WindowsManager implements IWindowsMainService { // 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.cli['folder-uri'] || []).length && !(openConfig.urisToOpen || []).length; + let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !(openConfig.urisToOpen && openConfig.urisToOpen.length); let focusLastOpened = true; let focusLastWindow = true; @@ -441,7 +444,7 @@ export class WindowsManager implements IWindowsMainService { const usedWindow = usedWindows[i]; if ( (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace - (usedWindow.openedFolderUri && foldersToRestore.some(folder => isEqual(folder, usedWindow.openedFolderUri, hasToIgnoreCase(folder)))) || // skip over restored folder + (usedWindow.openedFolderUri && foldersToRestore.some(folder => isEqual(folder, usedWindow.openedFolderUri))) || // skip over restored folder (usedWindow.backupPath && emptyToRestore.some(empty => empty === basename(usedWindow.backupPath))) // skip over restored empty window ) { continue; @@ -463,13 +466,13 @@ export class WindowsManager implements IWindowsMainService { // Also do not add paths when files are opened for diffing, only if opened individually if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.cli.diff) { const recentlyOpenedWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[] = []; - const recentlyOpenedFiles: string[] = []; + const recentlyOpenedFiles: URI[] = []; pathsToOpen.forEach(win => { if (win.workspace || win.folderUri) { recentlyOpenedWorkspaces.push(win.workspace || win.folderUri); - } else if (win.filePath) { - recentlyOpenedFiles.push(win.filePath); + } else if (win.fileUri) { + recentlyOpenedFiles.push(win.fileUri); } }); @@ -539,18 +542,10 @@ export class WindowsManager implements IWindowsMainService { newWindow: openFilesInNewWindow, reuseWindow: openConfig.forceReuseWindow, context: openConfig.context, - filePath: fileToCheck && fileToCheck.filePath, + fileUri: fileToCheck && fileToCheck.fileUri, workspaceResolver: workspace => this.workspacesMainService.resolveWorkspaceSync(workspace.configPath) }); - // 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: #54483 Ben This should not happen - console.error(`This should not happen`, bestWindowOrFolder, WindowsManager.WINDOWS); - bestWindowOrFolder = !openFilesInNewWindow ? this.getLastActiveWindow() : null; - } - // We found a window to open the files in if (bestWindowOrFolder instanceof CodeWindow) { @@ -578,13 +573,6 @@ export class WindowsManager implements IWindowsMainService { } } - // We found a suitable folder to open: add it to foldersToOpen - else if (typeof bestWindowOrFolder === 'string') { - //TODO: #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 else { usedWindows.push(this.openInBrowserWindow({ @@ -595,7 +583,8 @@ export class WindowsManager implements IWindowsMainService { filesToCreate, filesToDiff, filesToWait, - forceNewWindow: true + forceNewWindow: true, + forceNewTabbedWindow: openConfig.forceNewTabbedWindow })); // Reset these because we handled them @@ -671,7 +660,7 @@ export class WindowsManager implements IWindowsMainService { // Open remaining ones allFoldersToOpen.forEach(folderToOpen => { - if (windowsOnFolderPath.some(win => isEqual(win.openedFolderUri, folderToOpen, hasToIgnoreCase(win.openedFolderUri)))) { + if (windowsOnFolderPath.some(win => isEqual(win.openedFolderUri, folderToOpen))) { return; // ignore folders that are already open } @@ -700,6 +689,7 @@ export class WindowsManager implements IWindowsMainService { filesToDiff, filesToWait, forceNewWindow: true, + forceNewTabbedWindow: openConfig.forceNewTabbedWindow, emptyWindowBackupFolder })); @@ -720,7 +710,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 @@ -767,6 +758,7 @@ export class WindowsManager implements IWindowsMainService { filesToDiff, filesToWait, forceNewWindow, + forceNewTabbedWindow: openConfig.forceNewTabbedWindow, windowToUse }); @@ -789,7 +781,7 @@ export class WindowsManager implements IWindowsMainService { } // Extract paths: from CLI - else if (openConfig.cli._.length > 0 || (openConfig.cli['folder-uri'] || []).length > 0) { + else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri'])) { windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli); isCommandLineOrAPICall = true; } @@ -818,48 +810,74 @@ export class WindowsManager implements IWindowsMainService { } private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPath[] { - let pathsToOpen = openConfig.urisToOpen.map(pathToOpen => { - const path = this.parseUri(pathToOpen, { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }); + let pathsToOpen = []; + let parseOptions = { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }; + for (const pathToOpen of openConfig.urisToOpen) { + if (!pathToOpen) { + continue; + } - // Warn if the requested path to open does not exist - if (!path) { + const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile, parseOptions); + if (path) { + pathsToOpen.push(path); + } else { + // Warn about the invalid URI or path + + let message, detail; + if (pathToOpen.scheme === Schemas.file) { + message = localize('pathNotExistTitle', "Path does not exist"); + detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen.fsPath); + } else { + message = localize('uriInvalidTitle', "URI can not be opened"); + detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", pathToOpen.toString()); + } const options: Electron.MessageBoxOptions = { title: product.nameLong, 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.scheme === Schemas.file ? pathToOpen.fsPath : pathToOpen.path), + message, + detail, noLink: true }; this.dialogs.showMessageBox(options, this.getFocusedWindow()); } - - return path; - }); - - // get rid of nulls - pathsToOpen = arrays.coalesce(pathsToOpen); - + } return pathsToOpen; } private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] { const pathsToOpen = []; + const parseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto }; // 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 })))); + const folderUris = asArray(cli['folder-uri']); + for (let folderUri of folderUris) { + const path = this.parseUri(this.argToUri(folderUri), false, parseOptions); + if (path) { + pathsToOpen.push(path); + } + } + + // file uris + const fileUris = asArray(cli['file-uri']); + for (let fileUri of fileUris) { + const path = this.parseUri(this.argToUri(fileUri), true, parseOptions); + if (path) { + pathsToOpen.push(path); + } } // folder or file paths - if (cli._ && cli._.length) { - pathsToOpen.push(...arrays.coalesce(cli._.map(candidate => this.parsePath(candidate, { ignoreFileNotFound: true, gotoLineMode: cli.goto })))); + const cliArgs = asArray(cli._); + for (let cliArg of cliArgs) { + const path = this.parsePath(cliArg, parseOptions); + if (path) { + pathsToOpen.push(path); + } } - if (pathsToOpen.length > 0) { + if (pathsToOpen.length) { return pathsToOpen; } @@ -892,7 +910,7 @@ export class WindowsManager implements IWindowsMainService { // folder (if path is valid) else if (lastActiveWindow.folderUri) { - const validatedFolder = this.parseUri(lastActiveWindow.folderUri); + const validatedFolder = this.parseUri(lastActiveWindow.folderUri, false); if (validatedFolder && validatedFolder.folderUri) { return [validatedFolder]; } @@ -923,7 +941,7 @@ export class WindowsManager implements IWindowsMainService { if (lastActiveWindow && lastActiveWindow.folderUri) { folderCandidates.push(lastActiveWindow.folderUri); } - windowsToOpen.push(...folderCandidates.map(candidate => this.parseUri(candidate)).filter(window => window && window.folderUri)); + windowsToOpen.push(...folderCandidates.map(candidate => this.parseUri(candidate, false)).filter(window => window && window.folderUri)); // Windows that were Empty if (restoreWindows === 'all') { @@ -963,25 +981,47 @@ 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; + private argToUri(arg: string): URI { + try { + let uri = URI.parse(arg); + if (!uri.scheme) { + this.logService.error(`Invalid URI input string, scheme missing: ${arg}`); + return null; + } + return uri; + } catch (e) { + this.logService.error(`Invalid URI input string: ${arg}, ${e.message}`); } - return URI.parse(arg); + return null; } - private parseUri(anyUri: URI, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen { - if (!anyUri || !anyUri.scheme) { + private parseUri(uri: URI, isFile: boolean, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen { + if (!uri || !uri.scheme) { return null; } - - if (anyUri.scheme === Schemas.file) { - return this.parsePath(anyUri.fsPath, options); + if (uri.scheme === Schemas.file) { + return this.parsePath(uri.fsPath, options); + } + // normalize URI + uri = normalizePath(uri); + if (endsWith(uri.path, '/')) { + uri = uri.with({ path: uri.path.substr(0, uri.path.length - 1) }); + } + if (isFile) { + if (options && options.gotoLineMode) { + const parsedPath = parseLineAndColumnAware(uri.path); + return { + fileUri: uri.with({ path: parsedPath.path }), + lineNumber: parsedPath.line, + columnNumber: parsedPath.column + }; + } + return { + fileUri: uri + }; } - return { - folderUri: anyUri + folderUri: uri }; } @@ -1014,7 +1054,7 @@ export class WindowsManager implements IWindowsMainService { // File return { - filePath: candidate, + fileUri: URI.file(candidate), lineNumber: gotoLineMode ? parsedPath.line : void 0, columnNumber: gotoLineMode ? parsedPath.column : void 0 }; @@ -1030,10 +1070,11 @@ export class WindowsManager implements IWindowsMainService { } } } catch (error) { - this.historyMainService.removeFromRecentlyOpened([candidate]); // since file does not seem to exist anymore, remove from recent + const fileUri = URI.file(candidate); + this.historyMainService.removeFromRecentlyOpened([fileUri]); // since file does not seem to exist anymore, remove from recent if (options && options.ignoreFileNotFound) { - return { filePath: candidate, createFilePath: true }; // assume this is a file that does not yet exist + return { fileUri, createFilePath: true }; // assume this is a file that does not yet exist } } @@ -1093,45 +1134,55 @@ export class WindowsManager implements IWindowsMainService { return; } + let folderUris = asArray(openConfig.cli['folder-uri']); + let fileUris = asArray(openConfig.cli['file-uri']); + let cliArgs = openConfig.cli._; // Fill in previously opened workspace unless an explicit path is provided and we are not unit testing - if (openConfig.cli._.length === 0 && (openConfig.cli['folder-uri'] || []).length === 0 && !openConfig.cli.extensionTestsPath) { + if (!cliArgs.length && !folderUris.length && !fileUris.length && !openConfig.cli.extensionTestsPath) { const extensionDevelopmentWindowState = this.windowsState.lastPluginDevelopmentHostWindow; const workspaceToOpen = extensionDevelopmentWindowState && (extensionDevelopmentWindowState.workspace || extensionDevelopmentWindowState.folderUri); if (workspaceToOpen) { if (isSingleFolderWorkspaceIdentifier(workspaceToOpen)) { if (workspaceToOpen.scheme === Schemas.file) { - openConfig.cli._ = [workspaceToOpen.fsPath]; + cliArgs = [workspaceToOpen.fsPath]; } else { - openConfig.cli['folder-uri'] = [workspaceToOpen.toString()]; + folderUris = [workspaceToOpen.toString()]; } } else { - openConfig.cli._ = [workspaceToOpen.configPath]; + cliArgs = [workspaceToOpen.configPath]; } } } // Make sure we are not asked to open a workspace or folder that is already opened - 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'] = []; - } + if (cliArgs.length && cliArgs.some(path => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, URI.file(path)))) { + cliArgs = []; } + if (folderUris.length && folderUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) { + folderUris = []; + } + + if (fileUris.length && fileUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) { + fileUris = []; + } + + openConfig.cli._ = cliArgs; + openConfig.cli['folder-uri'] = folderUris; + openConfig.cli['file-uri'] = fileUris; + // Open it - this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0 && (openConfig.cli['folder-uri'] || []).length === 0, userEnv: openConfig.userEnv }); + this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length, userEnv: openConfig.userEnv }); } private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow { + // Build IWindowConfiguration from config and options const configuration: IWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI configuration.appRoot = this.environmentService.appRoot; configuration.machineId = this.machineId; + configuration.mainPid = process.pid; configuration.execPath = process.execPath; configuration.userEnv = assign({}, this.initialUserEnv, options.userEnv || {}); configuration.isInitialStartup = options.initialStartup; @@ -1152,7 +1203,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(); @@ -1179,12 +1230,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); @@ -1265,7 +1325,7 @@ export class WindowsManager implements IWindowsMainService { // Known Folder - load from stored settings if (configuration.folderUri) { - const stateForFolder = this.windowsState.openedWindows.filter(o => o.folderUri && isEqual(o.folderUri, configuration.folderUri, hasToIgnoreCase(o.folderUri))).map(o => o.uiState); + const stateForFolder = this.windowsState.openedWindows.filter(o => o.folderUri && isEqual(o.folderUri, configuration.folderUri)).map(o => o.uiState); if (stateForFolder.length) { return stateForFolder[0]; } @@ -1475,6 +1535,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<void> { return new TPromise<void>(c => { function handler(id: number) { diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 3ca3fd9ec94..a5f35fb14c1 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -87,18 +87,17 @@ export async function main(argv: string[]): Promise<any> { // Write source to target const data = fs.readFileSync(source); - try { + if (isWindows) { + // On Windows we use a different strategy of saving the file + // by first truncating the file and then writing with r+ mode. + // This helps to save hidden files on Windows + // (see https://github.com/Microsoft/vscode/issues/931) and + // prevent removing alternate data streams + // (see https://github.com/Microsoft/vscode/issues/6363) + fs.truncateSync(target, 0); + writeFileAndFlushSync(target, data, { flag: 'r+' }); + } else { writeFileAndFlushSync(target, data); - } catch (error) { - // On Windows and if the file exists with an EPERM error, we try a different strategy of saving the file - // by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows - // (see https://github.com/Microsoft/vscode/issues/931) - if (isWindows && error.code === 'EPERM') { - fs.truncateSync(target, 0); - writeFileAndFlushSync(target, data, { flag: 'r+' }); - } else { - throw error; - } } // Restore previous mode as needed diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 08bd0945583..2e2ff5d98af 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -41,6 +41,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService'; import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import Severity from 'vs/base/common/severity'; +import URI from 'vs/base/common/uri'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); @@ -101,7 +102,7 @@ class Main { .map(id => () => { const extension = path.isAbsolute(id) ? id : path.join(process.cwd(), id); - return this.extensionManagementService.install(extension).then(() => { + return this.extensionManagementService.install(URI.file(extension)).then(() => { console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", getBaseLabel(extension))); }, error => { if (isPromiseCanceledError(error)) { @@ -232,7 +233,7 @@ export function main(argv: ParsedArgs): TPromise<void> { const stateService = accessor.get(IStateService); return TPromise.join([envService.appSettingsHome, envService.extensionsPath].map(p => mkdirp(p))).then(() => { - const { appRoot, extensionsPath, extensionDevelopmentPath, isBuilt, installSourcePath } = envService; + const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService; const services = new ServiceCollection(); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); @@ -242,7 +243,7 @@ export function main(argv: ParsedArgs): TPromise<void> { services.set(IDialogService, new SyncDescriptor(CommandLineDialogService)); const appenders: AppInsightsAppender[] = []; - if (isBuilt && !extensionDevelopmentPath && !envService.args['disable-telemetry'] && product.enableTelemetry) { + if (isBuilt && !extensionDevelopmentLocationURI && !envService.args['disable-telemetry'] && product.enableTelemetry) { if (product.aiConfig && product.aiConfig.asimovKey) { appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, logService)); diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index cd9bca97297..22488f44ad6 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -8,15 +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, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { Schemas } from 'vs/base/common/network'; +import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import URI from 'vs/base/common/uri'; -import { hasToIgnoreCase, isEqual } from 'vs/base/common/resources'; +import { isEqual, isEqualOrParent } from 'vs/base/common/resources'; export interface ISimpleWindow { openedWorkspace?: IWorkspaceIdentifier; openedFolderUri?: URI; - openedFilePath?: string; + openedFileUri?: URI; extensionDevelopmentPath?: string; lastFocusTime: number; } @@ -26,15 +25,15 @@ export interface IBestWindowOrFolderOptions<W extends ISimpleWindow> { newWindow: boolean; reuseWindow: boolean; context: OpenContext; - filePath?: string; + fileUri?: URI; userHome?: string; codeSettingsFolder?: string; workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace; } -export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, filePath, workspaceResolver }: IBestWindowOrFolderOptions<W>): W { - if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { - const windowOnFilePath = findWindowOnFilePath(windows, filePath, workspaceResolver); +export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions<W>): W { + if (!newWindow && fileUri && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { + const windowOnFilePath = findWindowOnFilePath(windows, fileUri, workspaceResolver); if (windowOnFilePath) { return windowOnFilePath; } @@ -43,20 +42,20 @@ export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows return !newWindow ? getLastActiveWindow(windows) : null; } -function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], filePath: string, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W { +function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W { // First check for windows with workspaces that have a parent folder of the provided path opened const workspaceWindows = windows.filter(window => !!window.openedWorkspace); for (let i = 0; i < workspaceWindows.length; i++) { const window = workspaceWindows[i]; const resolvedWorkspace = workspaceResolver(window.openedWorkspace); - if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => folder.uri.scheme === Schemas.file && paths.isEqualOrParent(filePath, folder.uri.fsPath, !platform.isLinux /* ignorecase */))) { + if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) { return window; } } // Then go with single folder windows that are parent of the provided file path - const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && window.openedFolderUri.scheme === Schemas.file && paths.isEqualOrParent(filePath, window.openedFolderUri.fsPath, !platform.isLinux /* ignorecase */)); + const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && isEqualOrParent(fileUri, window.openedFolderUri)); if (singleFolderWindowsOnFilePath.length) { return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri.path.length - b.openedFolderUri.path.length))[0]; } @@ -71,51 +70,50 @@ export function getLastActiveWindow<W extends ISimpleWindow>(windows: W[]): W { } export function findWindowOnWorkspace<W extends ISimpleWindow>(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W { - return windows.filter(window => { - - // match on folder - if (isSingleFolderWorkspaceIdentifier(workspace)) { - if (window.openedFolderUri && isEqual(window.openedFolderUri, workspace, hasToIgnoreCase(window.openedFolderUri))) { - return true; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + for (const window of windows) { + // match on folder + if (isSingleFolderWorkspaceIdentifier(workspace)) { + if (window.openedFolderUri && isEqual(window.openedFolderUri, workspace)) { + return window; + } } } - - // match on workspace - else { + } else if (isWorkspaceIdentifier(workspace)) { + for (const window of windows) { + // match on workspace if (window.openedWorkspace && window.openedWorkspace.id === workspace.id) { - return true; + return window; } } - - return false; - })[0]; + } + return null; } export function findWindowOnExtensionDevelopmentPath<W extends ISimpleWindow>(windows: W[], extensionDevelopmentPath: string): W { - return windows.filter(window => { - - // match on extension development path + for (const window of windows) { + // match on extension development path. The path can be a path or uri string, using paths.isEqual is not 100% correct but good enough if (paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { - return true; + return window; } - - return false; - })[0]; + } + return null; } export function findWindowOnWorkspaceOrFolderUri<W extends ISimpleWindow>(windows: W[], uri: URI): W { - return windows.filter(window => { - + if (!uri) { + return null; + } + for (const window of windows) { // check for workspace config path if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), uri, !platform.isLinux /* ignorecase */)) { - return true; + return window; } // check for folder path - if (window.openedFolderUri && isEqual(window.openedFolderUri, uri, hasToIgnoreCase(uri))) { - return true; + if (window.openedFolderUri && isEqual(window.openedFolderUri, uri)) { + return window; } - - return false; - })[0]; + } + return null; } diff --git a/src/vs/code/test/node/windowsFinder.test.ts b/src/vs/code/test/node/windowsFinder.test.ts index b9b70a3503d..ed949d9a13a 100644 --- a/src/vs/code/test/node/windowsFinder.test.ts +++ b/src/vs/code/test/node/windowsFinder.test.ts @@ -11,8 +11,9 @@ 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(), @@ -45,32 +46,32 @@ suite('WindowsFinder', () => { test('New window without folder when no windows exist', () => { assert.equal(findBestWindowOrFolderForFile(options()), null); assert.equal(findBestWindowOrFolderForFile(options({ - filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt') + fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')) })), null); assert.equal(findBestWindowOrFolderForFile(options({ - filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')), newWindow: true })), null); assert.equal(findBestWindowOrFolderForFile(options({ - filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')), reuseWindow: true })), null); assert.equal(findBestWindowOrFolderForFile(options({ - filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')), context: OpenContext.API })), null); assert.equal(findBestWindowOrFolderForFile(options({ - filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')) })), null); assert.equal(findBestWindowOrFolderForFile(options({ - filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt') + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt')) })), null); }); test('New window without folder when windows exist', () => { assert.equal(findBestWindowOrFolderForFile(options({ windows, - filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'), + fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')), newWindow: true })), null); }); @@ -81,16 +82,16 @@ suite('WindowsFinder', () => { })), lastActiveWindow); assert.equal(findBestWindowOrFolderForFile(options({ windows, - filePath: path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt') + fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt')) })), lastActiveWindow); assert.equal(findBestWindowOrFolderForFile(options({ windows: [lastActiveWindow, noVscodeFolderWindow], - filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'), + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')), reuseWindow: true })), lastActiveWindow); assert.equal(findBestWindowOrFolderForFile(options({ windows, - filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'), + fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')), context: OpenContext.API })), lastActiveWindow); }); @@ -98,16 +99,16 @@ suite('WindowsFinder', () => { test('Existing window with folder', () => { assert.equal(findBestWindowOrFolderForFile(options({ windows, - filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt') + fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')) })), noVscodeFolderWindow); assert.equal(findBestWindowOrFolderForFile(options({ windows, - filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')) })), vscodeFolderWindow); 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') + fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')) })), window); }); @@ -116,7 +117,7 @@ suite('WindowsFinder', () => { 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') + fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt')) })), nestedFolderWindow); }); @@ -124,7 +125,7 @@ suite('WindowsFinder', () => { 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') + fileUri: URI.file(path.join(fixturesFolder, 'vscode_workspace_2_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')) })), window); }); }); diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index 6e5d782a037..63ba2d59a99 100644 --- a/src/vs/editor/browser/config/charWidthReader.ts +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -29,11 +29,7 @@ export class CharWidthRequest { } } -interface ICharWidthReader { - read(): void; -} - -class DomCharWidthReader implements ICharWidthReader { +class DomCharWidthReader { private readonly _bareFontInfo: BareFontInfo; private readonly _requests: CharWidthRequest[]; diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 74cb598bddf..f83aefc5125 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -90,6 +90,7 @@ export interface ISerializedFontInfo { readonly isMonospace: boolean; readonly typicalHalfwidthCharacterWidth: number; readonly typicalFullwidthCharacterWidth: number; + readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; readonly maxDigitWidth: number; } @@ -176,6 +177,7 @@ class CSSBasedConfiguration extends Disposable { isMonospace: readConfig.isMonospace, typicalHalfwidthCharacterWidth: Math.max(readConfig.typicalHalfwidthCharacterWidth, 5), typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5), + canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow, spaceWidth: Math.max(readConfig.spaceWidth, 5), maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5), }, false); @@ -214,7 +216,9 @@ class CSSBasedConfiguration extends Disposable { const digit9 = this.createRequest('9', CharWidthRequestType.Regular, all, monospace); // monospace test: used for whitespace rendering - this.createRequest('→', CharWidthRequestType.Regular, all, monospace); + const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace); + const halfwidthRightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, null); + this.createRequest('·', CharWidthRequestType.Regular, all, monospace); // monospace test: some characters @@ -256,6 +260,16 @@ class CSSBasedConfiguration extends Disposable { } } + let canUseHalfwidthRightwardsArrow = true; + if (isMonospace && halfwidthRightwardsArrow.width !== referenceWidth) { + // using a halfwidth rightwards arrow would break monospace... + canUseHalfwidthRightwardsArrow = false; + } + if (halfwidthRightwardsArrow.width > rightwardsArrow.width) { + // using a halfwidth rightwards arrow would paint a larger arrow than a regular rightwards arrow + canUseHalfwidthRightwardsArrow = false; + } + // let's trust the zoom level only 2s after it was changed. const canTrustBrowserZoomLevel = (browser.getTimeSinceLastZoomLevelChanged() > 2000); return new FontInfo({ @@ -268,6 +282,7 @@ class CSSBasedConfiguration extends Disposable { isMonospace: isMonospace, typicalHalfwidthCharacterWidth: typicalHalfwidthCharacter.width, typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width, + canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow, spaceWidth: space.width, maxDigitWidth: maxDigitWidth }, canTrustBrowserZoomLevel); diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 083e0fd94a6..46a8671d6f4 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -1706,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 } })); 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/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 28ecaca15e1..d2b256b0ac9 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -396,6 +396,14 @@ export interface ICodeEditor extends editorCommon.IEditor { * @internal */ onDidType(listener: (text: string) => void): IDisposable; + /** + * An event emitted after composition has started. + */ + onCompositionStart(listener: () => void): IDisposable; + /** + * An event emitted after composition has ended. + */ + onCompositionEnd(listener: () => void): IDisposable; /** * An event emitted when editing failed because the editor is read-only. * @event 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/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 6e1338d6adb..01acc025bfe 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -74,6 +74,7 @@ export class ViewLineOptions { public readonly renderControlCharacters: boolean; public readonly spaceWidth: number; public readonly useMonospaceOptimizations: boolean; + public readonly canUseHalfwidthRightwardsArrow: boolean; public readonly lineHeight: number; public readonly stopRenderingLineAfter: number; public readonly fontLigatures: boolean; @@ -87,6 +88,7 @@ export class ViewLineOptions { config.editor.fontInfo.isMonospace && !config.editor.viewInfo.disableMonospaceOptimizations ); + this.canUseHalfwidthRightwardsArrow = config.editor.fontInfo.canUseHalfwidthRightwardsArrow; this.lineHeight = config.editor.lineHeight; this.stopRenderingLineAfter = config.editor.viewInfo.stopRenderingLineAfter; this.fontLigatures = config.editor.viewInfo.fontLigatures; @@ -99,6 +101,7 @@ export class ViewLineOptions { && this.renderControlCharacters === other.renderControlCharacters && this.spaceWidth === other.spaceWidth && this.useMonospaceOptimizations === other.useMonospaceOptimizations + && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.lineHeight === other.lineHeight && this.stopRenderingLineAfter === other.stopRenderingLineAfter && this.fontLigatures === other.fontLigatures @@ -190,6 +193,7 @@ export class ViewLine implements IVisibleLine { let renderLineInput = new RenderLineInput( options.useMonospaceOptimizations, + options.canUseHalfwidthRightwardsArrow, lineData.content, lineData.continuesWithWrappedLine, lineData.isBasicASCII, diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index b6e5d67a9d3..6f588e16445 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -127,6 +127,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onDidType: Emitter<string> = this._register(new Emitter<string>()); public readonly onDidType = this._onDidType.event; + private readonly _onCompositionStart: Emitter<void> = this._register(new Emitter<void>()); + public readonly onCompositionStart = this._onCompositionStart.event; + + private readonly _onCompositionEnd: Emitter<void> = this._register(new Emitter<void>()); + public readonly onCompositionEnd = this._onCompositionEnd.event; + private readonly _onDidPaste: Emitter<Range> = this._register(new Emitter<Range>()); public readonly onDidPaste = this._onDidPaste.event; @@ -894,6 +900,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return; } + if (handlerId === editorCommon.Handler.CompositionStart) { + this._onCompositionStart.fire(); + } + if (handlerId === editorCommon.Handler.CompositionEnd) { + this._onCompositionEnd.fire(); + } + const action = this.getAction(handlerId); if (action) { TPromise.as(action.run()).then(null, onUnexpectedError); @@ -1818,7 +1831,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 dbd07dd8a74..481a3d62bd4 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -944,7 +944,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE clonedOptions.folding = false; clonedOptions.codeLens = false; clonedOptions.fixedOverflowWidgets = true; - clonedOptions.lineDecorationsWidth = '2ch'; + // clonedOptions.lineDecorationsWidth = '2ch'; if (!clonedOptions.minimap) { clonedOptions.minimap = {}; } @@ -1982,6 +1982,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, originalModel.mightContainRTL()); const output = renderViewLine(new RenderLineInput( (config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations), + config.fontInfo.canUseHalfwidthRightwardsArrow, lineContent, false, isBasicASCII, diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index fe42bf6cc0d..58b825408e6 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -769,6 +769,7 @@ export class DiffReview extends Disposable { const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, model.mightContainRTL()); const r = renderViewLine(new RenderLineInput( (config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations), + config.fontInfo.canUseHalfwidthRightwardsArrow, lineContent, false, isBasicASCII, diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/media/diffEditor.css index 08829aa00e7..5e71f38a9bd 100644 --- a/src/vs/editor/browser/widget/media/diffEditor.css +++ b/src/vs/editor/browser/widget/media/diffEditor.css @@ -41,6 +41,8 @@ opacity: 0.7; background-repeat: no-repeat; background-position: 50% 50%; + background-position: center; + background-size: 11px 11px; } .monaco-editor.hc-black .insert-sign, .monaco-diff-editor.hc-black .insert-sign, diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index adaf242cb6f..e8bc9eb8882 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,7 +272,7 @@ 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', @@ -282,44 +283,44 @@ const editorConfiguration: IConfigurationNode = { '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#` 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#` 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#` and `#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,27 +337,27 @@ 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', @@ -366,7 +367,7 @@ const editorConfiguration: IConfigurationNode = { 'editor.find.autoFindInSelection': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.find.autoFindInSelection, - 'description': nls.localize('find.autoFindInSelection', "Controls whether the 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', @@ -377,7 +378,7 @@ const editorConfiguration: IConfigurationNode = { '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({ @@ -407,7 +408,7 @@ const editorConfiguration: IConfigurationNode = { '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.', @@ -430,7 +431,7 @@ const editorConfiguration: IConfigurationNode = { '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', @@ -440,7 +441,7 @@ 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.', @@ -480,54 +481,96 @@ 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', + type: 'string', + enum: ['always', 'languageDefined', 'beforeWhitespace', 'never'], + enumDescriptions: [ + '', + nls.localize('editor.autoClosingBrackets.languageDefined', "Use language configurations to determine when to autoclose brackets."), + nls.localize('editor.autoClosingBrackets.beforeWhitespace', "Autoclose brackets only when the cursor is to the left of whitespace."), + '', + + ], '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.autoClosingQuotes': { + type: 'string', + enum: ['always', 'languageDefined', 'beforeWhitespace', 'never'], + enumDescriptions: [ + '', + nls.localize('editor.autoClosingQuotes.languageDefined', "Use language configurations to determine when to autoclose quotes."), + nls.localize('editor.autoClosingQuotes.beforeWhitespace', "Autoclose quotes only when the cursor is to the left of whitespace."), + '', + ], + 'default': EDITOR_DEFAULTS.autoClosingQuotes, + 'description': nls.localize('autoClosingQuotes', "Controls whether the editor should automatically close quotes after the user adds an opening quote.") + }, + 'editor.autoWrapping': { + type: 'string', + enum: ['always', 'brackets', 'quotes', 'never'], + enumDescriptions: [ + '', + nls.localize('editor.autoWrapping.brackets', "Wrap with brackets but not quotes."), + nls.localize('editor.autoWrapping.quotes', "Wrap with quotes but not brackets."), + '' + ], + 'default': EDITOR_DEFAULTS.autoWrapping, + 'description': nls.localize('autoWrapping', "Controls whether the editor should automatically wrap selections.") }, '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', @@ -587,7 +630,7 @@ 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', @@ -602,7 +645,7 @@ const editorConfiguration: IConfigurationNode = { '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', @@ -613,7 +656,7 @@ 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', @@ -624,17 +667,17 @@ const editorConfiguration: IConfigurationNode = { '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', @@ -660,18 +703,24 @@ const editorConfiguration: IConfigurationNode = { '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', @@ -681,12 +730,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', @@ -717,12 +762,12 @@ const editorConfiguration: IConfigurationNode = { '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', @@ -743,7 +788,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', @@ -753,14 +798,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': { @@ -772,23 +817,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', @@ -798,7 +843,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..b642b3caae4 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -95,6 +95,16 @@ export interface IEditorFindOptions { globalFindClipboard: boolean; } +/** + * Configuration options for auto closing quotes and brackets + */ +export type EditorAutoClosingStrategy = 'always' | 'languageDefined' | 'beforeWhitespace' | 'never'; + +/** + * Configuration options for auto wrapping quotes and brackets + */ +export type EditorAutoWrappingStrategy = 'always' | 'quotes' | 'brackets' | 'never'; + /** * Configuration options for editor minimap */ @@ -158,6 +168,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,19 +477,29 @@ export interface IEditorOptions { */ quickSuggestionsDelay?: number; /** - * Enables parameter hints + * Parameter hint options. */ - parameterHints?: boolean; + parameterHints?: IEditorParameterHintOptions; /** * Render icons in suggestions box. * Defaults to true. */ iconsInSuggestions?: boolean; /** - * Enable auto closing brackets. - * Defaults to true. + * Options for auto closing brackets. + * Defaults to language defined behavior. */ - autoClosingBrackets?: boolean; + autoClosingBrackets?: EditorAutoClosingStrategy; + /** + * Options for auto closing quotes. + * Defaults to language defined behavior. + */ + autoClosingQuotes?: EditorAutoClosingStrategy; + /** + * Options for autowrapping. + * Defaults to always allowing autowrapping. + */ + autoWrapping?: EditorAutoWrappingStrategy; /** * Enable auto indentation adjustment. * Defaults to false. @@ -851,6 +887,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 +952,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; @@ -959,7 +1000,9 @@ export interface IValidatedEditorOptions { readonly wordWrapBreakBeforeCharacters: string; readonly wordWrapBreakAfterCharacters: string; readonly wordWrapBreakObtrusiveCharacters: string; - readonly autoClosingBrackets: boolean; + readonly autoClosingBrackets: EditorAutoClosingStrategy; + readonly autoClosingQuotes: EditorAutoClosingStrategy; + readonly autoWrapping: EditorAutoWrappingStrategy; readonly autoIndent: boolean; readonly dragAndDrop: boolean; readonly emptySelectionClipboard: boolean; @@ -994,7 +1037,9 @@ export class InternalEditorOptions { // ---- cursor options readonly wordSeparators: string; - readonly autoClosingBrackets: boolean; + readonly autoClosingBrackets: EditorAutoClosingStrategy; + readonly autoClosingQuotes: EditorAutoClosingStrategy; + readonly autoWrapping: EditorAutoWrappingStrategy; readonly autoIndent: boolean; readonly useTabStops: boolean; readonly tabFocusMode: boolean; @@ -1021,7 +1066,9 @@ export class InternalEditorOptions { multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey'; multiCursorMergeOverlapping: boolean; wordSeparators: string; - autoClosingBrackets: boolean; + autoClosingBrackets: EditorAutoClosingStrategy; + autoClosingQuotes: EditorAutoClosingStrategy; + autoWrapping: EditorAutoWrappingStrategy; autoIndent: boolean; useTabStops: boolean; tabFocusMode: boolean; @@ -1044,6 +1091,8 @@ export class InternalEditorOptions { this.multiCursorMergeOverlapping = source.multiCursorMergeOverlapping; this.wordSeparators = source.wordSeparators; this.autoClosingBrackets = source.autoClosingBrackets; + this.autoClosingQuotes = source.autoClosingQuotes; + this.autoWrapping = source.autoWrapping; this.autoIndent = source.autoIndent; this.useTabStops = source.useTabStops; this.tabFocusMode = source.tabFocusMode; @@ -1072,6 +1121,8 @@ export class InternalEditorOptions { && this.multiCursorMergeOverlapping === other.multiCursorMergeOverlapping && this.wordSeparators === other.wordSeparators && this.autoClosingBrackets === other.autoClosingBrackets + && this.autoClosingQuotes === other.autoClosingQuotes + && this.autoWrapping === other.autoWrapping && this.autoIndent === other.autoIndent && this.useTabStops === other.useTabStops && this.tabFocusMode === other.tabFocusMode @@ -1101,6 +1152,8 @@ export class InternalEditorOptions { multiCursorMergeOverlapping: (this.multiCursorMergeOverlapping !== newOpts.multiCursorMergeOverlapping), wordSeparators: (this.wordSeparators !== newOpts.wordSeparators), autoClosingBrackets: (this.autoClosingBrackets !== newOpts.autoClosingBrackets), + autoClosingQuotes: (this.autoClosingQuotes !== newOpts.autoClosingQuotes), + autoWrapping: (this.autoWrapping !== newOpts.autoWrapping), autoIndent: (this.autoIndent !== newOpts.autoIndent), useTabStops: (this.useTabStops !== newOpts.useTabStops), tabFocusMode: (this.tabFocusMode !== newOpts.tabFocusMode), @@ -1237,6 +1290,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 +1354,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 @@ -1472,6 +1535,8 @@ export interface IConfigurationChangedEvent { readonly multiCursorMergeOverlapping: boolean; readonly wordSeparators: boolean; readonly autoClosingBrackets: boolean; + readonly autoClosingQuotes: boolean; + readonly autoWrapping: boolean; readonly autoIndent: boolean; readonly useTabStops: boolean; readonly tabFocusMode: boolean; @@ -1650,6 +1715,20 @@ export class EditorOptionsValidator { } const multiCursorModifier = _stringSet<'altKey' | 'metaKey' | 'ctrlKey'>(configuredMulticursorModifier, defaults.multiCursorModifier, ['altKey', 'metaKey', 'ctrlKey']); + let autoClosingBrackets: EditorAutoClosingStrategy; + let autoClosingQuotes: EditorAutoClosingStrategy; + let autoWrapping: EditorAutoWrappingStrategy; + if (typeof opts.autoClosingBrackets === 'boolean' && opts.autoClosingBrackets === false) { + // backwards compatibility: disable all on boolean false + autoClosingBrackets = 'never'; + autoClosingQuotes = 'never'; + autoWrapping = 'never'; + } else { + autoClosingBrackets = _stringSet<EditorAutoClosingStrategy>(opts.autoClosingBrackets, defaults.autoClosingBrackets, ['always', 'languageDefined', 'beforeWhitespace', 'never']); + autoClosingQuotes = _stringSet<EditorAutoClosingStrategy>(opts.autoClosingQuotes, defaults.autoClosingQuotes, ['always', 'languageDefined', 'beforeWhitespace', 'never']); + autoWrapping = _stringSet<EditorAutoWrappingStrategy>(opts.autoWrapping, defaults.autoWrapping, ['always', 'brackets', 'quotes', 'never'], ); + } + return { inDiffEditor: _boolean(opts.inDiffEditor, defaults.inDiffEditor), wordSeparators: _string(opts.wordSeparators, defaults.wordSeparators), @@ -1666,7 +1745,9 @@ export class EditorOptionsValidator { wordWrapBreakBeforeCharacters: _string(opts.wordWrapBreakBeforeCharacters, defaults.wordWrapBreakBeforeCharacters), wordWrapBreakAfterCharacters: _string(opts.wordWrapBreakAfterCharacters, defaults.wordWrapBreakAfterCharacters), wordWrapBreakObtrusiveCharacters: _string(opts.wordWrapBreakObtrusiveCharacters, defaults.wordWrapBreakObtrusiveCharacters), - autoClosingBrackets: _boolean(opts.autoClosingBrackets, defaults.autoClosingBrackets), + autoClosingBrackets, + autoClosingQuotes, + autoWrapping, autoIndent: _boolean(opts.autoIndent, defaults.autoIndent), dragAndDrop: _boolean(opts.dragAndDrop, defaults.dragAndDrop), emptySelectionClipboard: _boolean(opts.emptySelectionClipboard, defaults.emptySelectionClipboard), @@ -1732,6 +1813,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 +1844,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 +1976,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), @@ -1939,6 +2029,8 @@ export class InternalEditorOptionsFactory { wordWrapBreakAfterCharacters: opts.wordWrapBreakAfterCharacters, wordWrapBreakObtrusiveCharacters: opts.wordWrapBreakObtrusiveCharacters, autoClosingBrackets: opts.autoClosingBrackets, + autoClosingQuotes: opts.autoClosingQuotes, + autoWrapping: opts.autoWrapping, autoIndent: opts.autoIndent, dragAndDrop: opts.dragAndDrop, emptySelectionClipboard: opts.emptySelectionClipboard, @@ -2160,6 +2252,8 @@ export class InternalEditorOptionsFactory { multiCursorMergeOverlapping: opts.multiCursorMergeOverlapping, wordSeparators: opts.wordSeparators, autoClosingBrackets: opts.autoClosingBrackets, + autoClosingQuotes: opts.autoClosingQuotes, + autoWrapping: opts.autoWrapping, autoIndent: opts.autoIndent, useTabStops: opts.useTabStops, tabFocusMode: opts.readOnly ? true : env.tabFocusMode, @@ -2392,7 +2486,9 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { wordWrapBreakBeforeCharacters: '([{‘“〈《「『【〔([{「£¥$£¥++', wordWrapBreakAfterCharacters: ' \t})]?|&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', wordWrapBreakObtrusiveCharacters: '.', - autoClosingBrackets: true, + autoClosingBrackets: 'languageDefined', + autoClosingQuotes: 'languageDefined', + autoWrapping: 'always', autoIndent: true, dragAndDrop: true, emptySelectionClipboard: true, @@ -2465,7 +2561,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/config/fontInfo.ts b/src/vs/editor/common/config/fontInfo.ts index 24b105bbd4e..1c5b063979b 100644 --- a/src/vs/editor/common/config/fontInfo.ts +++ b/src/vs/editor/common/config/fontInfo.ts @@ -155,6 +155,7 @@ export class FontInfo extends BareFontInfo { readonly isMonospace: boolean; readonly typicalHalfwidthCharacterWidth: number; readonly typicalFullwidthCharacterWidth: number; + readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; readonly maxDigitWidth: number; @@ -171,6 +172,7 @@ export class FontInfo extends BareFontInfo { isMonospace: boolean; typicalHalfwidthCharacterWidth: number; typicalFullwidthCharacterWidth: number; + canUseHalfwidthRightwardsArrow: boolean; spaceWidth: number; maxDigitWidth: number; }, isTrusted: boolean) { @@ -179,6 +181,7 @@ export class FontInfo extends BareFontInfo { this.isMonospace = opts.isMonospace; this.typicalHalfwidthCharacterWidth = opts.typicalHalfwidthCharacterWidth; this.typicalFullwidthCharacterWidth = opts.typicalFullwidthCharacterWidth; + this.canUseHalfwidthRightwardsArrow = opts.canUseHalfwidthRightwardsArrow; this.spaceWidth = opts.spaceWidth; this.maxDigitWidth = opts.maxDigitWidth; } @@ -195,6 +198,7 @@ export class FontInfo extends BareFontInfo { && this.letterSpacing === other.letterSpacing && this.typicalHalfwidthCharacterWidth === other.typicalHalfwidthCharacterWidth && this.typicalFullwidthCharacterWidth === other.typicalFullwidthCharacterWidth + && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.spaceWidth === other.spaceWidth && this.maxDigitWidth === other.maxDigitWidth ); diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 0563b6a0771..cba876a0772 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -15,7 +15,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { onUnexpectedError } from 'vs/base/common/errors'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; -import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; +import { IConfigurationChangedEvent, EditorAutoClosingStrategy, EditorAutoWrappingStrategy } from 'vs/editor/common/config/editorOptions'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; @@ -66,6 +66,10 @@ export interface CharacterMap { [char: string]: string; } +const autoCloseAlways = _ => true; +const autoCloseNever = _ => false; +const autoCloseBeforeWhitespace = (chr: string) => (chr === ' ' || chr === '\t'); + export class CursorConfiguration { _cursorMoveConfigurationBrand: void; @@ -79,11 +83,14 @@ export class CursorConfiguration { public readonly wordSeparators: string; public readonly emptySelectionClipboard: boolean; public readonly multiCursorMergeOverlapping: boolean; - public readonly autoClosingBrackets: boolean; + public readonly autoClosingBrackets: EditorAutoClosingStrategy; + public readonly autoClosingQuotes: EditorAutoClosingStrategy; + public readonly autoWrapping: EditorAutoWrappingStrategy; public readonly autoIndent: boolean; public readonly autoClosingPairsOpen: CharacterMap; public readonly autoClosingPairsClose: CharacterMap; public readonly surroundingPairs: CharacterMap; + public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean, bracket: (ch: string) => boolean }; private readonly _languageIdentifier: LanguageIdentifier; private _electricChars: { [key: string]: boolean; }; @@ -95,6 +102,8 @@ export class CursorConfiguration { || e.emptySelectionClipboard || e.multiCursorMergeOverlapping || e.autoClosingBrackets + || e.autoClosingQuotes + || e.autoWrapping || e.useTabStops || e.lineHeight || e.readOnly @@ -122,6 +131,8 @@ export class CursorConfiguration { this.emptySelectionClipboard = c.emptySelectionClipboard; this.multiCursorMergeOverlapping = c.multiCursorMergeOverlapping; this.autoClosingBrackets = c.autoClosingBrackets; + this.autoClosingQuotes = c.autoClosingQuotes; + this.autoWrapping = c.autoWrapping; this.autoIndent = c.autoIndent; this.autoClosingPairsOpen = {}; @@ -129,6 +140,11 @@ export class CursorConfiguration { this.surroundingPairs = {}; this._electricChars = null; + this.shouldAutoCloseBefore = { + quote: CursorConfiguration._getShouldAutoClose(languageIdentifier, this.autoClosingQuotes), + bracket: CursorConfiguration._getShouldAutoClose(languageIdentifier, this.autoClosingBrackets) + }; + let autoClosingPairs = CursorConfiguration._getAutoClosingPairs(languageIdentifier); if (autoClosingPairs) { for (let i = 0; i < autoClosingPairs.length; i++) { @@ -180,6 +196,29 @@ export class CursorConfiguration { } } + private static _getShouldAutoClose(languageIdentifier: LanguageIdentifier, autoCloseConfig: EditorAutoClosingStrategy): (ch: string) => boolean { + switch (autoCloseConfig) { + case 'beforeWhitespace': + return autoCloseBeforeWhitespace; + case 'languageDefined': + return CursorConfiguration._getLanguageDefinedShouldAutoClose(languageIdentifier); + case 'always': + return autoCloseAlways; + case 'never': + return autoCloseNever; + } + } + + private static _getLanguageDefinedShouldAutoClose(languageIdentifier: LanguageIdentifier): (ch: string) => boolean { + try { + const autoCloseBeforeSet = LanguageConfigurationRegistry.getAutoCloseBeforeSet(languageIdentifier.id); + return c => autoCloseBeforeSet.indexOf(c) !== -1; + } catch (e) { + onUnexpectedError(e); + return autoCloseNever; + } + } + private static _getSurroundingPairs(languageIdentifier: LanguageIdentifier): IAutoClosingPair[] { try { return LanguageConfigurationRegistry.getSurroundingPairs(languageIdentifier.id); @@ -537,3 +576,7 @@ export class CursorColumns { return column - 1 - (column - 1) % tabSize; } } + +export function isQuote(ch: string): boolean { + return (ch === '\'' || ch === '"' || ch === '`'); +} diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index e5b498b40e9..e16c0944cb9 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -5,7 +5,7 @@ 'use strict'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType, isQuote } from 'vs/editor/common/controller/cursorCommon'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; @@ -49,7 +49,7 @@ export class DeleteOperations { } private static _isAutoClosingPairDelete(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): boolean { - if (!config.autoClosingBrackets) { + if (config.autoClosingBrackets === 'never' && config.autoClosingQuotes === 'never') { return false; } @@ -68,6 +68,16 @@ export class DeleteOperations { return false; } + if (isQuote(character)) { + if (config.autoClosingQuotes === 'never') { + return false; + } + } else { + if (config.autoClosingBrackets === 'never') { + return false; + } + } + const afterCharacter = lineText[position.column - 1]; const closeCharacter = config.autoClosingPairsOpen[character]; diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 7085aa16c93..ac7158f32df 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -6,7 +6,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ReplaceCommand, ReplaceCommandWithoutChangingPosition, ReplaceCommandWithOffsetCursorState } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns, CursorConfiguration, ICursorSimpleModel, EditOperationResult, EditOperationType, isQuote } from 'vs/editor/common/controller/cursorCommon'; import { Range } from 'vs/editor/common/core/range'; import { ICommand } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; @@ -437,7 +437,9 @@ export class TypeOperations { } private static _isAutoClosingCloseCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { - if (!config.autoClosingBrackets || !config.autoClosingPairsClose.hasOwnProperty(ch)) { + const autoCloseConfig = isQuote(ch) ? config.autoClosingQuotes : config.autoClosingBrackets; + + if (autoCloseConfig === 'never' || !config.autoClosingPairsClose.hasOwnProperty(ch)) { return false; } @@ -511,10 +513,15 @@ export class TypeOperations { } private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { - if (!config.autoClosingBrackets || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { + const chIsQuote = isQuote(ch); + const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; + + if (autoCloseConfig === 'never' || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { return false; } + let shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; + for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; if (!selection.isEmpty()) { @@ -525,7 +532,7 @@ export class TypeOperations { const lineText = model.getLineContent(position.lineNumber); // Do not auto-close ' or " after a word character - if ((ch === '\'' || ch === '"') && position.column > 1) { + if (chIsQuote && position.column > 1) { const wordSeparators = getMapForWordSeparators(config.wordSeparators); const characterBeforeCode = lineText.charCodeAt(position.column - 2); const characterBeforeType = wordSeparators.get(characterBeforeCode); @@ -538,7 +545,8 @@ export class TypeOperations { const characterAfter = lineText.charAt(position.column - 1); if (characterAfter) { let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter); - if (!isBeforeCloseBrace && !/\s/.test(characterAfter)) { + + if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) { return false; } } @@ -579,12 +587,21 @@ export class TypeOperations { }); } + private static _shouldSurroundChar(config: CursorConfiguration, ch: string): boolean { + if (isQuote(ch)) { + return (config.autoWrapping === 'quotes' || config.autoWrapping === 'always'); + } else { + // Character is a bracket + return (config.autoWrapping === 'brackets' || config.autoWrapping === 'always'); + } + } + private static _isSurroundSelectionType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { - if (!config.autoClosingBrackets || !config.surroundingPairs.hasOwnProperty(ch)) { + if (!TypeOperations._shouldSurroundChar(config, ch) || !config.surroundingPairs.hasOwnProperty(ch)) { return false; } - const isTypingAQuoteCharacter = (ch === '\'' || ch === '"'); + const isTypingAQuoteCharacter = isQuote(ch); for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; @@ -613,7 +630,7 @@ export class TypeOperations { if (isTypingAQuoteCharacter && selection.startLineNumber === selection.endLineNumber && selection.startColumn + 1 === selection.endColumn) { const selectionText = model.getValueInRange(selection); - if ((selectionText === '\'' || selectionText === '"')) { + if (isQuote(selectionText)) { // Typing a quote character on top of another quote character // => disable surround selection type return false; @@ -708,7 +725,7 @@ export class TypeOperations { } public static compositionEndWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[]): EditOperationResult { - if (!config.autoClosingBrackets) { + if (config.autoClosingQuotes === 'never') { return null; } @@ -736,7 +753,7 @@ export class TypeOperations { // As we are not typing in a new character, so we don't need to run `_runAutoClosingCloseCharType` // Next step, let's try to check if it's an open char. if (config.autoClosingPairsOpen.hasOwnProperty(ch)) { - if ((ch === '\'' || ch === '"') && position.column > 2) { + if (isQuote(ch) && position.column > 2) { const wordSeparators = getMapForWordSeparators(config.wordSeparators); const characterBeforeCode = lineText.charCodeAt(position.column - 3); const characterBeforeType = wordSeparators.get(characterBeforeCode); @@ -749,7 +766,8 @@ export class TypeOperations { if (characterAfter) { let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter); - if (!isBeforeCloseBrace && !/\s/.test(characterAfter)) { + let shouldAutoCloseBefore = isQuote(ch) ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; + if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) { continue; } } 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/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 714b67fa3aa..8ab17ce6977 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -439,7 +439,7 @@ export interface IEditor { /** * Gets the current model attached to this editor. */ - getModel(): IEditorModel; + getModel(): IEditorModel | null; /** * Sets the current model attached to this editor. @@ -449,7 +449,7 @@ export interface IEditor { * will not be destroyed. * It is safe to call setModel(null) to simply detach the current model from the editor. */ - setModel(model: IEditorModel): void; + setModel(model: IEditorModel | null): void; /** * Change the decorations. All decorations added through this changeAccessor diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index d37319c25b5..66a35fabb48 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -747,7 +747,6 @@ export interface ITextModel { /** * Get the word under or besides `position`. * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Might be null. */ getWordAtPosition(position: IPosition): IWordAtPosition; @@ -755,7 +754,6 @@ export interface ITextModel { /** * Get the word under or besides `position` trimmed to `position`.column * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Will never be null. */ getWordUntilPosition(position: IPosition): IWordAtPosition; @@ -814,7 +812,6 @@ export interface ITextModel { /** * Get the word under or besides `position`. * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Might be null. */ getWordAtPosition(position: IPosition): IWordAtPosition; @@ -822,7 +819,6 @@ export interface ITextModel { /** * Get the word under or besides `position` trimmed to `position`.column * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Will never be null. */ getWordUntilPosition(position: IPosition): IWordAtPosition; 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/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 491f5a38134..4d4b03519d1 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -1100,7 +1100,7 @@ export class PieceTreeBase { let endColumn = endOffset - this._buffers[0].lineStarts[endIndex]; let endPos = { line: endIndex, column: endColumn }; let newPiece = new Piece( - 0, /** todo */ + 0, /** todo@peng */ start, endPos, this.getLineFeedCnt(0, start, endPos), diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 02c236a53c2..e5078f5be8b 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -4,21 +4,21 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Color } from 'vs/base/common/color'; +import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; -import URI from 'vs/base/common/uri'; -import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; -import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { Position } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { Event } from 'vs/base/common/event'; -import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry'; -import { Color } from 'vs/base/common/color'; -import { IMarkerData } from 'vs/platform/markers/common/markers'; -import * as model from 'vs/editor/common/model'; import { isObject } from 'vs/base/common/types'; +import URI from 'vs/base/common/uri'; +import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; +import * as model from 'vs/editor/common/model'; +import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; +import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry'; +import { IMarkerData } from 'vs/platform/markers/common/markers'; /** * Open ended enum at runtime @@ -301,6 +301,7 @@ export interface ISuggestion { additionalTextEdits?: model.ISingleEditOperation[]; command?: Command; snippetType?: SnippetType; + noWhitespaceAdjust?: boolean; } /** @@ -848,12 +849,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; @@ -938,6 +939,9 @@ export interface Command { arguments?: any[]; } +/** + * @internal + */ export interface CommentInfo { owner: number; threads: CommentThread[]; @@ -945,6 +949,9 @@ export interface CommentInfo { reply?: Command; } +/** + * @internal + */ export enum CommentThreadCollapsibleState { /** * Determines an item is collapsed @@ -956,6 +963,9 @@ export enum CommentThreadCollapsibleState { Expanded = 1 } +/** + * @internal + */ export interface CommentThread { threadId: string; resource: string; @@ -965,11 +975,17 @@ export interface CommentThread { reply?: Command; } +/** + * @internal + */ export interface NewCommentAction { ranges: IRange[]; actions: Command[]; } +/** + * @internal + */ export interface Comment { readonly commentId: string; readonly body: IMarkdownString; @@ -978,6 +994,9 @@ export interface Comment { readonly command?: Command; } +/** + * @internal + */ export interface CommentThreadChangedEvent { readonly owner: number; /** @@ -996,7 +1015,9 @@ export interface CommentThreadChangedEvent { readonly changed: CommentThread[]; } - +/** + * @internal + */ export interface DocumentCommentProvider { provideDocumentComments(resource: URI, token: CancellationToken): Promise<CommentInfo>; createNewCommentThread(resource: URI, range: Range, text: string, token: CancellationToken): Promise<CommentThread>; @@ -1004,7 +1025,9 @@ export interface DocumentCommentProvider { onDidChangeCommentThreads(): Event<CommentThreadChangedEvent>; } - +/** + * @internal + */ export interface WorkspaceCommentProvider { provideWorkspaceComments(token: CancellationToken): Promise<CommentThread[]>; createNewCommentThread(resource: URI, range: Range, text: string, token: CancellationToken): Promise<CommentThread>; diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index aaf139ee7ba..1b5a057a8f6 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -62,6 +62,13 @@ export interface LanguageConfiguration { */ surroundingPairs?: IAutoClosingPair[]; + /** + * Defines what characters must be after the cursor for bracket or quote autoclosing to occur when using the \'languageDefined\' autoclosing setting. + * + * This is typically the set of characters which can not start an expression, such as whitespace, closing brackets, non-unary operators, etc. + */ + autoCloseBefore?: string; + /** * The language's folding rules. */ diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 842df862701..6dccea67963 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -119,6 +119,7 @@ export class RichEditSupport { onEnterRules: (prev ? current.onEnterRules || prev.onEnterRules : current.onEnterRules), autoClosingPairs: (prev ? current.autoClosingPairs || prev.autoClosingPairs : current.autoClosingPairs), surroundingPairs: (prev ? current.surroundingPairs || prev.surroundingPairs : current.surroundingPairs), + autoCloseBefore: (prev ? current.autoCloseBefore || prev.autoCloseBefore : current.autoCloseBefore), folding: (prev ? current.folding || prev.folding : current.folding), __electricCharacterSupport: (prev ? current.__electricCharacterSupport || prev.__electricCharacterSupport : current.__electricCharacterSupport), }; @@ -269,6 +270,14 @@ export class LanguageConfigurationRegistryImpl { return characterPairSupport.getAutoClosingPairs(); } + public getAutoCloseBeforeSet(languageId: LanguageId): string { + let characterPairSupport = this._getCharacterPairSupport(languageId); + if (!characterPairSupport) { + return CharacterPairSupport.DEFAULT_AUTOCLOSE_BEFORE_LANGUAGE_DEFINED; + } + return characterPairSupport.getAutoCloseBeforeSet(); + } + public getSurroundingPairs(languageId: LanguageId): IAutoClosingPair[] { let characterPairSupport = this._getCharacterPairSupport(languageId); if (!characterPairSupport) { diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts index b6ce3dc3f35..70eb9ed7b65 100644 --- a/src/vs/editor/common/modes/supports/characterPair.ts +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -9,10 +9,14 @@ import { CharacterPair, IAutoClosingPair, IAutoClosingPairConditional, StandardA export class CharacterPairSupport { + static readonly DEFAULT_AUTOCLOSE_BEFORE_LANGUAGE_DEFINED = ';:.,=}])> \n\t'; + static readonly DEFAULT_AUTOCLOSE_BEFORE_WHITESPACE = ' \n\t'; + private readonly _autoClosingPairs: StandardAutoClosingPairConditional[]; private readonly _surroundingPairs: IAutoClosingPair[]; + private readonly _autoCloseBefore: string; - constructor(config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[] }) { + constructor(config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[], autoCloseBefore?: string }) { if (config.autoClosingPairs) { this._autoClosingPairs = config.autoClosingPairs.map(el => new StandardAutoClosingPairConditional(el)); } else if (config.brackets) { @@ -21,6 +25,8 @@ export class CharacterPairSupport { this._autoClosingPairs = []; } + this._autoCloseBefore = typeof config.autoCloseBefore === 'string' ? config.autoCloseBefore : CharacterPairSupport.DEFAULT_AUTOCLOSE_BEFORE_LANGUAGE_DEFINED; + this._surroundingPairs = config.surroundingPairs || this._autoClosingPairs; } @@ -28,6 +34,10 @@ export class CharacterPairSupport { return this._autoClosingPairs; } + public getAutoCloseBeforeSet(): string { + return this._autoCloseBefore; + } + public shouldAutoClosePair(character: string, context: ScopedLineTokens, column: number): boolean { // Always complete on empty line if (context.getTokenCount() === 0) { diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index d478fef73c6..884fba7fa02 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -529,6 +529,7 @@ export abstract class BaseEditorSimpleWorker { } return TPromise.as(methods); } + // ESM-comment-begin return new TPromise<any>((c, e) => { require([moduleId], (foreignModule: { create: IForeignModuleFactory }) => { this._foreignModule = foreignModule.create(ctx, createData); @@ -544,6 +545,11 @@ export abstract class BaseEditorSimpleWorker { }, e); }); + // ESM-comment-end + + // ESM-uncomment-begin + // return TPromise.wrapError(new Error(`Unexpected usage`)); + // ESM-uncomment-end } // foreign method request diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 56a282da585..1a1202dde3c 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -28,6 +28,7 @@ import { overviewRulerWarning, overviewRulerError, overviewRulerInfo } from 'vs/ import { ITextModel, IModelDeltaDecoration, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, DefaultEndOfLine, ITextModelCreationOptions, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBufferFactory, ITextBuffer, EndOfLinePreference } from 'vs/editor/common/model'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { basename } from 'vs/base/common/paths'; +import { isThenable } from 'vs/base/common/async'; function MODEL_ID(resource: URI): string { return resource.toString(); @@ -85,9 +86,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); @@ -497,7 +501,7 @@ export class ModelServiceImpl implements IModelService { public createModel(value: string | ITextBufferFactory, modeOrPromise: TPromise<IMode> | IMode, resource: URI, isForSimpleWidget: boolean = false): ITextModel { let modelData: ModelData; - if (!modeOrPromise || TPromise.is(modeOrPromise)) { + if (!modeOrPromise || isThenable(modeOrPromise)) { modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget); this.setMode(modelData.model, modeOrPromise); } else { @@ -518,7 +522,7 @@ export class ModelServiceImpl implements IModelService { if (!modeOrPromise) { return; } - if (TPromise.is(modeOrPromise)) { + if (isThenable(modeOrPromise)) { modeOrPromise.then((mode) => { if (!model.isDisposed()) { model.setMode(mode.getLanguageIdentifier()); diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/services/resolverService.ts index 173713e7018..56dca5511ff 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/services/resolverService.ts @@ -42,4 +42,6 @@ export interface ITextEditorModel extends IEditorModel { * Provides access to the underlying `ITextModel`. */ textEditorModel: ITextModel; + + isReadonly(): boolean; } diff --git a/src/vs/editor/common/standalone/standaloneBase.ts b/src/vs/editor/common/standalone/standaloneBase.ts index c4683e6d6ed..6c44f5f1fa5 100644 --- a/src/vs/editor/common/standalone/standaloneBase.ts +++ b/src/vs/editor/common/standalone/standaloneBase.ts @@ -18,12 +18,6 @@ import URI from 'vs/base/common/uri'; // This is repeated here so it can be exported // because TS inlines const enums // -------------------------------------------- -export enum Severity { - Ignore = 0, - Info = 1, - Warning = 2, - Error = 3, -} export enum MarkerTag { Unnecessary = 1, @@ -248,7 +242,6 @@ export function createMonacoBaseAPI(): typeof monaco { Range: Range, Selection: Selection, SelectionDirection: SelectionDirection, - Severity: Severity, MarkerSeverity: MarkerSeverity, MarkerTag: MarkerTag, Promise: TPromise, 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/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 977f9a5235b..8b183bda4d1 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -35,6 +35,7 @@ class LinePart { export class RenderLineInput { public readonly useMonospaceOptimizations: boolean; + public readonly canUseHalfwidthRightwardsArrow: boolean; public readonly lineContent: string; public readonly continuesWithWrappedLine: boolean; public readonly isBasicASCII: boolean; @@ -51,6 +52,7 @@ export class RenderLineInput { constructor( useMonospaceOptimizations: boolean, + canUseHalfwidthRightwardsArrow: boolean, lineContent: string, continuesWithWrappedLine: boolean, isBasicASCII: boolean, @@ -66,6 +68,7 @@ export class RenderLineInput { fontLigatures: boolean ) { this.useMonospaceOptimizations = useMonospaceOptimizations; + this.canUseHalfwidthRightwardsArrow = canUseHalfwidthRightwardsArrow; this.lineContent = lineContent; this.continuesWithWrappedLine = continuesWithWrappedLine; this.isBasicASCII = isBasicASCII; @@ -90,6 +93,7 @@ export class RenderLineInput { public equals(other: RenderLineInput): boolean { return ( this.useMonospaceOptimizations === other.useMonospaceOptimizations + && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.lineContent === other.lineContent && this.continuesWithWrappedLine === other.continuesWithWrappedLine && this.isBasicASCII === other.isBasicASCII @@ -303,6 +307,7 @@ export function renderViewLine2(input: RenderLineInput): RenderLineOutput2 { class ResolvedRenderLineInput { constructor( public readonly fontIsMonospace: boolean, + public readonly canUseHalfwidthRightwardsArrow: boolean, public readonly lineContent: string, public readonly len: number, public readonly isOverflowing: boolean, @@ -358,6 +363,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput return new ResolvedRenderLineInput( useMonospaceOptimizations, + input.canUseHalfwidthRightwardsArrow, lineContent, len, isOverflowing, @@ -613,6 +619,7 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: LineP */ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): RenderLineOutput { const fontIsMonospace = input.fontIsMonospace; + const canUseHalfwidthRightwardsArrow = input.canUseHalfwidthRightwardsArrow; const containsForeignElements = input.containsForeignElements; const lineContent = input.lineContent; const len = input.len; @@ -688,7 +695,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render tabsCharDelta += insertSpacesCount - 1; charOffsetInPart += insertSpacesCount - 1; if (insertSpacesCount > 0) { - if (insertSpacesCount > 1) { + if (!canUseHalfwidthRightwardsArrow || insertSpacesCount > 1) { sb.write1(0x2192); // RIGHTWARDS ARROW } else { sb.write1(0xffeb); // HALFWIDTH RIGHTWARDS ARROW diff --git a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index 5ed915fe01a..ed17ffb44dc 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -118,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/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 1f229bfbfbc..d8d998a1fb8 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -27,6 +27,7 @@ import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeAct import { CodeActionContextMenu } from './codeActionWidget'; import { LightBulbWidget } from './lightBulbWidget'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { onUnexpectedError } from 'vs/base/common/errors'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -98,7 +99,7 @@ export class QuickFixController implements IEditorContribution { } else { this._codeActionContextMenu.show(e.actions, e.position); } - }); + }).catch(onUnexpectedError); return; } @@ -123,7 +124,7 @@ export class QuickFixController implements IEditorContribution { } private _handleLightBulbSelect(coords: { x: number, y: number }): void { - if (this._lightBulbWidget.model.actions) { + if (this._lightBulbWidget.model && this._lightBulbWidget.model.actions) { this._codeActionContextMenu.show(this._lightBulbWidget.model.actions, coords); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index b680ed6008b..ebf9f659b0f 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -30,7 +30,7 @@ export class CodeActionContextMenu { show(fixes: Thenable<CodeAction[]>, at: { x: number; y: number } | Position) { - const actions = fixes.then(value => { + const actions = fixes ? fixes.then(value => { return value.map(action => { return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => { return always( @@ -41,10 +41,10 @@ export class CodeActionContextMenu { }).then(actions => { if (!this._editor.getDomNode()) { // cancel when editor went off-dom - return TPromise.wrapError<any>(canceled()); + return TPromise.wrapError<Action[]>(canceled()); } return actions; - }); + }) : TPromise.as([] as Action[]); this._contextMenuService.showContextMenu({ getAnchor: () => { diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index b3836d6aa68..a676d0e8c36 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -17,6 +17,7 @@ import { getColors, IColorData } from 'vs/editor/contrib/colorPicker/color'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { TimeoutTimer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; const MAX_DECORATORS = 500; @@ -28,7 +29,7 @@ export class ColorDetector implements IEditorContribution { private _globalToDispose: IDisposable[] = []; private _localToDispose: IDisposable[] = []; - private _computePromise: CancelablePromise<void>; + private _computePromise: CancelablePromise<IColorData[]>; private _timeoutTimer: TimeoutTimer; private _decorationsIds: string[] = []; @@ -127,13 +128,12 @@ export class ColorDetector implements IEditorContribution { } private beginCompute(): void { - this._computePromise = createCancelablePromise(token => { - return getColors(this._editor.getModel(), token).then(colorInfos => { - this.updateDecorations(colorInfos); - this.updateColorDecorators(colorInfos); - this._computePromise = null; - }); - }); + this._computePromise = createCancelablePromise(token => getColors(this._editor.getModel(), token)); + this._computePromise.then((colorInfos) => { + this.updateDecorations(colorInfos); + this.updateColorDecorators(colorInfos); + this._computePromise = null; + }, onUnexpectedError); } private stop(): void { 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 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-action-orange{fill:#f1cb95}</style><g id="canvas"><path id="XMLID_1_" class="icon-canvas-transparent" d="M16 16H0V0h16v16z"/></g><path class="icon-vs-out" d="M16 6.586l-3-3L11.586 5H9.414l1-1-4-4h-.828L0 5.586v.828l4 4L6.414 8H7v5h1.586l3 3h.828L16 12.414v-.828L13.914 9.5 16 7.414v-.828z" id="outline"/><g id="iconBg"><path class="icon-vs-action-orange" d="M13 10l2 2-3 3-2-2 1-1H8V7H6L4 9 1 6l5-5 3 3-2 2h5l1-1 2 2-3 3-2-2 1-1H9v4l2.999.002L13 10z"/></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-action-orange{fill:#e8ab53}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 6.586l-3-3L11.586 5H9.414l1-1-4-4h-.828L0 5.586v.828l4 4L6.414 8H7v5h1.586l3 3h.828L16 12.414v-.828L13.914 9.5 16 7.414v-.828z" id="outline"/><path class="icon-vs-action-orange" d="M13 10l2 2-3 3-2-2 1-1H8V7H6L4 9 1 6l5-5 3 3-2 2h5l1-1 2 2-3 3-2-2 1-1H9v4l2.999.002L13 10z" id="iconBg"/></svg> \ 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 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 10.736v-6.236l9-4.5 7 3.5v6.236l-9 4.5-7-3.5z" id="outline"/><path class="icon-vs-action-blue" d="M9 1l-8 4v5l6 3 8-4v-5l-6-3zm-2 5.882l-3.764-1.882 5.764-2.882 3.764 1.882-5.764 2.882z" id="iconBg"/><path class="icon-vs-fg" d="M9 2.118l3.764 1.882-5.764 2.882-3.764-1.882 5.764-2.882z" id="iconFg"/></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 10.736V4.5L9 0l7 3.5v6.236l-9 4.5-7-3.5z" id="outline"/><path class="icon-vs-action-blue" d="M9 1L1 5v5l6 3 8-4V4L9 1zM7 6.882L3.236 5 9 2.118 12.764 4 7 6.882z" id="iconBg"/><path class="icon-vs-fg" d="M9 2.118L12.764 4 7 6.882 3.236 5 9 2.118z" id="iconFg"/></svg> \ 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 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M11.5 12c-1.915 0-3.602-1.241-4.228-3h-1.41c-.536.985-1.572 1.625-2.737 1.625-1.723 0-3.125-1.402-3.125-3.125s1.402-3.125 3.125-3.125c1.165 0 2.201.639 2.737 1.625h1.41c.626-1.759 2.313-3 4.228-3 2.481 0 4.5 2.019 4.5 4.5s-2.019 4.5-4.5 4.5z" id="outline"/><path class="icon-vs-fg" d="M11.5 9c-.827 0-1.5-.674-1.5-1.5 0-.828.673-1.5 1.5-1.5s1.5.672 1.5 1.5c0 .826-.673 1.5-1.5 1.5z" id="iconFg"/><path class="icon-vs-action-blue" d="M11.5 4c-1.762 0-3.205 1.306-3.45 3h-2.865c-.226-.931-1.059-1.625-2.06-1.625-1.174 0-2.125.951-2.125 2.125s.951 2.125 2.125 2.125c1 0 1.834-.694 2.06-1.625h2.865c.245 1.694 1.688 3 3.45 3 1.933 0 3.5-1.567 3.5-3.5s-1.567-3.5-3.5-3.5zm0 5c-.827 0-1.5-.673-1.5-1.5s.673-1.5 1.5-1.5 1.5.673 1.5 1.5-.673 1.5-1.5 1.5z" id="iconBg"/></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M11.5 12c-1.915 0-3.602-1.241-4.228-3h-1.41a3.11 3.11 0 0 1-2.737 1.625C1.402 10.625 0 9.223 0 7.5s1.402-3.125 3.125-3.125c1.165 0 2.201.639 2.737 1.625h1.41c.626-1.759 2.313-3 4.228-3C13.981 3 16 5.019 16 7.5S13.981 12 11.5 12z" id="outline"/><path class="icon-vs-fg" d="M11.5 9A1.501 1.501 0 1 1 13 7.5c0 .826-.673 1.5-1.5 1.5z" id="iconFg"/><path class="icon-vs-action-blue" d="M11.5 4a3.49 3.49 0 0 0-3.45 3H5.185A2.122 2.122 0 0 0 1 7.5a2.123 2.123 0 1 0 4.185.5H8.05a3.49 3.49 0 0 0 3.45 3 3.5 3.5 0 1 0 0-7zm0 5c-.827 0-1.5-.673-1.5-1.5S10.673 6 11.5 6s1.5.673 1.5 1.5S12.327 9 11.5 9z" id="iconBg"/></svg> \ 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 3232bc55f4f..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'); diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 38157732bfa..c980bd1f32c 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -4,18 +4,18 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { DocumentSymbolProviderRegistry, DocumentSymbolProvider, DocumentSymbol } from 'vs/editor/common/modes'; -import { ITextModel } from 'vs/editor/common/model'; -import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; -import { IPosition } from 'vs/editor/common/core/position'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { first, size, forEach } from 'vs/base/common/collections'; -import { isFalsyOrEmpty, binarySearch, coalesce } from 'vs/base/common/arrays'; -import { commonPrefixLength } from 'vs/base/common/strings'; -import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { LRUCache } from 'vs/base/common/map'; +import { binarySearch, coalesce, isFalsyOrEmpty } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { first, forEach, size } from 'vs/base/common/collections'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; +import { LRUCache } from 'vs/base/common/map'; +import { commonPrefixLength } from 'vs/base/common/strings'; +import { IPosition } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; +import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; export abstract class TreeElement { @@ -393,11 +393,17 @@ export class OutlineModel extends TreeElement { return true; } + private _matches: [string, OutlineElement]; + updateMatches(pattern: string): OutlineElement { + if (this._matches && this._matches[0] === pattern) { + return this._matches[1]; + } let topMatch: OutlineElement; for (const key in this._groups) { topMatch = this._groups[key].updateMatches(pattern, topMatch); } + this._matches = [pattern, topMatch]; return topMatch; } diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 72f66dd91e2..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 diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index a2a57574264..ab87bfe286b 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -33,6 +33,7 @@ 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'; +import { onUnexpectedError } from 'vs/base/common/errors'; export const ID = 'editor.contrib.folding'; @@ -166,7 +167,7 @@ export class FoldingController implements IEditorContribution { if (foldingModel) { foldingModel.applyMemento(state.collapsedRegions); } - }); + }).done(undefined, onUnexpectedError); } } @@ -312,7 +313,7 @@ export class FoldingController implements IEditorContribution { } } } - }); + }).done(undefined, onUnexpectedError); } @@ -332,10 +333,13 @@ export class FoldingController implements IEditorContribution { switch (e.target.type) { case MouseTargetType.GUTTER_LINE_DECORATIONS: const data = e.target.detail as IMarginData; - const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft; + const offsetLeftInGutter = (e.target.element as HTMLElement).offsetLeft; + const gutterOffsetX = data.offsetX - offsetLeftInGutter; + + // const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft; // TODO@joao TODO@alex TODO@martin this is such that we don't collide with dirty diff - if (gutterOffsetX <= 10) { + if (gutterOffsetX < 5) { // the whitespace between the border and the real folding icon border is 5px return; } @@ -404,7 +408,7 @@ export class FoldingController implements IEditorContribution { } } } - }); + }).done(undefined, onUnexpectedError); } public reveal(position: IPosition): void { 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<TextEdit[], NoProviderError> { +export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[]> { 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<TextEdit[]> { +export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions, token: CancellationToken): Promise<TextEdit[]> { 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 25dee8a028a..02a454eab11 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -27,6 +27,7 @@ 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 { @@ -239,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)) { @@ -267,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); } @@ -276,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; } @@ -293,7 +294,8 @@ export abstract class AbstractFormatAction extends EditorAction { }); } - protected abstract _getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]>; + protected abstract _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]>; + protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { notificationService.info(nls.localize('no.provider', "There is no formatter for '{0}'-files installed.", language)); } @@ -322,10 +324,10 @@ export class FormatDocumentAction extends AbstractFormatAction { }); } - protected _getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]> { + protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]> { 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 { @@ -354,10 +356,10 @@ export class FormatSelectionAction extends AbstractFormatAction { }); } - protected _getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]> { + protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]> { 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 { @@ -379,14 +381,14 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => { constructor() { super({} as IActionOptions); } - _getFormattingEdits(editor: ICodeEditor): TPromise<ISingleEditOperation[]> { + _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise<ISingleEditOperation[]> { 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/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 667e77dbf2d..6ca73451d74 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -26,6 +26,7 @@ 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 { @@ -375,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/hover/hoverOperation.ts b/src/vs/editor/contrib/hover/hoverOperation.ts index 16699608cd6..2b55f24057d 100644 --- a/src/vs/editor/contrib/hover/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/hoverOperation.ts @@ -57,7 +57,7 @@ export class HoverOperation<Result> { private _firstWaitScheduler: RunOnceScheduler; private _secondWaitScheduler: RunOnceScheduler; private _loadingMessageScheduler: RunOnceScheduler; - private _asyncComputationPromise: CancelablePromise<void>; + private _asyncComputationPromise: CancelablePromise<Result>; private _asyncComputationPromiseDone: boolean; private _completeCallback: (r: Result) => void; @@ -103,12 +103,11 @@ export class HoverOperation<Result> { if (this._computer.computeAsync) { this._asyncComputationPromiseDone = false; - this._asyncComputationPromise = createCancelablePromise(token => { - return this._computer.computeAsync(token).then((asyncResult: Result) => { - this._asyncComputationPromiseDone = true; - this._withAsyncResult(asyncResult); - }, (e) => this._onError(e)); - }); + this._asyncComputationPromise = createCancelablePromise(token => this._computer.computeAsync(token)); + this._asyncComputationPromise.then((asyncResult: Result) => { + this._asyncComputationPromiseDone = true; + this._withAsyncResult(asyncResult); + }, (e) => this._onError(e)); } else { this._asyncComputationPromiseDone = true; 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/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<void> { - 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 4171729014e..6ec061f49ac 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -24,6 +24,7 @@ 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 @@ -63,6 +64,12 @@ class CopyLinesUpAction extends AbstractCopyLinesAction { primary: 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 } }); } @@ -80,6 +87,12 @@ class CopyLinesDownAction extends AbstractCopyLinesAction { primary: 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 } }); } @@ -124,6 +137,12 @@ class MoveLinesUpAction extends AbstractMoveLinesAction { 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 } }); } @@ -141,6 +160,12 @@ class MoveLinesDownAction extends AbstractMoveLinesAction { 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 } }); } diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 87fe09b073e..91adad411bd 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -149,7 +149,7 @@ class LinkDetector implements editorCommon.IEditorContribution { private editor: ICodeEditor; private enabled: boolean; private listenersToRemove: IDisposable[]; - private timeoutPromise: async.CancelablePromise<void>; + private timeout: async.TimeoutTimer; private computePromise: async.CancelablePromise<Link[]>; private activeLinkDecorationId: string; private openerService: IOpenerService; @@ -201,7 +201,7 @@ class LinkDetector implements editorCommon.IEditorContribution { this.listenersToRemove.push(editor.onDidChangeModelLanguage((e) => this.onModelModeChanged())); this.listenersToRemove.push(LinkProviderRegistry.onDidChange((e) => this.onModelModeChanged())); - this.timeoutPromise = null; + this.timeout = new async.TimeoutTimer(); this.computePromise = null; this.currentOccurrences = {}; this.activeLinkDecorationId = null; @@ -225,13 +225,7 @@ class LinkDetector implements editorCommon.IEditorContribution { } private onChange(): void { - if (!this.timeoutPromise) { - this.timeoutPromise = async.timeout(LinkDetector.RECOMPUTE_TIME); - this.timeoutPromise.then(() => { - this.timeoutPromise = null; - this.beginCompute(); - }); - } + this.timeout.setIfNotSet(() => this.beginCompute(), LinkDetector.RECOMPUTE_TIME); } private async beginCompute(): Promise<void> { @@ -374,10 +368,7 @@ class LinkDetector implements editorCommon.IEditorContribution { } private stop(): void { - if (this.timeoutPromise) { - this.timeoutPromise.cancel(); - this.timeoutPromise = null; - } + this.timeout.cancel(); if (this.computePromise) { this.computePromise.cancel(); this.computePromise = null; @@ -387,6 +378,7 @@ class LinkDetector implements editorCommon.IEditorContribution { public dispose(): void { this.listenersToRemove = dispose(this.listenersToRemove); this.stop(); + this.timeout.dispose(); } } diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index ed677d9b80f..c93314e994a 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -26,6 +26,7 @@ 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 { @@ -43,6 +44,12 @@ export class InsertCursorAbove extends EditorAction { 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 } }); } @@ -82,6 +89,12 @@ export class InsertCursorBelow extends EditorAction { 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 } }); } @@ -117,6 +130,12 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { kbExpr: EditorContextKeys.editorTextFocus, 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 } }); } @@ -528,6 +547,12 @@ export class AddSelectionToNextFindMatchAction extends MultiCursorSelectionContr kbExpr: EditorContextKeys.focus, 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 } }); } @@ -542,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 { @@ -594,6 +625,12 @@ export class SelectHighlightsAction extends MultiCursorSelectionControllerAction kbExpr: EditorContextKeys.focus, 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 } }); } diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 9a5cfbdb383..ec1fe98b373 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -8,7 +8,6 @@ import 'vs/css!./parameterHints'; import * as nls from 'vs/nls'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as dom from 'vs/base/browser/dom'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { SignatureHelp, SignatureInformation, SignatureHelpProviderRegistry } from 'vs/editor/common/modes'; @@ -169,7 +168,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(); @@ -294,7 +293,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.keyVisible.set(true); this.visible = true; - TPromise.timeout(100).done(() => dom.addClass(this.element, 'visible')); + setTimeout(() => dom.addClass(this.element, 'visible'), 100); this.editor.layoutContentWidget(this); } @@ -476,14 +475,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 +496,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/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 = <LayoutData>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..ce56fc0822e 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 { ILabelService } from 'vs/platform/label/common/label'; +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, + @ILabelService private readonly _uriLabel: ILabelService, @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._uriLabel.getUriLabel(parent, true) : undefined, { title: this._uriLabel.getUriLabel(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, + @ILabelService private readonly _uriLabel: ILabelService, ) { // } @@ -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._uriLabel, 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, + @ILabelService private _uriLabel: ILabelService ) { 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._uriLabel.getUriLabel(dirname(reference.uri), false)); } else { this.setTitle(nls.localize('peekView.alternateTitle', "References")); } diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 76b6057a039..cfe14c52788 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -17,6 +17,7 @@ import { TokenSelectionSupport, ILogicalSelectionEntry } from './tokenSelectionS 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 @@ -176,6 +177,12 @@ class GrowSelectionAction extends AbstractSmartSelect { primary: KeyMod.Shift | KeyMod.Alt | 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 } }); } @@ -193,6 +200,12 @@ class ShrinkSelectionAction extends AbstractSmartSelect { primary: KeyMod.Shift | KeyMod.Alt | 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 9a3a443b47a..03e9a581b1a 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -5,22 +5,22 @@ 'use strict'; -import { RawContextKey, IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { SnippetSession } from './snippetSession'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/suggest'; -import { ISuggestion } from 'vs/editor/common/modes'; -import { Selection } from 'vs/editor/common/core/selection'; -import { Range } from 'vs/editor/common/core/range'; -import { Choice } from 'vs/editor/contrib/snippet/snippetParser'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { repeat } from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ISuggestion } from 'vs/editor/common/modes'; +import { Choice } from 'vs/editor/contrib/snippet/snippetParser'; +import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/suggest'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; +import { SnippetSession } from './snippetSession'; export class SnippetController2 implements IEditorContribution { @@ -65,13 +65,14 @@ export class SnippetController2 implements IEditorContribution { insert( template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, - undoStopBefore: boolean = true, undoStopAfter: boolean = true + undoStopBefore: boolean = true, undoStopAfter: boolean = true, + adjustWhitespace: boolean = true, ): void { // this is here to find out more about the yet-not-understood // error that sometimes happens when we fail to inserted a nested // snippet try { - this._doInsert(template, overwriteBefore, overwriteAfter, undoStopBefore, undoStopAfter); + this._doInsert(template, overwriteBefore, overwriteAfter, undoStopBefore, undoStopAfter, adjustWhitespace); } catch (e) { this.cancel(); @@ -85,7 +86,8 @@ export class SnippetController2 implements IEditorContribution { private _doInsert( template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, - undoStopBefore: boolean = true, undoStopAfter: boolean = true + undoStopBefore: boolean = true, undoStopAfter: boolean = true, + adjustWhitespace: boolean = true, ): void { // don't listen while inserting the snippet @@ -98,10 +100,10 @@ export class SnippetController2 implements IEditorContribution { if (!this._session) { this._modelVersionId = this._editor.getModel().getAlternativeVersionId(); - this._session = new SnippetSession(this._editor, template, overwriteBefore, overwriteAfter); + this._session = new SnippetSession(this._editor, template, overwriteBefore, overwriteAfter, adjustWhitespace); this._session.insert(); } else { - this._session.merge(template, overwriteBefore, overwriteAfter); + this._session.merge(template, overwriteBefore, overwriteAfter, adjustWhitespace); } if (undoStopAfter) { 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/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index ba550ec1d24..65d1fe67f3d 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -5,21 +5,21 @@ 'use strict'; -import 'vs/css!./snippetSession'; -import { getLeadingWhitespace } from 'vs/base/common/strings'; -import { ITextModel, TrackedRangeStickiness, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { TextmateSnippet, Placeholder, Choice, Text, SnippetParser } from './snippetParser'; -import { Selection } from 'vs/editor/common/core/selection'; -import { Range } from 'vs/editor/common/core/range'; -import { IPosition } from 'vs/editor/common/core/position'; import { groupBy } from 'vs/base/common/arrays'; import { dispose } from 'vs/base/common/lifecycle'; -import { SelectionBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, ClipboardBasedVariableResolver, TimeBasedVariableResolver } from './snippetVariables'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { getLeadingWhitespace } from 'vs/base/common/strings'; +import 'vs/css!./snippetSession'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { IPosition } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { optional } from 'vs/platform/instantiation/common/instantiation'; +import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet } from './snippetParser'; +import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver } from './snippetVariables'; export class OneSnippet { @@ -271,7 +271,7 @@ export class OneSnippet { export class SnippetSession { - static adjustWhitespace2(model: ITextModel, position: IPosition, snippet: TextmateSnippet): void { + static adjustWhitespace(model: ITextModel, position: IPosition, snippet: TextmateSnippet): void { const line = model.getLineContent(position.lineNumber); const lineLeadingWhitespace = getLeadingWhitespace(line, 0, position.column - 1); @@ -317,7 +317,7 @@ export class SnippetSession { return selection; } - static createEditsAndSnippets(editor: ICodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { + static createEditsAndSnippets(editor: ICodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { const model = editor.getModel(); const edits: IIdentifiedSingleEditOperation[] = []; @@ -365,7 +365,9 @@ export class SnippetSession { // adjust the template string to match the indentation and // whitespace rules of this insert location (can be different for each cursor) const start = snippetSelection.getStartPosition(); - SnippetSession.adjustWhitespace2(model, start, snippet); + if (adjustWhitespace) { + SnippetSession.adjustWhitespace(model, start, snippet); + } snippet.resolveVariables(new CompositeSnippetVariableResolver([ modelBasedVariableResolver, @@ -392,13 +394,15 @@ export class SnippetSession { private readonly _templateMerges: [number, number, string][] = []; private readonly _overwriteBefore: number; private readonly _overwriteAfter: number; + private readonly _adjustWhitespace: boolean; private _snippets: OneSnippet[] = []; - constructor(editor: ICodeEditor, template: string, overwriteBefore: number = 0, overwriteAfter: number = 0) { + constructor(editor: ICodeEditor, template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, adjustWhitespace: boolean = true) { this._editor = editor; this._template = template; this._overwriteBefore = overwriteBefore; this._overwriteAfter = overwriteAfter; + this._adjustWhitespace = adjustWhitespace; } dispose(): void { @@ -414,7 +418,7 @@ export class SnippetSession { const model = this._editor.getModel(); // make insert edit and start with first selections - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._overwriteBefore, this._overwriteAfter, false); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._overwriteBefore, this._overwriteAfter, false, this._adjustWhitespace); this._snippets = snippets; const selections = model.pushEditOperations(this._editor.getSelections(), edits, undoEdits => { @@ -428,9 +432,9 @@ export class SnippetSession { this._editor.revealRange(selections[0]); } - merge(template: string, overwriteBefore: number = 0, overwriteAfter: number = 0): void { + merge(template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, adjustWhitespace: boolean = true): void { this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]); - const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, overwriteBefore, overwriteAfter, true); + const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, overwriteBefore, overwriteAfter, true, adjustWhitespace); this._editor.setSelections(this._editor.getModel().pushEditOperations(this._editor.getSelections(), edits, undoEdits => { diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index 2787ad8e2a1..361e3d57009 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts @@ -5,14 +5,14 @@ 'use strict'; import * as assert from 'assert'; -import { Selection } from 'vs/editor/common/core/selection'; -import { Range } from 'vs/editor/common/core/range'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { SnippetSession } from 'vs/editor/contrib/snippet/snippetSession'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; -import { TextModel } from 'vs/editor/common/model/textModel'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; suite('SnippetSession', function () { @@ -43,7 +43,7 @@ suite('SnippetSession', function () { function assertNormalized(position: IPosition, input: string, expected: string): void { const snippet = new SnippetParser().parse(input); - SnippetSession.adjustWhitespace2(model, position, snippet); + SnippetSession.adjustWhitespace(model, position, snippet); assert.equal(snippet.toTextmateString(), expected); } @@ -125,6 +125,14 @@ suite('SnippetSession', function () { assertSelections(editor, new Selection(3, 1, 3, 1), new Selection(6, 5, 6, 5)); }); + test('snippets, newline NO whitespace adjust', () => { + + editor.setSelection(new Selection(2, 5, 2, 5)); + const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', 0, 0, false); + session.insert(); + assert.equal(editor.getModel().getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}'); + }); + test('snippets, selections -> next/prev', () => { const session = new SnippetSession(editor, 'f$1oo${2:bar}foo$0'); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index e281a671244..8fbac1b8084 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -4,30 +4,30 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as nls from 'vs/nls'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { alert } from 'vs/base/browser/ui/aria/aria'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ISuggestSupport } from 'vs/editor/common/modes'; -import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; -import { Context as SuggestContext } from './suggest'; -import { SuggestModel, State } from './suggestModel'; -import { ICompletionItem } from './completionModel'; -import { SuggestWidget, ISelectedSuggestion } from './suggestWidget'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory'; +import * as nls from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ICompletionItem } from './completionModel'; +import { Context as SuggestContext } from './suggest'; +import { State, SuggestModel } from './suggestModel'; +import { ISelectedSuggestion, SuggestWidget } from './suggestWidget'; class AcceptOnCharacterOracle { @@ -88,6 +88,8 @@ export class SuggestController implements IEditorContribution { private _memory: SuggestMemories; private _toDispose: IDisposable[] = []; + private readonly _sticky = false; // for development purposes only + constructor( private _editor: ICodeEditor, @ICommandService private readonly _commandService: ICommandService, @@ -112,6 +114,11 @@ export class SuggestController implements IEditorContribution { this._widget.hideWidget(); } })); + this._toDispose.push(this._editor.onDidBlurEditorText(() => { + if (!this._sticky) { + this._model.cancel(); + } + })); // Manage the acceptSuggestionsOnEnter context key let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService); @@ -216,7 +223,8 @@ export class SuggestController implements IEditorContribution { insertText, suggestion.overwriteBefore + columnDelta, suggestion.overwriteAfter, - false, false + false, false, + !suggestion.noWhitespaceAdjust ); this._editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 4a427b85dfe..cceda3b6cfd 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -134,9 +134,24 @@ export class SuggestModel implements IDisposable { this._toDispose.push(this._editor.onDidChangeCursorSelection(e => { this._onCursorChange(e); })); - this._toDispose.push(this._editor.onDidChangeModelContent(e => { + + let editorIsComposing = false; + this._toDispose.push(this._editor.onCompositionStart(() => { + editorIsComposing = true; + })); + this._toDispose.push(this._editor.onCompositionEnd(() => { + // refilter when composition ends + editorIsComposing = false; this._refilterCompletionItems(); })); + this._toDispose.push(this._editor.onDidChangeModelContent(() => { + // only filter completions when the editor isn't + // composing a character, e.g. ¨ + u makes ü but just + // ¨ cannot be used for filtering + if (!editorIsComposing) { + this._refilterCompletionItems(); + } + })); this._updateTriggerCharacters(); this._updateQuickSuggest(); diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 3360b6fbda0..90e7748c573 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -35,7 +35,6 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TimeoutTimer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -const sticky = false; // for development purposes const expandSuggestionDocsByDefault = false; const maxSuggestionsToShow = 12; @@ -111,10 +110,12 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> { const fontFamily = configuration.fontInfo.fontFamily; const fontSize = configuration.contribInfo.suggestFontSize || configuration.fontInfo.fontSize; const lineHeight = configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight; + const fontWeight = configuration.fontInfo.fontWeight; const fontSizePx = `${fontSize}px`; const lineHeightPx = `${lineHeight}px`; data.root.style.fontSize = fontSizePx; + data.root.style.fontWeight = fontWeight; main.style.fontFamily = fontFamily; main.style.lineHeight = lineHeightPx; data.icon.style.height = lineHeightPx; @@ -331,10 +332,12 @@ class SuggestionDetails { const fontFamily = configuration.fontInfo.fontFamily; const fontSize = configuration.contribInfo.suggestFontSize || configuration.fontInfo.fontSize; const lineHeight = configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight; + const fontWeight = configuration.fontInfo.fontWeight; const fontSizePx = `${fontSize}px`; const lineHeightPx = `${lineHeight}px`; this.el.style.fontSize = fontSizePx; + this.el.style.fontWeight = fontWeight; this.type.style.fontFamily = fontFamily; this.close.style.height = lineHeightPx; this.close.style.width = lineHeightPx; @@ -456,7 +459,6 @@ export class SuggestWidget implements IContentWidget, IVirtualDelegate<ICompleti listInactiveFocusOutline: activeContrastBorder }), themeService.onThemeChange(t => this.onThemeChange(t)), - editor.onDidBlurEditorText(() => this.onEditorBlur()), editor.onDidLayoutChange(() => this.onEditorLayoutChange()), this.list.onSelectionChange(e => this.onListSelection(e)), this.list.onFocusChange(e => this.onListFocus(e)), @@ -481,18 +483,6 @@ export class SuggestWidget implements IContentWidget, IVirtualDelegate<ICompleti this.editor.layoutContentWidget(this); } - private onEditorBlur(): void { - if (sticky) { - return; - } - - this.editorBlurTimeout.cancelAndSet(() => { - if (!this.editor.hasTextFocus()) { - this.setState(State.Hidden); - } - }, 150); - } - private onEditorLayoutChange(): void { if ((this.state === State.Open || this.state === State.Details) && this.expandDocsSettingFromStorage()) { this.expandSideOrBelow(); @@ -714,18 +704,21 @@ export class SuggestWidget implements IContentWidget, IVirtualDelegate<ICompleti this.completionModel = null; } else { - const { stats } = this.completionModel; - stats['wasAutomaticallyTriggered'] = !!isAuto; - /* __GDPR__ - "suggestWidget" : { - "wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "${include}": [ - "${ICompletionStats}", - "${EditorTelemetryData}" - ] - } - */ - this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() }); + + if (this.state !== State.Open) { + const { stats } = this.completionModel; + stats['wasAutomaticallyTriggered'] = !!isAuto; + /* __GDPR__ + "suggestWidget" : { + "wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "${include}": [ + "${ICompletionStats}", + "${EditorTelemetryData}" + ] + } + */ + this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() }); + } this.list.splice(0, this.list.length, this.completionModel.items); diff --git a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index 8c852574b12..684fc6757e2 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { first2, createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; -import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { onUnexpectedExternalError, onUnexpectedError } from 'vs/base/common/errors'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { registerEditorContribution, EditorAction, IActionOptions, registerEditorAction, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; @@ -300,7 +300,7 @@ class WordHighlighter { this.workerRequestValue = data || []; this._beginRenderDecorations(); } - }); + }, onUnexpectedError); } this._lastWordRange = currentWordRange; 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 229dbedfc18..fec93549d35 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -16,6 +16,7 @@ import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharact 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() { @@ -26,7 +27,7 @@ export class DeleteWordPartLeft extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Backspace, + primary: 0, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace }, weight: KeybindingWeight.EditorContrib } @@ -51,7 +52,7 @@ export class DeleteWordPartRight extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Delete, + primary: 0, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Delete }, weight: KeybindingWeight.EditorContrib } @@ -79,33 +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, + 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, + 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 { @@ -121,7 +127,7 @@ export class CursorWordPartRight extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, + primary: 0, mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow }, weight: KeybindingWeight.EditorContrib } @@ -137,7 +143,7 @@ export class CursorWordPartRightSelect extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | 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/editor.api.ts b/src/vs/editor/editor.api.ts index 5594a22627c..7bd274aab60 100644 --- a/src/vs/editor/editor.api.ts +++ b/src/vs/editor/editor.api.ts @@ -35,7 +35,6 @@ export const Position = api.Position; export const Range = api.Range; export const Selection = api.Selection; export const SelectionDirection = api.SelectionDirection; -export const Severity = api.Severity; export const MarkerSeverity = api.MarkerSeverity; export const MarkerTag = api.MarkerTag; export const Promise = api.Promise; diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index 1f5595800c8..c079eaa094b 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -14,6 +14,7 @@ import { LineTokens, IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import * as strings from 'vs/base/common/strings'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { timeout } from 'vs/base/common/async'; export interface IColorizerOptions { tabSize?: number; @@ -85,7 +86,7 @@ export class Colorizer { } // wait 500ms for mode to load, then give up - return TPromise.any([this._tokenizationSupportChangedPromise(language), TPromise.timeout(500)]).then(_ => { + return TPromise.any([this._tokenizationSupportChangedPromise(language), timeout(500)]).then(_ => { let tokenizationSupport = TokenizationRegistry.get(language); if (tokenizationSupport) { return _colorize(lines, options.tabSize, tokenizationSupport); @@ -99,6 +100,7 @@ export class Colorizer { const containsRTL = ViewLineRenderingData.containsRTL(line, isBasicASCII, mightContainRTL); let renderResult = renderViewLine(new RenderLineInput( false, + true, line, false, isBasicASCII, @@ -152,6 +154,7 @@ function _fakeColorize(lines: string[], tabSize: number): string { const containsRTL = ViewLineRenderingData.containsRTL(line, isBasicASCII, /* check for RTL */true); let renderResult = renderViewLine(new RenderLineInput( false, + true, line, false, isBasicASCII, @@ -187,6 +190,7 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport: const containsRTL = ViewLineRenderingData.containsRTL(line, isBasicASCII, /* check for RTL */true); let renderResult = renderViewLine(new RenderLineInput( false, + true, line, false, isBasicASCII, diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index 7fe59dd0453..7a0168883c7 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -50,7 +50,7 @@ export class EditorActionCommandEntry extends QuickOpenEntryGroup { if (mode === Mode.OPEN) { // Use a timeout to give the quick open widget a chance to close itself first - TPromise.timeout(50).done(() => { + setTimeout(() => { // Some actions are enabled only when editor has focus this.editor.focus(); @@ -61,7 +61,7 @@ export class EditorActionCommandEntry extends QuickOpenEntryGroup { } catch (error) { onUnexpectedError(error); } - }, onUnexpectedError); + }, 50); return true; } 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 d20f7346b83..b7e07e8bd6f 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -45,6 +45,7 @@ import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/mo import { IModelService } from 'vs/editor/common/services/modelService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { localize } from 'vs/nls'; +import { ILabelService, LabelRules } from 'vs/platform/label/common/label'; export class SimpleModel implements ITextEditorModel { @@ -68,6 +69,10 @@ export class SimpleModel implements ITextEditorModel { return this.model; } + public isReadonly(): boolean { + return false; + } + public dispose(): void { this._onDispose.fire(); } @@ -503,7 +508,7 @@ export class SimpleWorkspaceContextService implements IWorkspaceContextService { constructor() { const resource = URI.from({ scheme: SimpleWorkspaceContextService.SCHEME, authority: 'model', path: '/' }); - this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [new WorkspaceFolder({ uri: resource, name: '', index: 0 })], name: resource.fsPath }; + this.workspace = { id: '4064f6ec-cb38-4ad0-af64-ee6467e63c82', folders: [new WorkspaceFolder({ uri: resource, name: '', index: 0 })] }; } public getWorkspace(): IWorkspace { @@ -590,3 +595,25 @@ export class SimpleBulkEditService implements IBulkEditService { }); } } + +export class SimpleUriLabelService implements ILabelService { + _serviceBrand: any; + + private readonly _onDidRegisterFormatter: Emitter<{ scheme: string, formatter: LabelRules }> = new Emitter<{ scheme: string, formatter: LabelRules }>(); + public readonly onDidRegisterFormatter: Event<{ scheme: string, formatter: LabelRules }> = this._onDidRegisterFormatter.event; + + public getUriLabel(resource: URI, relative?: boolean): string { + if (resource.scheme === 'file') { + return resource.fsPath; + } + return resource.path; + } + + public getWorkspaceLabel(workspace: IWorkspaceIdentifier | URI | IWorkspace, options?: { verbose: boolean; }): string { + return ''; + } + + public registerFormatter(schema: string, formatter: LabelRules): IDisposable { + throw new Error('Not implemented'); + } +} diff --git a/src/vs/editor/standalone/browser/standalone-tokens.css b/src/vs/editor/standalone/browser/standalone-tokens.css index c5860572a8e..1396ea01d0a 100644 --- a/src/vs/editor/standalone/browser/standalone-tokens.css +++ b/src/vs/editor/standalone/browser/standalone-tokens.css @@ -9,14 +9,14 @@ font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; } -.monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus { +.monaco-menu .monaco-action-bar.vertical .action-item .action-menu-item:focus .action-label { color: #0059AC; stroke-width: 1.2px; text-shadow: 0px 0px 0.15px #0059AC; } -.monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus, -.monaco-editor.hc-black .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus { +.monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label, +.monaco-editor.hc-black .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label { color: #ACDDFF; stroke-width: 1.2px; text-shadow: 0px 0px 0.15px #ACDDFF; @@ -211,14 +211,14 @@ } /* contextmenu */ - .monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus, - .monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item .action-label:focus { + .monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label, + .monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .action-label { -ms-high-contrast-adjust: none; color: highlighttext !important; background-color: highlight !important; } - .monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-item .action-label:hover, - .monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item .action-label:hover { + .monaco-editor.vs .monaco-menu .monaco-action-bar.vertical .action-menu-item:hover .action-label, + .monaco-editor.vs-dark .monaco-menu .monaco-action-bar.vertical .action-menu-item:hover .action-label { -ms-high-contrast-adjust: none; background: transparent !important; border: 1px solid highlight; diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 6018f9f47d5..890b51f49bd 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -84,7 +84,7 @@ export interface IEditorConstructionOptions extends IEditorOptions { /** * The initial model associated with this code editor. */ - model?: ITextModel; + model?: ITextModel | null; /** * The initial value of the auto created model in the editor. * To not create automatically a model, use `model: null`. diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 592e1705796..b0287953b31 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -880,7 +880,6 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { SymbolKind: modes.SymbolKind, IndentAction: IndentAction, SuggestTriggerKind: modes.SuggestTriggerKind, - CommentThreadCollapsibleState: modes.CommentThreadCollapsibleState, FoldingRangeKind: modes.FoldingRangeKind }; } diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index d3275d83f30..c6ca31a3c08 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -33,7 +33,7 @@ import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/st import { SimpleConfigurationService, SimpleResourceConfigurationService, SimpleMenuService, SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService, SimpleNotificationService, - StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleDialogService, SimpleBulkEditService + StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleDialogService, SimpleBulkEditService, SimpleUriLabelService } from 'vs/editor/standalone/browser/simpleServices'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IMenuService } from 'vs/platform/actions/common/actions'; @@ -44,6 +44,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati 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'; +import { ILabelService } from 'vs/platform/label/common/label'; export interface IEditorOverrideServices { [index: string]: any; @@ -121,6 +122,8 @@ export module StaticServices { export const contextService = define(IWorkspaceContextService, () => new SimpleWorkspaceContextService()); + export const labelService = define(ILabelService, () => new SimpleUriLabelService()); + export const telemetryService = define(ITelemetryService, () => new StandaloneTelemetryService()); export const dialogService = define(IDialogService, () => new SimpleDialogService()); diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 61e358dc4a0..f72758ff75e 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -100,7 +100,7 @@ class StandaloneTheme implements IStandaloneTheme { } public defines(colorId: ColorIdentifier): boolean { - return this.getColors().hasOwnProperty(colorId); + return Object.prototype.hasOwnProperty.call(this.getColors(), colorId); } public get type() { diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 7c4d3136f27..85f20c7dd29 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -3947,6 +3947,21 @@ suite('autoClosingPairs', () => { ], })); } + + public setAutocloseEnabledSet(chars: string) { + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + autoCloseBefore: chars, + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '\'', close: '\'', notIn: ['string', 'comment'] }, + { open: '\"', close: '\"', notIn: ['string'] }, + { open: '`', close: '`', notIn: ['string', 'comment'] }, + { open: '/**', close: ' */', notIn: ['string'] } + ], + })); + } } const enum ColumnType { @@ -3982,7 +3997,7 @@ suite('autoClosingPairs', () => { cursorCommand(cursor, H.Undo); } - test('open parens', () => { + test('open parens: default', () => { let mode = new AutoClosingMode(); usingCursor({ text: [ @@ -3998,6 +4013,52 @@ suite('autoClosingPairs', () => { languageIdentifier: mode.getLanguageIdentifier() }, (model, cursor) => { + let autoClosePositions = [ + 'var| a| |=| [|]|;|', + 'var| b| |=| `asd`|;|', + 'var| c| |=| \'asd\'|;|', + 'var| d| |=| "asd"|;|', + 'var| e| |=| /*3*/| 3|;|', + 'var| f| |=| /**| 3| */3|;|', + 'var| g| |=| (3+5|)|;|', + 'var| h| |=| {| a|:| \'value\'| |}|;|', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + } + } + } + }); + mode.dispose(); + }); + + test('open parens: whitespace', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: 'beforeWhitespace' + } + }, (model, cursor) => { + let autoClosePositions = [ 'var| a| =| [|];|', 'var| b| =| `asd`;|', @@ -4025,6 +4086,259 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('open parens disabled/enabled open quotes enabled/disabled', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = [];', + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: 'beforeWhitespace', + autoClosingQuotes: 'never' + } + }, (model, cursor) => { + + let autoClosePositions = [ + 'var| a| =| [|];|', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + } + assertType(model, cursor, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); + } + } + }); + + usingCursor({ + text: [ + 'var b = [];', + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: 'never', + autoClosingQuotes: 'beforeWhitespace' + } + }, (model, cursor) => { + + let autoClosePositions = [ + 'var b =| [|];|', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`); + } + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + } + } + }); + mode.dispose(); + }); + + test('configurable open parens', () => { + let mode = new AutoClosingMode(); + mode.setAutocloseEnabledSet('abc'); + usingCursor({ + text: [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: 'languageDefined' + } + }, (model, cursor) => { + + let autoClosePositions = [ + 'v|ar |a = [|];|', + 'v|ar |b = `|asd`;|', + 'v|ar |c = \'|asd\';|', + 'v|ar d = "|asd";|', + 'v|ar e = /*3*/ 3;|', + 'v|ar f = /** 3 */3;|', + 'v|ar g = (3+5|);|', + 'v|ar h = { |a: \'v|alue\' |};|', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + } + } + } + }); + mode.dispose(); + }); + + test('auto-pairing can be disabled', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: 'never', + autoClosingQuotes: 'never' + } + }, (model, cursor) => { + + let autoClosePositions = [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + assertType(model, cursor, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(model, cursor, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`); + } + } + } + }); + mode.dispose(); + }); + + test('auto wrapping is configurable', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = asd' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + cursor.setSelections('test', [ + new Selection(1, 1, 1, 4), + new Selection(1, 9, 1, 12), + ]); + + // type a ` + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + + assert.equal(model.getValue(), '`var` a = `asd`'); + + // type a ( + cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + + assert.equal(model.getValue(), '`(var)` a = `(asd)`'); + }); + + usingCursor({ + text: [ + 'var a = asd' + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoWrapping: 'never' + } + }, (model, cursor) => { + + cursor.setSelections('test', [ + new Selection(1, 1, 1, 4), + ]); + + // type a ` + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + + assert.equal(model.getValue(), '` a = asd'); + }); + + usingCursor({ + text: [ + 'var a = asd' + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoWrapping: 'quotes' + } + }, (model, cursor) => { + + cursor.setSelections('test', [ + new Selection(1, 1, 1, 4), + ]); + + // type a ` + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + assert.equal(model.getValue(), '`var` a = asd'); + + // type a ( + cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + assert.equal(model.getValue(), '`(` a = asd'); + }); + + usingCursor({ + text: [ + 'var a = asd' + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoWrapping: 'brackets' + } + }, (model, cursor) => { + + cursor.setSelections('test', [ + new Selection(1, 1, 1, 4), + ]); + + // type a ( + cursorCommand(cursor, H.Type, { text: '(' }, 'keyboard'); + assert.equal(model.getValue(), '(var) a = asd'); + + // type a ` + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + assert.equal(model.getValue(), '(`) a = asd'); + }); + mode.dispose(); + }); + test('quote', () => { let mode = new AutoClosingMode(); usingCursor({ @@ -4042,14 +4356,14 @@ suite('autoClosingPairs', () => { }, (model, cursor) => { let autoClosePositions = [ - 'var a =| [|];|', - 'var b =| |`asd`;|', - 'var c =| |\'asd!\';|', - 'var d =| |"asd";|', - 'var e =| /*3*/| 3;|', - 'var f =| /**| 3 */3;|', - 'var g =| (3+5);|', - 'var h =| {| a:| |\'value!\'| |};|', + 'var a |=| [|]|;|', + 'var b |=| |`asd`|;|', + 'var c |=| |\'asd!\'|;|', + 'var d |=| |"asd"|;|', + 'var e |=| /*3*/| 3;|', + 'var f |=| /**| 3 */3;|', + 'var g |=| (3+5)|;|', + 'var h |=| {| a:| |\'value!\'| |}|;|', ]; for (let i = 0, len = autoClosePositions.length; i < len; i++) { const lineNumber = i + 1; 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/editor/test/common/mocks/testConfiguration.ts b/src/vs/editor/test/common/mocks/testConfiguration.ts index b94830a8456..0492346bff7 100644 --- a/src/vs/editor/test/common/mocks/testConfiguration.ts +++ b/src/vs/editor/test/common/mocks/testConfiguration.ts @@ -39,6 +39,7 @@ export class TestConfiguration extends CommonEditorConfiguration { isMonospace: true, typicalHalfwidthCharacterWidth: 10, typicalFullwidthCharacterWidth: 20, + canUseHalfwidthRightwardsArrow: true, spaceWidth: 10, maxDigitWidth: 10, }, true); diff --git a/src/vs/editor/test/common/standalone/standaloneBase.test.ts b/src/vs/editor/test/common/standalone/standaloneBase.test.ts index 64849eb80cb..4cec807f3c1 100644 --- a/src/vs/editor/test/common/standalone/standaloneBase.test.ts +++ b/src/vs/editor/test/common/standalone/standaloneBase.test.ts @@ -5,18 +5,8 @@ 'use strict'; import * as assert from 'assert'; -import { KeyCode as StandaloneKeyCode, Severity as StandaloneSeverity } from 'vs/editor/common/standalone/standaloneBase'; +import { KeyCode as StandaloneKeyCode } from 'vs/editor/common/standalone/standaloneBase'; import { KeyCode as RuntimeKeyCode } from 'vs/base/common/keyCodes'; -import RuntimeSeverity from 'vs/base/common/severity'; - -suite('StandaloneBase', () => { - test('exports enums correctly', () => { - assert.equal(StandaloneSeverity.Ignore, RuntimeSeverity.Ignore); - assert.equal(StandaloneSeverity.Info, RuntimeSeverity.Info); - assert.equal(StandaloneSeverity.Warning, RuntimeSeverity.Warning); - assert.equal(StandaloneSeverity.Error, RuntimeSeverity.Error); - }); -}); suite('KeyCode', () => { test('is exported correctly in standalone editor', () => { diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 59705ec0d93..968b1a20126 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -29,6 +29,7 @@ suite('viewLineRenderer.renderLine', () => { function assertCharacterReplacement(lineContent: string, tabSize: number, expected: string, expectedCharOffsetInPart: number[][], expectedPartLengts: number[]): void { let _actual = renderViewLine(new RenderLineInput( false, + true, lineContent, false, strings.isBasicASCII(lineContent), @@ -77,6 +78,7 @@ suite('viewLineRenderer.renderLine', () => { function assertParts(lineContent: string, tabSize: number, parts: ViewLineToken[], expected: string, expectedCharOffsetInPart: number[][], expectedPartLengts: number[]): void { let _actual = renderViewLine(new RenderLineInput( false, + true, lineContent, false, true, @@ -115,6 +117,7 @@ suite('viewLineRenderer.renderLine', () => { test('overflow', () => { let _actual = renderViewLine(new RenderLineInput( false, + true, 'Hello world!', false, true, @@ -218,6 +221,7 @@ suite('viewLineRenderer.renderLine', () => { let _actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, true, @@ -279,6 +283,7 @@ suite('viewLineRenderer.renderLine', () => { let _actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, true, @@ -340,6 +345,7 @@ suite('viewLineRenderer.renderLine', () => { let _actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, true, @@ -378,6 +384,7 @@ suite('viewLineRenderer.renderLine', () => { let _actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, false, @@ -407,6 +414,7 @@ suite('viewLineRenderer.renderLine', () => { let lineParts = createViewLineTokens([createPart(lineText.length, 1)]); let actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, true, @@ -506,6 +514,7 @@ suite('viewLineRenderer.renderLine', () => { let lineParts = createViewLineTokens([createPart(lineText.length, 1)]); let actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, true, @@ -541,6 +550,7 @@ suite('viewLineRenderer.renderLine', () => { let lineParts = createViewLineTokens([createPart(lineText.length, 1)]); let actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, false, @@ -569,6 +579,7 @@ suite('viewLineRenderer.renderLine', () => { ]; let actual = renderViewLine(new RenderLineInput( false, + true, lineText, false, false, @@ -613,6 +624,7 @@ suite('viewLineRenderer.renderLine', () => { ].join(''); let _actual = renderViewLine(new RenderLineInput( + true, true, lineText, false, @@ -696,6 +708,7 @@ suite('viewLineRenderer.renderLine 2', () => { function testCreateLineParts(fontIsMonospace: boolean, lineContent: string, tokens: ViewLineToken[], fauxIndentLength: number, renderWhitespace: 'none' | 'boundary' | 'all', expected: string): void { let actual = renderViewLine(new RenderLineInput( fontIsMonospace, + true, lineContent, false, true, @@ -720,6 +733,7 @@ suite('viewLineRenderer.renderLine 2', () => { let actual = renderViewLine(new RenderLineInput( false, + true, lineContent, false, true, @@ -749,6 +763,7 @@ suite('viewLineRenderer.renderLine 2', () => { let lineContent = '\'let url = `http://***/_api/web/lists/GetByTitle(\\\'Teambuildingaanvragen\\\')/items`;\''; let actual = renderViewLine(new RenderLineInput( + true, true, lineContent, false, @@ -1016,6 +1031,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('createLineParts can handle unsorted inline decorations', () => { let actual = renderViewLine(new RenderLineInput( false, + true, 'Hello world', false, true, @@ -1059,6 +1075,7 @@ suite('viewLineRenderer.renderLine 2', () => { let actual = renderViewLine(new RenderLineInput( false, + true, lineContent, false, true, @@ -1090,6 +1107,7 @@ suite('viewLineRenderer.renderLine 2', () => { let actual = renderViewLine(new RenderLineInput( false, + true, lineContent, false, true, @@ -1122,6 +1140,7 @@ suite('viewLineRenderer.renderLine 2', () => { let actual = renderViewLine(new RenderLineInput( false, + true, lineContent, false, true, @@ -1149,6 +1168,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #37208: Collapsing bullet point containing emoji in Markdown document results in [??] character', () => { let actual = renderViewLine(new RenderLineInput( + true, true, ' 1. 🙏', false, @@ -1178,6 +1198,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #37401: Allow both before and after decorations on empty line', () => { let actual = renderViewLine(new RenderLineInput( + true, true, '', false, @@ -1209,6 +1230,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #38935: GitLens end-of-line blame no longer rendering', () => { let actual = renderViewLine(new RenderLineInput( + true, true, '\t}', false, @@ -1241,6 +1263,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #22832: Consider fullwidth characters when rendering tabs', () => { let actual = renderViewLine(new RenderLineInput( + true, true, 'asd = "擦"\t\t#asd', false, @@ -1269,6 +1292,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #22832: Consider fullwidth characters when rendering tabs (render whitespace)', () => { let actual = renderViewLine(new RenderLineInput( + true, true, 'asd = "擦"\t\t#asd', false, @@ -1303,6 +1327,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #22352: COMBINING ACUTE ACCENT (U+0301)', () => { let actual = renderViewLine(new RenderLineInput( + true, true, '12345689012345678901234568901234567890123456890abába', false, @@ -1331,6 +1356,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #22352: Partially Broken Complex Script Rendering of Tamil', () => { let actual = renderViewLine(new RenderLineInput( + true, true, ' JoyShareல் பின்தொடர்ந்து, விடீயோ, ஜோக்குகள், அனிமேசன், நகைச்சுவை படங்கள் மற்றும் செய்திகளை பெறுவீர்', false, @@ -1363,6 +1389,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #42700: Hindi characters are not being rendered properly', () => { let actual = renderViewLine(new RenderLineInput( + true, true, ' वो ऐसा क्या है जो हमारे अंदर भी है और बाहर भी है। जिसकी वजह से हम सब हैं। जिसने इस सृष्टि की रचना की है।', false, @@ -1390,6 +1417,7 @@ suite('viewLineRenderer.renderLine 2', () => { test('issue #38123: editor.renderWhitespace: "boundary" renders whitespace at line wrap point when line is wrapped', () => { let actual = renderViewLine(new RenderLineInput( + true, true, 'This is a long line which never uses more than two spaces. ', true, @@ -1418,6 +1446,7 @@ suite('viewLineRenderer.renderLine 2', () => { function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: ViewLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void { let renderLineOutput = renderViewLine(new RenderLineInput( false, + true, lineContent, false, true, diff --git a/src/vs/loader.js b/src/vs/loader.js index d2b9f26ab38..406eaf4b65a 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -22,7 +22,7 @@ var _amdLoaderGlobal = this; var AMDLoader; (function (AMDLoader) { AMDLoader.global = _amdLoaderGlobal; - var Environment = /** @class */ (function () { + var Environment = (function () { function Environment() { this._detected = false; this._isWindows = false; @@ -93,7 +93,7 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - var LoaderEvent = /** @class */ (function () { + var LoaderEvent = (function () { function LoaderEvent(type, detail, timestamp) { this.type = type; this.detail = detail; @@ -102,7 +102,7 @@ var AMDLoader; return LoaderEvent; }()); AMDLoader.LoaderEvent = LoaderEvent; - var LoaderEventRecorder = /** @class */ (function () { + var LoaderEventRecorder = (function () { function LoaderEventRecorder(loaderAvailableTimestamp) { this._events = [new LoaderEvent(1 /* LoaderAvailable */, '', loaderAvailableTimestamp)]; } @@ -115,7 +115,7 @@ var AMDLoader; return LoaderEventRecorder; }()); AMDLoader.LoaderEventRecorder = LoaderEventRecorder; - var NullLoaderEventRecorder = /** @class */ (function () { + var NullLoaderEventRecorder = (function () { function NullLoaderEventRecorder() { } NullLoaderEventRecorder.prototype.record = function (type, detail) { @@ -124,9 +124,9 @@ var AMDLoader; NullLoaderEventRecorder.prototype.getEvents = function () { return []; }; - NullLoaderEventRecorder.INSTANCE = new NullLoaderEventRecorder(); return NullLoaderEventRecorder; }()); + NullLoaderEventRecorder.INSTANCE = new NullLoaderEventRecorder(); AMDLoader.NullLoaderEventRecorder = NullLoaderEventRecorder; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- @@ -135,14 +135,14 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - var Utilities = /** @class */ (function () { + var Utilities = (function () { function Utilities() { } /** * This method does not take care of / vs \ */ Utilities.fileUriToFilePath = function (isWindows, uri) { - uri = decodeURI(uri); + uri = decodeURI(uri).replace(/%23/g, '#'); if (isWindows) { if (/^file:\/\/\//.test(uri)) { // This is a URI without a hostname => return only the path segment @@ -221,11 +221,11 @@ var AMDLoader; } return (this.HAS_PERFORMANCE_NOW ? AMDLoader.global.performance.now() : Date.now()); }; - Utilities.NEXT_ANONYMOUS_ID = 1; - Utilities.PERFORMANCE_NOW_PROBED = false; - Utilities.HAS_PERFORMANCE_NOW = false; return Utilities; }()); + Utilities.NEXT_ANONYMOUS_ID = 1; + Utilities.PERFORMANCE_NOW_PROBED = false; + Utilities.HAS_PERFORMANCE_NOW = false; AMDLoader.Utilities = Utilities; })(AMDLoader || (AMDLoader = {})); /*--------------------------------------------------------------------------------------------- @@ -234,7 +234,7 @@ var AMDLoader; *--------------------------------------------------------------------------------------------*/ var AMDLoader; (function (AMDLoader) { - var ConfigurationOptionsUtil = /** @class */ (function () { + var ConfigurationOptionsUtil = (function () { function ConfigurationOptionsUtil() { } /** @@ -340,7 +340,7 @@ var AMDLoader; return ConfigurationOptionsUtil; }()); AMDLoader.ConfigurationOptionsUtil = ConfigurationOptionsUtil; - var Configuration = /** @class */ (function () { + var Configuration = (function () { function Configuration(env, options) { this._env = env; this.options = ConfigurationOptionsUtil.mergeConfigurationOptions(options); @@ -551,7 +551,7 @@ var AMDLoader; /** * Load `scriptSrc` only once (avoid multiple <script> tags) */ - var OnlyOnceScriptLoader = /** @class */ (function () { + var OnlyOnceScriptLoader = (function () { function OnlyOnceScriptLoader(env) { this._env = env; this._scriptLoader = null; @@ -593,7 +593,7 @@ var AMDLoader; }; return OnlyOnceScriptLoader; }()); - var BrowserScriptLoader = /** @class */ (function () { + var BrowserScriptLoader = (function () { function BrowserScriptLoader() { } /** @@ -626,7 +626,7 @@ var AMDLoader; }; return BrowserScriptLoader; }()); - var WorkerScriptLoader = /** @class */ (function () { + var WorkerScriptLoader = (function () { function WorkerScriptLoader() { } WorkerScriptLoader.prototype.load = function (moduleManager, scriptSrc, callback, errorback) { @@ -640,7 +640,7 @@ var AMDLoader; }; return WorkerScriptLoader; }()); - var NodeScriptLoader = /** @class */ (function () { + var NodeScriptLoader = (function () { function NodeScriptLoader(env) { this._env = env; this._didInitialize = false; @@ -847,9 +847,9 @@ var AMDLoader; var timeout = minTimeout + Math.ceil(Math.random() * minTimeout); setTimeout(callback, timeout); }; - NodeScriptLoader._BOM = 0xFEFF; return NodeScriptLoader; }()); + NodeScriptLoader._BOM = 0xFEFF; function createScriptLoader(env) { return new OnlyOnceScriptLoader(env); } @@ -863,7 +863,7 @@ var AMDLoader; (function (AMDLoader) { // ------------------------------------------------------------------------ // ModuleIdResolver - var ModuleIdResolver = /** @class */ (function () { + var ModuleIdResolver = (function () { function ModuleIdResolver(fromModuleId) { var lastSlash = fromModuleId.lastIndexOf('/'); if (lastSlash !== -1) { @@ -906,13 +906,13 @@ var AMDLoader; } return result; }; - ModuleIdResolver.ROOT = new ModuleIdResolver(''); return ModuleIdResolver; }()); + ModuleIdResolver.ROOT = new ModuleIdResolver(''); AMDLoader.ModuleIdResolver = ModuleIdResolver; // ------------------------------------------------------------------------ // Module - var Module = /** @class */ (function () { + var Module = (function () { function Module(id, strId, dependencies, callback, errorback, moduleIdResolver) { this.id = id; this.strId = strId; @@ -1002,7 +1002,7 @@ var AMDLoader; return Module; }()); AMDLoader.Module = Module; - var ModuleIdProvider = /** @class */ (function () { + var ModuleIdProvider = (function () { function ModuleIdProvider() { this._nextId = 0; this._strModuleIdToIntModuleId = new Map(); @@ -1029,17 +1029,17 @@ var AMDLoader; }; return ModuleIdProvider; }()); - var RegularDependency = /** @class */ (function () { + var RegularDependency = (function () { function RegularDependency(id) { this.id = id; } - RegularDependency.EXPORTS = new RegularDependency(0 /* EXPORTS */); - RegularDependency.MODULE = new RegularDependency(1 /* MODULE */); - RegularDependency.REQUIRE = new RegularDependency(2 /* REQUIRE */); return RegularDependency; }()); + RegularDependency.EXPORTS = new RegularDependency(0 /* EXPORTS */); + RegularDependency.MODULE = new RegularDependency(1 /* MODULE */); + RegularDependency.REQUIRE = new RegularDependency(2 /* REQUIRE */); AMDLoader.RegularDependency = RegularDependency; - var PluginDependency = /** @class */ (function () { + var PluginDependency = (function () { function PluginDependency(id, pluginId, pluginParam) { this.id = id; this.pluginId = pluginId; @@ -1048,7 +1048,7 @@ var AMDLoader; return PluginDependency; }()); AMDLoader.PluginDependency = PluginDependency; - var ModuleManager = /** @class */ (function () { + var ModuleManager = (function () { function ModuleManager(env, scriptLoader, defineFunc, requireFunc, loaderAvailableTimestamp) { if (loaderAvailableTimestamp === void 0) { loaderAvailableTimestamp = 0; } this._env = env; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 438105b4d89..a7154a7bd85 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -25,13 +25,6 @@ declare namespace monaco { dispose(): void; } - export enum Severity { - Ignore = 0, - Info = 1, - Warning = 2, - Error = 3, - } - export enum MarkerTag { Unnecessary = 1, } @@ -48,26 +41,21 @@ declare namespace monaco { export type TValueCallback<T = any> = (value: T | PromiseLike<T>) => void; - export type ProgressCallback<TProgress = any> = (progress: TProgress) => void; - - export class Promise<T = any, TProgress = any> { + export class Promise<T = any> { constructor( executor: ( resolve: (value: T | PromiseLike<T>) => void, - reject: (reason: any) => void, - progress: (progress: TProgress) => void) => void, + reject: (reason: any) => void) => void, oncancel?: () => void); public then<TResult1 = T, TResult2 = never>( onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null, - onprogress?: (progress: TProgress) => void): Promise<TResult1 | TResult2, TProgress>; + onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>; public done( onfulfilled?: (value: T) => void, - onrejected?: (reason: any) => void, - onprogress?: (progress: TProgress) => void): void; + onrejected?: (reason: any) => void): void; public cancel(): void; @@ -77,8 +65,6 @@ declare namespace monaco { public static as<T, SomePromise extends PromiseLike<T>>(value: SomePromise): SomePromise; public static as<T>(value: T): Promise<T>; - public static is(value: any): value is PromiseLike<any>; - public static timeout(delay: number): Promise<void>; public static join<T1, T2>(promises: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>; @@ -107,7 +93,7 @@ declare namespace monaco { } /** * Uniform Resource Identifier (Uri) http://tools.ietf.org/html/rfc3986. - * This class is a simple parser which creates the basic component paths + * This class is a simple parser which creates the basic component parts * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation * and encoding. * @@ -118,8 +104,6 @@ declare namespace monaco { * | _____________________|__ * / \ / \ * urn:example:animal:ferret:nose - * - * */ export class Uri implements UriComponents { static isUri(thing: any): thing is Uri; @@ -147,28 +131,80 @@ declare namespace monaco { readonly fragment: string; /** * Returns a string representing the corresponding file system path of this Uri. - * Will handle UNC paths and normalize windows drive letters to lower-case. Also - * uses the platform specific path separator. Will *not* validate the path for - * invalid characters and semantics. Will *not* look at the scheme of this Uri. + * Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the + * platform specific path separator. + * + * * Will *not* validate the path for invalid characters and semantics. + * * Will *not* look at the scheme of this Uri. + * * The result shall *not* be used for display purposes but for accessing a file on disk. + * + * + * The *difference* to `Uri#path` is the use of the platform specific separator and the handling + * of UNC paths. See the below sample of a file-uri with an authority (UNC path). + * + * ```ts + const u = Uri.parse('file://server/c$/folder/file.txt') + u.authority === 'server' + u.path === '/shares/c$/file.txt' + u.fsPath === '\\server\c$\folder\file.txt' + ``` + * + * Using `Uri#path` to read a file (using fs-apis) would not be enough because parts of the path, + * namely the server name, would be missing. Therefore `Uri#fsPath` exists - it's sugar to ease working + * with URIs that represent files on disk (`file` scheme). */ readonly fsPath: string; with(change: { scheme?: string; - authority?: string; - path?: string; - query?: string; - fragment?: string; + authority?: string | null; + path?: string | null; + query?: string | null; + fragment?: string | null; }): Uri; + /** + * Creates a new Uri from a string, e.g. `http://www.msft.com/some/path`, + * `file:///usr/home`, or `scheme:with/path`. + * + * @param value A string which represents an Uri (see `Uri#toString`). + */ static parse(value: string): Uri; + /** + * Creates a new Uri from a file system path, e.g. `c:\my\files`, + * `/usr/home`, or `\\server\share\some\path`. + * + * The *difference* between `Uri#parse` and `Uri#file` is that the latter treats the argument + * as path, not as stringified-uri. E.g. `Uri.file(path)` is **not the same as** + * `Uri.parse('file://' + path)` because the path might contain characters that are + * interpreted (# and ?). See the following sample: + * ```ts + const good = Uri.file('/coding/c#/project1'); + good.scheme === 'file'; + good.path === '/coding/c#/project1'; + good.fragment === ''; + const bad = Uri.parse('file://' + '/coding/c#/project1'); + bad.scheme === 'file'; + bad.path === '/coding/c'; // path is now broken + bad.fragment === '/project1'; + ``` + * + * @param path A file system path (see `Uri#fsPath`) + */ static file(path: string): Uri; static from(components: { - scheme?: string; + scheme: string; authority?: string; path?: string; query?: string; fragment?: string; }): Uri; /** + * Creates a string presentation for this Uri. It's guardeed that calling + * `Uri.parse` with the result of this function creates an Uri which is equal + * to this Uri. + * + * * The result shall *not* be used for display purposes but for externalization or transport. + * * The result will be encoded using the percentage encoding and encoding happens mostly + * ignore the scheme-specific encoding rules. * * @param skipEncoding Do not encode the result, default is `false` */ @@ -1014,7 +1050,7 @@ declare namespace monaco.editor { /** * The initial model associated with this code editor. */ - model?: ITextModel; + model?: ITextModel | null; /** * The initial value of the auto created model in the editor. * To not create automatically a model, use `model: null`. @@ -1629,14 +1665,12 @@ declare namespace monaco.editor { /** * Get the word under or besides `position`. * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Might be null. */ getWordAtPosition(position: IPosition): IWordAtPosition; /** * Get the word under or besides `position` trimmed to `position`.column * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Will never be null. */ getWordUntilPosition(position: IPosition): IWordAtPosition; @@ -1647,14 +1681,12 @@ declare namespace monaco.editor { /** * Get the word under or besides `position`. * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Might be null. */ getWordAtPosition(position: IPosition): IWordAtPosition; /** * Get the word under or besides `position` trimmed to `position`.column * @param position The position to look for a word. - * @param skipSyntaxTokens Ignore syntax tokens, as identified by the mode. * @return The word under or besides `position`. Will never be null. */ getWordUntilPosition(position: IPosition): IWordAtPosition; @@ -2153,7 +2185,7 @@ declare namespace monaco.editor { /** * Gets the current model attached to this editor. */ - getModel(): IEditorModel; + getModel(): IEditorModel | null; /** * Sets the current model attached to this editor. * If the previous model was created by the editor via the value key in the options @@ -2162,7 +2194,7 @@ declare namespace monaco.editor { * will not be destroyed. * It is safe to call setModel(null) to simply detach the current model from the editor. */ - setModel(model: IEditorModel): void; + setModel(model: IEditorModel | null): void; } /** @@ -2445,6 +2477,16 @@ declare namespace monaco.editor { autoFindInSelection: boolean; } + /** + * Configuration options for auto closing quotes and brackets + */ + export type EditorAutoClosingStrategy = 'always' | 'languageDefined' | 'beforeWhitespace' | 'never'; + + /** + * Configuration options for auto wrapping quotes and brackets + */ + export type EditorAutoWrappingStrategy = 'always' | 'quotes' | 'brackets' | 'never'; + /** * Configuration options for editor minimap */ @@ -2508,6 +2550,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,19 +2851,29 @@ declare namespace monaco.editor { */ quickSuggestionsDelay?: number; /** - * Enables parameter hints + * Parameter hint options. */ - parameterHints?: boolean; + parameterHints?: IEditorParameterHintOptions; /** * Render icons in suggestions box. * Defaults to true. */ iconsInSuggestions?: boolean; /** - * Enable auto closing brackets. - * Defaults to true. + * Options for auto closing brackets. + * Defaults to language defined behavior. */ - autoClosingBrackets?: boolean; + autoClosingBrackets?: EditorAutoClosingStrategy; + /** + * Options for auto closing quotes. + * Defaults to language defined behavior. + */ + autoClosingQuotes?: EditorAutoClosingStrategy; + /** + * Options for autowrapping. + * Defaults to always allowing autowrapping. + */ + autoWrapping?: EditorAutoWrappingStrategy; /** * Enable auto indentation adjustment. * Defaults to false. @@ -3130,6 +3198,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 +3267,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; @@ -3234,7 +3307,9 @@ declare namespace monaco.editor { readonly multiCursorMergeOverlapping: boolean; readonly showUnused: boolean; readonly wordSeparators: string; - readonly autoClosingBrackets: boolean; + readonly autoClosingBrackets: EditorAutoClosingStrategy; + readonly autoClosingQuotes: EditorAutoClosingStrategy; + readonly autoWrapping: EditorAutoWrappingStrategy; readonly autoIndent: boolean; readonly useTabStops: boolean; readonly tabFocusMode: boolean; @@ -3373,6 +3448,8 @@ declare namespace monaco.editor { readonly multiCursorMergeOverlapping: boolean; readonly wordSeparators: boolean; readonly autoClosingBrackets: boolean; + readonly autoClosingQuotes: boolean; + readonly autoWrapping: boolean; readonly autoIndent: boolean; readonly useTabStops: boolean; readonly tabFocusMode: boolean; @@ -3736,6 +3813,14 @@ declare namespace monaco.editor { * @event */ onDidBlurEditorWidget(listener: () => void): IDisposable; + /** + * An event emitted after composition has started. + */ + onCompositionStart(listener: () => void): IDisposable; + /** + * An event emitted after composition has ended. + */ + onCompositionEnd(listener: () => void): IDisposable; /** * An event emitted on a "mouseup". * @event @@ -4036,6 +4121,7 @@ declare namespace monaco.editor { readonly isMonospace: boolean; readonly typicalHalfwidthCharacterWidth: number; readonly typicalFullwidthCharacterWidth: number; + readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; readonly maxDigitWidth: number; } @@ -4529,6 +4615,12 @@ declare namespace monaco.languages { * settings will be used. */ surroundingPairs?: IAutoClosingPair[]; + /** + * Defines what characters must be after the cursor for bracket or quote autoclosing to occur when using the \'languageDefined\' autoclosing setting. + * + * This is typically the set of characters which can not start an expression, such as whitespace, closing brackets, non-unary operators, etc. + */ + autoCloseBefore?: string; /** * The language's folding rules. */ @@ -5166,11 +5258,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; /** @@ -5244,76 +5336,6 @@ declare namespace monaco.languages { arguments?: any[]; } - export interface CommentInfo { - owner: number; - threads: CommentThread[]; - commentingRanges?: IRange[]; - reply?: Command; - } - - export enum CommentThreadCollapsibleState { - /** - * Determines an item is collapsed - */ - Collapsed = 0, - /** - * Determines an item is expanded - */ - Expanded = 1 - } - - export interface CommentThread { - threadId: string; - resource: string; - range: IRange; - comments: Comment[]; - collapsibleState?: CommentThreadCollapsibleState; - reply?: Command; - } - - export interface NewCommentAction { - ranges: IRange[]; - actions: Command[]; - } - - export interface Comment { - readonly commentId: string; - readonly body: IMarkdownString; - readonly userName: string; - readonly gravatar: string; - readonly command?: Command; - } - - export interface CommentThreadChangedEvent { - readonly owner: number; - /** - * Added comment threads. - */ - readonly added: CommentThread[]; - /** - * Removed comment threads. - */ - readonly removed: CommentThread[]; - /** - * Changed comment threads. - */ - readonly changed: CommentThread[]; - } - - export interface DocumentCommentProvider { - provideDocumentComments(resource: Uri, token: CancellationToken): Promise<CommentInfo>; - createNewCommentThread(resource: Uri, range: Range, text: string, token: CancellationToken): Promise<CommentThread>; - replyToCommentThread(resource: Uri, range: Range, thread: CommentThread, text: string, token: CancellationToken): Promise<CommentThread>; - onDidChangeCommentThreads(): IEvent<CommentThreadChangedEvent>; - } - - export interface WorkspaceCommentProvider { - provideWorkspaceComments(token: CancellationToken): Promise<CommentThread[]>; - createNewCommentThread(resource: Uri, range: Range, text: string, token: CancellationToken): Promise<CommentThread>; - replyToCommentThread(resource: Uri, range: Range, thread: CommentThread, text: string, token: CancellationToken): Promise<CommentThread>; - onDidChangeCommentThreads(): IEvent<CommentThreadChangedEvent>; - } - export interface ICodeLensSymbol { range: IRange; id?: string; diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuItemActionItem.ts index a8ff208c0a6..85d70509ab6 100644 --- a/src/vs/platform/actions/browser/menuItemActionItem.ts +++ b/src/vs/platform/actions/browser/menuItemActionItem.ts @@ -9,13 +9,13 @@ import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IMenu, MenuItemAction, IMenuActionOptions, ICommandAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { domEvent } from 'vs/base/browser/event'; import { Emitter } from 'vs/base/common/event'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IdGenerator } from 'vs/base/common/idGenerator'; -import { createCSSRule } from 'vs/base/browser/dom'; +import { createCSSRule, addClasses, removeClasses } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isWindows, isLinux } from 'vs/base/common/platform'; @@ -199,17 +199,17 @@ export class MenuItemActionItem extends ActionItem { } }; - this._callOnDispose.push(alternativeKeyEmitter.event(value => { + this._register(alternativeKeyEmitter.event(value => { alternativeKeyDown = value; updateAltState(); })); - this._callOnDispose.push(domEvent(container, 'mouseleave')(_ => { + this._register(domEvent(container, 'mouseleave')(_ => { mouseOver = false; updateAltState(); })); - this._callOnDispose.push(domEvent(container, 'mouseenter')(e => { + this._register(domEvent(container, 'mouseenter')(e => { mouseOver = true; updateAltState(); })); @@ -217,12 +217,12 @@ export class MenuItemActionItem extends ActionItem { _updateLabel(): void { if (this.options.label) { - this.$e.text(this._commandAction.label); + this.label.textContent = this._commandAction.label; } } _updateTooltip(): void { - const element = this.$e.getHTMLElement(); + const element = this.label; const keybinding = this._keybindingService.lookupKeybinding(this._commandAction.id); const keybindingLabel = keybinding && keybinding.getLabel(); @@ -259,8 +259,8 @@ export class MenuItemActionItem extends ActionItem { MenuItemActionItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); } - this.$e.getHTMLElement().classList.add('icon', iconClass); - this._itemClassDispose = { dispose: () => this.$e.getHTMLElement().classList.remove('icon', iconClass) }; + addClasses(this.label, 'icon', iconClass); + this._itemClassDispose = toDisposable(() => removeClasses(this.label, 'icon', iconClass)); } } diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index e919252f47a..e3095b53b9b 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -93,9 +93,10 @@ 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 MenubarTasksMenu = new MenuId(); - static readonly MenubarWindowMenu = new MenuId(); + static readonly MenubarNewBreakpointMenu = new MenuId(); static readonly MenubarPreferencesMenu = new MenuId(); static readonly MenubarHelpMenu = new MenuId(); static readonly MenubarTerminalMenu = new MenuId(); diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 8c879b66d8b..278490fc6fe 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -121,7 +121,7 @@ export class BackupMainService implements IBackupMainService { } public registerFolderBackupSync(folderUri: URI): string { - if (!this.folderWorkspaces.some(uri => areResourcesEquals(folderUri, uri, hasToIgnoreCase(folderUri)))) { + if (!this.folderWorkspaces.some(uri => areResourcesEquals(folderUri, uri))) { this.folderWorkspaces.push(folderUri); this.saveSync(); } @@ -129,7 +129,7 @@ export class BackupMainService implements IBackupMainService { } public unregisterFolderBackupSync(folderUri: URI): void { - let index = arrays.firstIndex(this.folderWorkspaces, uri => areResourcesEquals(folderUri, uri, hasToIgnoreCase(folderUri))); + let index = arrays.firstIndex(this.folderWorkspaces, uri => areResourcesEquals(folderUri, uri)); if (index !== -1) { this.folderWorkspaces.splice(index, 1); this.saveSync(); diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index b5f23e0b9f3..b912a022dfc 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -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; } @@ -99,6 +100,12 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR }); } + registerCommandAlias(oldId: string, newId: string): IDisposable { + return CommandsRegistry.registerCommand(oldId, (accessor, ...args) => { + accessor.get(ICommandService).executeCommand(newId, ...args); + }); + } + getCommand(id: string): ICommand { const list = this._commands.get(id); if (!list || list.isEmpty()) { 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..b39ec136593 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -6,8 +6,7 @@ 'use strict'; import 'vs/css!./contextMenuHandler'; -import { $, Builder } from 'vs/base/browser/builder'; -import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ActionRunner, IAction, IRunEvent } from 'vs/base/common/actions'; import { Menu } from 'vs/base/browser/ui/menu/menu'; @@ -16,6 +15,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; +import { addDisposableListener } from 'vs/base/browser/dom'; export class ContextMenuHandler { @@ -23,8 +23,10 @@ export class ContextMenuHandler { private notificationService: INotificationService; private telemetryService: ITelemetryService; - private $el: Builder; + private element: HTMLElement; + private elementDisposable: IDisposable; private menuContainerElement: HTMLElement; + private focusToReturn: HTMLElement; constructor(element: HTMLElement, contextViewService: IContextViewService, telemetryService: ITelemetryService, notificationService: INotificationService) { this.setContainer(element); @@ -37,13 +39,13 @@ export class ContextMenuHandler { } public setContainer(container: HTMLElement): void { - if (this.$el) { - this.$el.off(['click', 'mousedown']); - this.$el = null; + if (this.element) { + this.elementDisposable = dispose(this.elementDisposable); + this.element = null; } if (container) { - this.$el = $(container); - this.$el.on('mousedown', (e: Event) => this.onMouseDown(e as MouseEvent)); + this.element = container; + this.elementDisposable = addDisposableListener(this.element, 'mousedown', (e) => this.onMouseDown(e as MouseEvent)); } } @@ -53,6 +55,8 @@ export class ContextMenuHandler { return; // Don't render an empty context menu } + this.focusToReturn = document.activeElement as HTMLElement; + this.contextViewService.showContextView({ getAnchor: () => delegate.getAnchor(), canRelayout: false, @@ -82,7 +86,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]); }, @@ -110,6 +114,11 @@ export class ContextMenuHandler { } this.contextViewService.hideContextView(false); + + // Restore focus here + if (this.focusToReturn) { + this.focusToReturn.focus(); + } } private onDidActionRun(e: IRunEvent): void { diff --git a/src/vs/platform/dialogs/common/dialogIpc.ts b/src/vs/platform/dialogs/node/dialogIpc.ts similarity index 96% rename from src/vs/platform/dialogs/common/dialogIpc.ts rename to src/vs/platform/dialogs/node/dialogIpc.ts index 0a73419d531..06a2376ea0c 100644 --- a/src/vs/platform/dialogs/common/dialogIpc.ts +++ b/src/vs/platform/dialogs/node/dialogIpc.ts @@ -6,7 +6,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/download/common/download.ts b/src/vs/platform/download/common/download.ts new file mode 100644 index 00000000000..80a9de7cbc0 --- /dev/null +++ b/src/vs/platform/download/common/download.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export const IDownloadService = createDecorator<IDownloadService>('downloadService'); + +export interface IDownloadService { + + _serviceBrand: any; + + download(location: URI, file: string): TPromise<void>; + +} \ No newline at end of file diff --git a/src/vs/platform/download/node/downloadIpc.ts b/src/vs/platform/download/node/downloadIpc.ts new file mode 100644 index 00000000000..606fdc05dc3 --- /dev/null +++ b/src/vs/platform/download/node/downloadIpc.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import * as path from 'path'; +import * as fs from 'fs'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { Event, Emitter, buffer } from 'vs/base/common/event'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { mkdirp } from 'vs/base/node/pfs'; +import { IURITransformer } from 'vs/base/common/uriIpc'; + +export type UploadResponse = Buffer | string | undefined; + +export function upload(uri: URI): Event<UploadResponse> { + const stream = new Emitter<UploadResponse>(); + const readstream = fs.createReadStream(uri.fsPath); + readstream.on('data', data => stream.fire(data)); + readstream.on('error', error => stream.fire(error.toString())); + readstream.on('close', () => stream.fire()); + return stream.event; +} + +export interface IDownloadServiceChannel extends IChannel { + listen(event: 'upload', uri: URI): Event<UploadResponse>; + listen(event: string, arg?: any): Event<any>; +} + +export class DownloadServiceChannel implements IDownloadServiceChannel { + + constructor() { } + + listen(event: string, arg?: any): Event<any> { + switch (event) { + case 'upload': return buffer(upload(URI.revive(arg))); + } + return undefined; + } + + call(command: string, arg?: any): TPromise<any> { + throw new Error('No calls'); + } +} + +export class DownloadServiceChannelClient implements IDownloadService { + + _serviceBrand: any; + + constructor(private channel: IDownloadServiceChannel, private uriTransformer: IURITransformer) { } + + download(from: URI, to: string): TPromise<void> { + from = this.uriTransformer.transformOutgoing(from); + const dirName = path.dirname(to); + let out: fs.WriteStream; + return new TPromise((c, e) => { + return mkdirp(dirName) + .then(() => { + out = fs.createWriteStream(to); + out.once('close', () => c(null)); + out.once('error', e); + const uploadStream = this.channel.listen('upload', from); + const disposable = uploadStream((result: UploadResponse) => { + if (result === void 0) { + out.end(); + disposable.dispose(); + c(null); + } else if (Buffer.isBuffer(result)) { + out.write(result); + } else if (typeof result === 'string') { + out.close(); + disposable.dispose(); + e(result); + } + }); + }); + }); + } +} \ No newline at end of file diff --git a/src/vs/platform/driver/common/driver.ts b/src/vs/platform/driver/common/driver.ts deleted file mode 100644 index 82e907f2e25..00000000000 --- a/src/vs/platform/driver/common/driver.ts +++ /dev/null @@ -1,292 +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 { TPromise } from 'vs/base/common/winjs.base'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event } from 'vs/base/common/event'; - -export const ID = 'driverService'; -export const IDriver = createDecorator<IDriver>(ID); - -// !! Do not remove the following START and END markers, they are parsed by the smoketest build - -//*START -export interface IElement { - tagName: string; - className: string; - textContent: string; - attributes: { [name: string]: string; }; - children: IElement[]; - top: number; - left: number; -} - -export interface IDriver { - _serviceBrand: any; - - getWindowIds(): TPromise<number[]>; - capturePage(windowId: number): TPromise<string>; - reloadWindow(windowId: number): TPromise<void>; - dispatchKeybinding(windowId: number, keybinding: string): TPromise<void>; - click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): TPromise<void>; - doubleClick(windowId: number, selector: string): TPromise<void>; - setValue(windowId: number, selector: string, text: string): TPromise<void>; - getTitle(windowId: number): TPromise<string>; - isActiveElement(windowId: number, selector: string): TPromise<boolean>; - getElements(windowId: number, selector: string, recursive?: boolean): TPromise<IElement[]>; - typeInEditor(windowId: number, selector: string, text: string): TPromise<void>; - getTerminalBuffer(windowId: number, selector: string): TPromise<string[]>; - writeInTerminal(windowId: number, selector: string, text: string): TPromise<void>; -} -//*END - -export interface IDriverChannel extends IChannel { - call(command: 'getWindowIds'): TPromise<number[]>; - call(command: 'capturePage'): TPromise<string>; - call(command: 'reloadWindow', arg: number): TPromise<void>; - call(command: 'dispatchKeybinding', arg: [number, string]): TPromise<void>; - call(command: 'click', arg: [number, string, number | undefined, number | undefined]): TPromise<void>; - call(command: 'doubleClick', arg: [number, string]): TPromise<void>; - call(command: 'setValue', arg: [number, string, string]): TPromise<void>; - call(command: 'getTitle', arg: [number]): TPromise<string>; - call(command: 'isActiveElement', arg: [number, string]): TPromise<boolean>; - call(command: 'getElements', arg: [number, string, boolean]): TPromise<IElement[]>; - call(command: 'typeInEditor', arg: [number, string, string]): TPromise<void>; - call(command: 'getTerminalBuffer', arg: [number, string]): TPromise<string[]>; - call(command: 'writeInTerminal', arg: [number, string, string]): TPromise<void>; - call(command: string, arg: any): TPromise<any>; -} - -export class DriverChannel implements IDriverChannel { - - constructor(private driver: IDriver) { } - - listen<T>(event: string): Event<T> { - throw new Error('No event found'); - } - - call(command: string, arg?: any): TPromise<any> { - switch (command) { - case 'getWindowIds': return this.driver.getWindowIds(); - case 'capturePage': return this.driver.capturePage(arg); - case 'reloadWindow': return this.driver.reloadWindow(arg); - case 'dispatchKeybinding': return this.driver.dispatchKeybinding(arg[0], arg[1]); - case 'click': return this.driver.click(arg[0], arg[1], arg[2], arg[3]); - case 'doubleClick': return this.driver.doubleClick(arg[0], arg[1]); - case 'setValue': return this.driver.setValue(arg[0], arg[1], arg[2]); - case 'getTitle': return this.driver.getTitle(arg[0]); - case 'isActiveElement': return this.driver.isActiveElement(arg[0], arg[1]); - case 'getElements': return this.driver.getElements(arg[0], arg[1], arg[2]); - case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1], arg[2]); - case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg[0], arg[1]); - case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]); - } - - return undefined; - } -} - -export class DriverChannelClient implements IDriver { - - _serviceBrand: any; - - constructor(private channel: IDriverChannel) { } - - getWindowIds(): TPromise<number[]> { - return this.channel.call('getWindowIds'); - } - - capturePage(windowId: number): TPromise<string> { - return this.channel.call('capturePage', windowId); - } - - reloadWindow(windowId: number): TPromise<void> { - return this.channel.call('reloadWindow', windowId); - } - - dispatchKeybinding(windowId: number, keybinding: string): TPromise<void> { - return this.channel.call('dispatchKeybinding', [windowId, keybinding]); - } - - click(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): TPromise<void> { - return this.channel.call('click', [windowId, selector, xoffset, yoffset]); - } - - doubleClick(windowId: number, selector: string): TPromise<void> { - return this.channel.call('doubleClick', [windowId, selector]); - } - - setValue(windowId: number, selector: string, text: string): TPromise<void> { - return this.channel.call('setValue', [windowId, selector, text]); - } - - getTitle(windowId: number): TPromise<string> { - return this.channel.call('getTitle', [windowId]); - } - - isActiveElement(windowId: number, selector: string): TPromise<boolean> { - return this.channel.call('isActiveElement', [windowId, selector]); - } - - getElements(windowId: number, selector: string, recursive: boolean): TPromise<IElement[]> { - return this.channel.call('getElements', [windowId, selector, recursive]); - } - - typeInEditor(windowId: number, selector: string, text: string): TPromise<void> { - return this.channel.call('typeInEditor', [windowId, selector, text]); - } - - getTerminalBuffer(windowId: number, selector: string): TPromise<string[]> { - return this.channel.call('getTerminalBuffer', [windowId, selector]); - } - - writeInTerminal(windowId: number, selector: string, text: string): TPromise<void> { - return this.channel.call('writeInTerminal', [windowId, selector, text]); - } -} - -export interface IDriverOptions { - verbose: boolean; -} - -export interface IWindowDriverRegistry { - registerWindowDriver(windowId: number): TPromise<IDriverOptions>; - reloadWindowDriver(windowId: number): TPromise<void>; -} - -export interface IWindowDriverRegistryChannel extends IChannel { - call(command: 'registerWindowDriver', arg: number): TPromise<IDriverOptions>; - call(command: 'reloadWindowDriver', arg: number): TPromise<void>; - call(command: string, arg: any): TPromise<any>; -} - -export class WindowDriverRegistryChannel implements IWindowDriverRegistryChannel { - - constructor(private registry: IWindowDriverRegistry) { } - - listen<T>(event: string): Event<T> { - throw new Error('No event found'); - } - - call(command: string, arg?: any): TPromise<any> { - switch (command) { - case 'registerWindowDriver': return this.registry.registerWindowDriver(arg); - case 'reloadWindowDriver': return this.registry.reloadWindowDriver(arg); - } - - return undefined; - } -} - -export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry { - - _serviceBrand: any; - - constructor(private channel: IWindowDriverRegistryChannel) { } - - registerWindowDriver(windowId: number): TPromise<IDriverOptions> { - return this.channel.call('registerWindowDriver', windowId); - } - - reloadWindowDriver(windowId: number): TPromise<void> { - return this.channel.call('reloadWindowDriver', windowId); - } -} - -export interface IWindowDriver { - click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): TPromise<void>; - doubleClick(selector: string): TPromise<void>; - setValue(selector: string, text: string): TPromise<void>; - getTitle(): TPromise<string>; - isActiveElement(selector: string): TPromise<boolean>; - getElements(selector: string, recursive: boolean): TPromise<IElement[]>; - typeInEditor(selector: string, text: string): TPromise<void>; - getTerminalBuffer(selector: string): TPromise<string[]>; - writeInTerminal(selector: string, text: string): TPromise<void>; -} - -export interface IWindowDriverChannel extends IChannel { - call(command: 'click', arg: [string, number | undefined, number | undefined]): TPromise<void>; - call(command: 'doubleClick', arg: string): TPromise<void>; - call(command: 'setValue', arg: [string, string]): TPromise<void>; - call(command: 'getTitle'): TPromise<string>; - call(command: 'isActiveElement', arg: string): TPromise<boolean>; - call(command: 'getElements', arg: [string, boolean]): TPromise<IElement[]>; - call(command: 'typeInEditor', arg: [string, string]): TPromise<void>; - call(command: 'getTerminalBuffer', arg: string): TPromise<string[]>; - call(command: 'writeInTerminal', arg: [string, string]): TPromise<void>; - call(command: string, arg: any): TPromise<any>; -} - -export class WindowDriverChannel implements IWindowDriverChannel { - - constructor(private driver: IWindowDriver) { } - - listen<T>(event: string): Event<T> { - throw new Error('No event found'); - } - - call(command: string, arg?: any): TPromise<any> { - switch (command) { - case 'click': return this.driver.click(arg[0], arg[1], arg[2]); - case 'doubleClick': return this.driver.doubleClick(arg); - case 'setValue': return this.driver.setValue(arg[0], arg[1]); - case 'getTitle': return this.driver.getTitle(); - case 'isActiveElement': return this.driver.isActiveElement(arg); - case 'getElements': return this.driver.getElements(arg[0], arg[1]); - case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]); - case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg); - case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]); - } - - return undefined; - } -} - -export class WindowDriverChannelClient implements IWindowDriver { - - _serviceBrand: any; - - constructor(private channel: IWindowDriverChannel) { } - - click(selector: string, xoffset?: number, yoffset?: number): TPromise<void> { - return this.channel.call('click', [selector, xoffset, yoffset]); - } - - doubleClick(selector: string): TPromise<void> { - return this.channel.call('doubleClick', selector); - } - - setValue(selector: string, text: string): TPromise<void> { - return this.channel.call('setValue', [selector, text]); - } - - getTitle(): TPromise<string> { - return this.channel.call('getTitle'); - } - - isActiveElement(selector: string): TPromise<boolean> { - return this.channel.call('isActiveElement', selector); - } - - getElements(selector: string, recursive: boolean): TPromise<IElement[]> { - return this.channel.call('getElements', [selector, recursive]); - } - - typeInEditor(selector: string, text: string): TPromise<void> { - return this.channel.call('typeInEditor', [selector, text]); - } - - getTerminalBuffer(selector: string): TPromise<string[]> { - return this.channel.call('getTerminalBuffer', selector); - } - - writeInTerminal(selector: string, text: string): TPromise<void> { - return this.channel.call('writeInTerminal', [selector, text]); - } -} \ No newline at end of file diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index 4dbaaff6c95..a2305e2d393 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -7,8 +7,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { IWindowDriver, IElement, WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/common/driver'; -import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { IWindowDriver, IElement, WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/node/driver'; +import { IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom'; import * as electron from 'electron'; @@ -86,7 +86,7 @@ class WindowDriver implements IWindowDriver { private _click(selector: string, clickCount: number, xoffset?: number, yoffset?: number): TPromise<void> { return this._getElementXY(selector, xoffset, yoffset).then(({ x, y }) => { - const webContents = electron.remote.getCurrentWebContents(); + const webContents: electron.WebContents = (electron as any).remote.getCurrentWebContents(); webContents.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any); return TPromise.timeout(10).then(() => { @@ -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/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index f9667d4d695..a17e1d9ac75 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -6,12 +6,12 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IDriver, DriverChannel, IElement, IWindowDriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/common/driver'; +import { IDriver, DriverChannel, IElement, IWindowDriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/node/driver'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net'; import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IPCServer, IClientRouter } from 'vs/base/parts/ipc/common/ipc'; +import { IPCServer, IClientRouter } from 'vs/base/parts/ipc/node/ipc'; import { SimpleKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { OS } from 'vs/base/common/platform'; diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts index 1f5df95b0c7..5e1da358de0 100644 --- a/src/vs/platform/driver/node/driver.ts +++ b/src/vs/platform/driver/node/driver.ts @@ -5,8 +5,292 @@ 'use strict'; -import { IDriver, DriverChannelClient } from 'vs/platform/driver/common/driver'; import { connect as connectNet, Client } from 'vs/base/parts/ipc/node/ipc.net'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { Event } from 'vs/base/common/event'; + +export const ID = 'driverService'; +export const IDriver = createDecorator<IDriver>(ID); + +// !! Do not remove the following START and END markers, they are parsed by the smoketest build + +//*START +export interface IElement { + tagName: string; + className: string; + textContent: string; + attributes: { [name: string]: string; }; + children: IElement[]; + top: number; + left: number; +} + +export interface IDriver { + _serviceBrand: any; + + getWindowIds(): TPromise<number[]>; + capturePage(windowId: number): TPromise<string>; + reloadWindow(windowId: number): TPromise<void>; + dispatchKeybinding(windowId: number, keybinding: string): TPromise<void>; + click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): TPromise<void>; + doubleClick(windowId: number, selector: string): TPromise<void>; + setValue(windowId: number, selector: string, text: string): TPromise<void>; + getTitle(windowId: number): TPromise<string>; + isActiveElement(windowId: number, selector: string): TPromise<boolean>; + getElements(windowId: number, selector: string, recursive?: boolean): TPromise<IElement[]>; + typeInEditor(windowId: number, selector: string, text: string): TPromise<void>; + getTerminalBuffer(windowId: number, selector: string): TPromise<string[]>; + writeInTerminal(windowId: number, selector: string, text: string): TPromise<void>; +} +//*END + +export interface IDriverChannel extends IChannel { + call(command: 'getWindowIds'): TPromise<number[]>; + call(command: 'capturePage'): TPromise<string>; + call(command: 'reloadWindow', arg: number): TPromise<void>; + call(command: 'dispatchKeybinding', arg: [number, string]): TPromise<void>; + call(command: 'click', arg: [number, string, number | undefined, number | undefined]): TPromise<void>; + call(command: 'doubleClick', arg: [number, string]): TPromise<void>; + call(command: 'setValue', arg: [number, string, string]): TPromise<void>; + call(command: 'getTitle', arg: [number]): TPromise<string>; + call(command: 'isActiveElement', arg: [number, string]): TPromise<boolean>; + call(command: 'getElements', arg: [number, string, boolean]): TPromise<IElement[]>; + call(command: 'typeInEditor', arg: [number, string, string]): TPromise<void>; + call(command: 'getTerminalBuffer', arg: [number, string]): TPromise<string[]>; + call(command: 'writeInTerminal', arg: [number, string, string]): TPromise<void>; + call(command: string, arg: any): TPromise<any>; +} + +export class DriverChannel implements IDriverChannel { + + constructor(private driver: IDriver) { } + + listen<T>(event: string): Event<T> { + throw new Error('No event found'); + } + + call(command: string, arg?: any): TPromise<any> { + switch (command) { + case 'getWindowIds': return this.driver.getWindowIds(); + case 'capturePage': return this.driver.capturePage(arg); + case 'reloadWindow': return this.driver.reloadWindow(arg); + case 'dispatchKeybinding': return this.driver.dispatchKeybinding(arg[0], arg[1]); + case 'click': return this.driver.click(arg[0], arg[1], arg[2], arg[3]); + case 'doubleClick': return this.driver.doubleClick(arg[0], arg[1]); + case 'setValue': return this.driver.setValue(arg[0], arg[1], arg[2]); + case 'getTitle': return this.driver.getTitle(arg[0]); + case 'isActiveElement': return this.driver.isActiveElement(arg[0], arg[1]); + case 'getElements': return this.driver.getElements(arg[0], arg[1], arg[2]); + case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1], arg[2]); + case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg[0], arg[1]); + case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]); + } + + return undefined; + } +} + +export class DriverChannelClient implements IDriver { + + _serviceBrand: any; + + constructor(private channel: IDriverChannel) { } + + getWindowIds(): TPromise<number[]> { + return this.channel.call('getWindowIds'); + } + + capturePage(windowId: number): TPromise<string> { + return this.channel.call('capturePage', windowId); + } + + reloadWindow(windowId: number): TPromise<void> { + return this.channel.call('reloadWindow', windowId); + } + + dispatchKeybinding(windowId: number, keybinding: string): TPromise<void> { + return this.channel.call('dispatchKeybinding', [windowId, keybinding]); + } + + click(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): TPromise<void> { + return this.channel.call('click', [windowId, selector, xoffset, yoffset]); + } + + doubleClick(windowId: number, selector: string): TPromise<void> { + return this.channel.call('doubleClick', [windowId, selector]); + } + + setValue(windowId: number, selector: string, text: string): TPromise<void> { + return this.channel.call('setValue', [windowId, selector, text]); + } + + getTitle(windowId: number): TPromise<string> { + return this.channel.call('getTitle', [windowId]); + } + + isActiveElement(windowId: number, selector: string): TPromise<boolean> { + return this.channel.call('isActiveElement', [windowId, selector]); + } + + getElements(windowId: number, selector: string, recursive: boolean): TPromise<IElement[]> { + return this.channel.call('getElements', [windowId, selector, recursive]); + } + + typeInEditor(windowId: number, selector: string, text: string): TPromise<void> { + return this.channel.call('typeInEditor', [windowId, selector, text]); + } + + getTerminalBuffer(windowId: number, selector: string): TPromise<string[]> { + return this.channel.call('getTerminalBuffer', [windowId, selector]); + } + + writeInTerminal(windowId: number, selector: string, text: string): TPromise<void> { + return this.channel.call('writeInTerminal', [windowId, selector, text]); + } +} + +export interface IDriverOptions { + verbose: boolean; +} + +export interface IWindowDriverRegistry { + registerWindowDriver(windowId: number): TPromise<IDriverOptions>; + reloadWindowDriver(windowId: number): TPromise<void>; +} + +export interface IWindowDriverRegistryChannel extends IChannel { + call(command: 'registerWindowDriver', arg: number): TPromise<IDriverOptions>; + call(command: 'reloadWindowDriver', arg: number): TPromise<void>; + call(command: string, arg: any): TPromise<any>; +} + +export class WindowDriverRegistryChannel implements IWindowDriverRegistryChannel { + + constructor(private registry: IWindowDriverRegistry) { } + + listen<T>(event: string): Event<T> { + throw new Error('No event found'); + } + + call(command: string, arg?: any): TPromise<any> { + switch (command) { + case 'registerWindowDriver': return this.registry.registerWindowDriver(arg); + case 'reloadWindowDriver': return this.registry.reloadWindowDriver(arg); + } + + return undefined; + } +} + +export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry { + + _serviceBrand: any; + + constructor(private channel: IWindowDriverRegistryChannel) { } + + registerWindowDriver(windowId: number): TPromise<IDriverOptions> { + return this.channel.call('registerWindowDriver', windowId); + } + + reloadWindowDriver(windowId: number): TPromise<void> { + return this.channel.call('reloadWindowDriver', windowId); + } +} + +export interface IWindowDriver { + click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): TPromise<void>; + doubleClick(selector: string): TPromise<void>; + setValue(selector: string, text: string): TPromise<void>; + getTitle(): TPromise<string>; + isActiveElement(selector: string): TPromise<boolean>; + getElements(selector: string, recursive: boolean): TPromise<IElement[]>; + typeInEditor(selector: string, text: string): TPromise<void>; + getTerminalBuffer(selector: string): TPromise<string[]>; + writeInTerminal(selector: string, text: string): TPromise<void>; +} + +export interface IWindowDriverChannel extends IChannel { + call(command: 'click', arg: [string, number | undefined, number | undefined]): TPromise<void>; + call(command: 'doubleClick', arg: string): TPromise<void>; + call(command: 'setValue', arg: [string, string]): TPromise<void>; + call(command: 'getTitle'): TPromise<string>; + call(command: 'isActiveElement', arg: string): TPromise<boolean>; + call(command: 'getElements', arg: [string, boolean]): TPromise<IElement[]>; + call(command: 'typeInEditor', arg: [string, string]): TPromise<void>; + call(command: 'getTerminalBuffer', arg: string): TPromise<string[]>; + call(command: 'writeInTerminal', arg: [string, string]): TPromise<void>; + call(command: string, arg: any): TPromise<any>; +} + +export class WindowDriverChannel implements IWindowDriverChannel { + + constructor(private driver: IWindowDriver) { } + + listen<T>(event: string): Event<T> { + throw new Error('No event found'); + } + + call(command: string, arg?: any): TPromise<any> { + switch (command) { + case 'click': return this.driver.click(arg[0], arg[1], arg[2]); + case 'doubleClick': return this.driver.doubleClick(arg); + case 'setValue': return this.driver.setValue(arg[0], arg[1]); + case 'getTitle': return this.driver.getTitle(); + case 'isActiveElement': return this.driver.isActiveElement(arg); + case 'getElements': return this.driver.getElements(arg[0], arg[1]); + case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]); + case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg); + case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]); + } + + return undefined; + } +} + +export class WindowDriverChannelClient implements IWindowDriver { + + _serviceBrand: any; + + constructor(private channel: IWindowDriverChannel) { } + + click(selector: string, xoffset?: number, yoffset?: number): TPromise<void> { + return this.channel.call('click', [selector, xoffset, yoffset]); + } + + doubleClick(selector: string): TPromise<void> { + return this.channel.call('doubleClick', selector); + } + + setValue(selector: string, text: string): TPromise<void> { + return this.channel.call('setValue', [selector, text]); + } + + getTitle(): TPromise<string> { + return this.channel.call('getTitle'); + } + + isActiveElement(selector: string): TPromise<boolean> { + return this.channel.call('isActiveElement', selector); + } + + getElements(selector: string, recursive: boolean): TPromise<IElement[]> { + return this.channel.call('getElements', [selector, recursive]); + } + + typeInEditor(selector: string, text: string): TPromise<void> { + return this.channel.call('typeInEditor', [selector, text]); + } + + getTerminalBuffer(selector: string): TPromise<string[]> { + return this.channel.call('getTerminalBuffer', selector); + } + + writeInTerminal(selector: string, text: string): TPromise<void> { + return this.channel.call('writeInTerminal', [selector, text]); + } +} export async function connect(handle: string): Promise<{ client: Client, driver: IDriver }> { const client = await connectNet(handle, 'driverClient'); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index c291dff9fec..d97203a0530 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import URI from 'vs/base/common/uri'; export interface ParsedArgs { [arg: string]: any; _: string[]; 'folder-uri'?: string | string[]; + 'file-uri'?: string | string[]; _urls?: string[]; help?: boolean; version?: boolean; @@ -31,6 +33,7 @@ export interface ParsedArgs { log?: string; logExtensionHostCommunication?: boolean; 'extensions-dir'?: string; + 'builtin-extensions-dir'?: string; extensionDevelopmentPath?: string; extensionTestsPath?: string; debugPluginHost?: string; @@ -103,8 +106,9 @@ export interface IEnvironmentService { isExtensionDevelopment: boolean; disableExtensions: boolean | string[]; + builtinExtensionsPath: string; extensionsPath: string; - extensionDevelopmentPath: string; + extensionDevelopmentLocationURI: URI; extensionTestsPath: string; debugExtensionHost: IExtensionHostDebugParams; @@ -119,6 +123,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 30cfd3ddd91..7183c2471eb 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -19,6 +19,7 @@ const options: minimist.Opts = { 'user-data-dir', 'extensions-dir', 'folder-uri', + 'file-uri', 'extensionDevelopmentPath', 'extensionTestsPath', 'install-extension', @@ -145,7 +146,6 @@ export function parseArgs(args: string[]): ParsedArgs { const optionsHelp: { [name: string]: string; } = { '-d, --diff <file> <file>': localize('diff', "Compare two files with each other."), - '--folder-uri <uri>': localize('folder uri', "Opens a window with given folder uri(s)"), '-a, --add <dir>': localize('add', "Add folder(s) to the last active window."), '-g, --goto <file:line[:character]>': 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."), @@ -234,3 +234,30 @@ ${formatOptions(extensionsHelp, columns)} ${ localize('troubleshooting', "Troubleshooting")}: ${formatOptions(troubleshootingHelp, columns)}`; } + +/** + * Converts an argument into an array + * @param arg a argument value. Can be undefined, an entry or an array + */ +export function asArray(arg: string | string[] | undefined): string[] { + if (arg) { + if (Array.isArray(arg)) { + return arg; + } + return [arg]; + } + return []; +} + +/** + * Returns whether an argument is present. + */ +export function hasArgs(arg: string | string[] | undefined): boolean { + if (arg) { + if (Array.isArray(arg)) { + return !!arg.length; + } + return true; + } + return false; +} \ No newline at end of file diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 9ef3481c43d..fdd300c54e4 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -8,12 +8,13 @@ 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'; +import URI from 'vs/base/common/uri'; // Read this before there's any chance it is overwritten // Related to https://github.com/Microsoft/vscode/issues/30624 @@ -77,7 +78,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; } @@ -132,6 +133,16 @@ export class EnvironmentService implements IEnvironmentService { @memoize get installSourcePath(): string { return path.join(this.userDataPath, 'installSource'); } + @memoize + get builtinExtensionsPath(): string { + const fromArgs = parsePathArg(this._args['builtin-extensions-dir'], process); + if (fromArgs) { + return fromArgs; + } else { + return path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions')); + } + } + @memoize get extensionsPath(): string { const fromArgs = parsePathArg(this._args['extensions-dir'], process); @@ -148,7 +159,16 @@ export class EnvironmentService implements IEnvironmentService { } @memoize - get extensionDevelopmentPath(): string { return this._args.extensionDevelopmentPath ? path.normalize(this._args.extensionDevelopmentPath) : this._args.extensionDevelopmentPath; } + get extensionDevelopmentLocationURI(): URI { + const s = this._args.extensionDevelopmentPath; + if (s) { + if (/^[^:/?#]+?:\/\//.test(s)) { + return URI.parse(s); + } + return URI.file(path.normalize(s)); + } + return void 0; + } @memoize get extensionTestsPath(): string { return this._args.extensionTestsPath ? path.normalize(this._args.extensionTestsPath) : this._args.extensionTestsPath; } @@ -183,6 +203,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/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index e30d7e8787e..3f6e9a49fbe 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -122,6 +122,7 @@ export interface IExtensionManifest { main?: string; icon?: string; categories?: string[]; + keywords?: string[]; activationEvents?: string[]; extensionDependencies?: string[]; extensionPack?: string[]; @@ -255,7 +256,8 @@ export interface IReportedExtension { } export enum InstallOperation { - Install = 1, + None = 0, + Install, Update } @@ -276,6 +278,7 @@ export interface IExtensionGalleryService { loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension>; loadAllDependencies(dependencies: IExtensionIdentifier[]): TPromise<IGalleryExtension[]>; getExtensionsReport(): TPromise<IReportedExtension[]>; + getExtension(id: IExtensionIdentifier, version?: string): TPromise<IGalleryExtension>; } export interface InstallExtensionEvent { @@ -306,7 +309,9 @@ export interface IExtensionManagementService { onUninstallExtension: Event<IExtensionIdentifier>; onDidUninstallExtension: Event<DidUninstallExtensionEvent>; - install(zipPath: string): TPromise<void>; + zip(extension: ILocalExtension): TPromise<URI>; + unzip(zipLocation: URI, type: LocalExtensionType): TPromise<IExtensionIdentifier>; + install(vsix: URI): TPromise<IExtensionIdentifier>; installFromGallery(extension: IGalleryExtension): TPromise<void>; uninstall(extension: ILocalExtension, force?: boolean): TPromise<void>; reinstallFromGallery(extension: ILocalExtension): TPromise<void>; @@ -320,13 +325,14 @@ export const IExtensionManagementServerService = createDecorator<IExtensionManag export interface IExtensionManagementServer { extensionManagementService: IExtensionManagementService; - location: URI; + authority: string; + label: string; } export interface IExtensionManagementServerService { _serviceBrand: any; readonly extensionManagementServers: IExtensionManagementServer[]; - getDefaultExtensionManagementServer(): IExtensionManagementServer; + getLocalExtensionManagementServer(): IExtensionManagementServer; getExtensionManagementServer(location: URI): IExtensionManagementServer; } diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index 505673fa7fc..4241abb9f91 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -5,7 +5,8 @@ 'use strict'; -import { ILocalExtension, IGalleryExtension, EXTENSION_IDENTIFIER_REGEX, IExtensionIdentifier, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ILocalExtension, IGalleryExtension, EXTENSION_IDENTIFIER_REGEX, IExtensionIdentifier, IReportedExtension, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean { if (a.uuid && b.uuid) { @@ -117,4 +118,48 @@ export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set<str } return result; +} + +const workspaceExtensions = new Set<string>(); +[ + 'vscode.extension-editing', + 'vscode.configuration-editing', + 'vscode.search-rg', + 'vscode.css-language-features', + 'vscode.git', + 'vscode.grunt', + 'vscode.gulp', + 'vscode.html-language-features', + 'vscode.json-language-features', + 'vscode.markdown-language-features', + 'vscode.npm', + 'vscode.php-language-features', + 'vscode.typescript-language-features', + 'ms-vscode.node-debug', + 'ms-vscode.node-debug2', + 'ms-python.python', + 'eg2.tslint', + 'dbaeumer.vscode-eslint', + 'eamodio.gitlens' +].forEach(extension => workspaceExtensions.add(extension)); + +export function isWorkspaceExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { + const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); + const configuredWorkspaceExtensions = configurationService.getValue<string[]>('_workbench.workspaceExtensions') || []; + if (configuredWorkspaceExtensions.length) { + if (configuredWorkspaceExtensions.indexOf(extensionId) !== -1) { + return true; + } + if (configuredWorkspaceExtensions.indexOf(`-${extensionId}`) !== -1) { + return false; + } + } + + if (manifest.main) { + if ((manifest.categories || []).indexOf('Workspace Extension') !== -1) { + return true; + } + return workspaceExtensions.has(extensionId); + } + return false; } \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/common/multiExtensionManagement.ts b/src/vs/platform/extensionManagement/common/multiExtensionManagement.ts index c9308a0d0f1..852ccee6bfc 100644 --- a/src/vs/platform/extensionManagement/common/multiExtensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/multiExtensionManagement.ts @@ -7,29 +7,53 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Event, EventMultiplexer } from 'vs/base/common/event'; import { IExtensionManagementService, ILocalExtension, IGalleryExtension, LocalExtensionType, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, - IExtensionManagementServerService, IExtensionManagementServer + IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { flatten } from 'vs/base/common/arrays'; +import { isWorkspaceExtension, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import URI from 'vs/base/common/uri'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { localize } from 'vs/nls'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { Action } from 'vs/base/common/actions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -export class MulitExtensionManagementService implements IExtensionManagementService { +export class MulitExtensionManagementService extends Disposable implements IExtensionManagementService { _serviceBrand: any; - onInstallExtension: Event<InstallExtensionEvent>; - onDidInstallExtension: Event<DidInstallExtensionEvent>; - onUninstallExtension: Event<IExtensionIdentifier>; - onDidUninstallExtension: Event<DidUninstallExtensionEvent>; + readonly onInstallExtension: Event<InstallExtensionEvent>; + readonly onDidInstallExtension: Event<DidInstallExtensionEvent>; + readonly onUninstallExtension: Event<IExtensionIdentifier>; + readonly onDidUninstallExtension: Event<DidUninstallExtensionEvent>; private readonly servers: IExtensionManagementServer[]; + private readonly localServer: IExtensionManagementServer; + private readonly otherServers: IExtensionManagementServer[]; constructor( - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @INotificationService private notificationService: INotificationService, + @IWindowService private windowService: IWindowService, + @ILogService private logService: ILogService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, + @IConfigurationService private configurationService: IConfigurationService ) { + super(); this.servers = this.extensionManagementServerService.extensionManagementServers; - this.onInstallExtension = this.servers.reduce((emitter: EventMultiplexer<InstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer<InstallExtensionEvent>()).event; - this.onDidInstallExtension = this.servers.reduce((emitter: EventMultiplexer<DidInstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer<DidInstallExtensionEvent>()).event; - this.onUninstallExtension = this.servers.reduce((emitter: EventMultiplexer<IExtensionIdentifier>, server) => { emitter.add(server.extensionManagementService.onUninstallExtension); return emitter; }, new EventMultiplexer<IExtensionIdentifier>()).event; - this.onDidUninstallExtension = this.servers.reduce((emitter: EventMultiplexer<DidUninstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer<DidUninstallExtensionEvent>()).event; + this.localServer = this.extensionManagementServerService.getLocalExtensionManagementServer(); + this.otherServers = this.servers.filter(s => s !== this.localServer); + + this.onInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<InstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer<InstallExtensionEvent>())).event; + this.onDidInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<DidInstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer<DidInstallExtensionEvent>())).event; + this.onUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<IExtensionIdentifier>, server) => { emitter.add(server.extensionManagementService.onUninstallExtension); return emitter; }, new EventMultiplexer<IExtensionIdentifier>())).event; + this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<DidUninstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer<DidUninstallExtensionEvent>())).event; + + if (this.otherServers.length) { + this.syncExtensions(); + } } getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]> { @@ -49,19 +73,118 @@ export class MulitExtensionManagementService implements IExtensionManagementServ return this.getServer(extension).extensionManagementService.updateMetadata(extension, metadata); } - install(zipPath: string): TPromise<void> { - return this.servers[0].extensionManagementService.install(zipPath); + zip(extension: ILocalExtension): TPromise<URI> { + throw new Error('Not Supported'); } - installFromGallery(extension: IGalleryExtension): TPromise<void> { - return TPromise.join(this.servers.map(server => server.extensionManagementService.installFromGallery(extension))).then(() => null); + unzip(zipLocation: URI, type: LocalExtensionType): TPromise<IExtensionIdentifier> { + return TPromise.join(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(() => null); + } + + install(vsix: URI): TPromise<IExtensionIdentifier> { + return this.localServer.extensionManagementService.install(vsix) + .then(extensionIdentifer => this.localServer.extensionManagementService.getInstalled(LocalExtensionType.User) + .then(installed => { + const extension = installed.filter(i => areSameExtensions(i.identifier, extensionIdentifer))[0]; + if (this.otherServers.length && extension && isWorkspaceExtension(extension.manifest, this.configurationService)) { + return TPromise.join(this.otherServers.map(server => server.extensionManagementService.install(vsix))) + .then(() => extensionIdentifer); + } + return extensionIdentifer; + })); + } + + installFromGallery(gallery: IGalleryExtension): TPromise<void> { + if (this.otherServers.length === 0) { + return this.localServer.extensionManagementService.installFromGallery(gallery); + } + return this.extensionGalleryService.getManifest(gallery) + .then(manifest => { + const servers = isWorkspaceExtension(manifest, this.configurationService) ? this.servers : [this.localServer]; + return TPromise.join(servers.map(server => server.extensionManagementService.installFromGallery(gallery))) + .then(() => null); + }); } getExtensionsReport(): TPromise<IReportedExtension[]> { - return this.servers[0].extensionManagementService.getExtensionsReport(); + return this.extensionManagementServerService.getLocalExtensionManagementServer().extensionManagementService.getExtensionsReport(); } private getServer(extension: ILocalExtension): IExtensionManagementServer { return this.extensionManagementServerService.getExtensionManagementServer(extension.location); } + + private async syncExtensions(): Promise<void> { + this.localServer.extensionManagementService.getInstalled(LocalExtensionType.User) + .then(async localExtensions => { + const workspaceExtensions = localExtensions.filter(e => isWorkspaceExtension(e.manifest, this.configurationService)); + const extensionsToSync: Map<IExtensionManagementServer, ILocalExtension[]> = await this.getExtensionsToSync(workspaceExtensions); + if (extensionsToSync.size > 0) { + const handler = this.notificationService.notify({ severity: Severity.Info, message: localize('synchronising', "Synchronising workspace extensions...") }); + handler.progress.infinite(); + this.doSyncExtensions(extensionsToSync).then(() => { + handler.progress.done(); + handler.updateMessage(localize('Synchronize.finished', "Finished synchronising workspace extensions. Please reload now.")); + handler.updateActions({ + primary: [ + new Action('Synchronize.reloadNow', localize('Synchronize.reloadNow', "Reload Now"), null, true, () => this.windowService.reloadWindow()) + ] + }); + }, error => { + handler.progress.done(); + handler.updateMessage(error); + }); + } + }, err => this.logService.error('Error while Synchronisation', err)); + } + + private async getExtensionsToSync(workspaceExtensions: ILocalExtension[]): Promise<Map<IExtensionManagementServer, ILocalExtension[]>> { + const extensionsToSync: Map<IExtensionManagementServer, ILocalExtension[]> = new Map<IExtensionManagementServer, ILocalExtension[]>(); + for (const server of this.otherServers) { + const extensions = await server.extensionManagementService.getInstalled(LocalExtensionType.User); + const groupedByVersionId: Map<string, ILocalExtension> = extensions.reduce((groupedById, extension) => groupedById.set(`${extension.galleryIdentifier.id}-${extension.manifest.version}`, extension), new Map<string, ILocalExtension>()); + const toSync = workspaceExtensions.filter(e => !groupedByVersionId.has(`${e.galleryIdentifier.id}-${e.manifest.version}`)); + if (toSync.length) { + extensionsToSync.set(server, toSync); + } + } + return extensionsToSync; + } + + private async doSyncExtensions(extensionsToSync: Map<IExtensionManagementServer, ILocalExtension[]>): Promise<void> { + const ids: string[] = []; + const zipLocationResolvers: TPromise<{ location: URI, vsix: boolean }>[] = []; + + extensionsToSync.forEach(extensions => { + for (const extension of extensions) { + if (ids.indexOf(extension.galleryIdentifier.id) === -1) { + ids.push(extension.galleryIdentifier.id); + zipLocationResolvers.push(this.downloadFromGallery(extension) + .then(location => location ? { location, vsix: true } : this.localServer.extensionManagementService.zip(extension).then(location => ({ location, vsix: false })))); + } + } + }); + + const zipLocations = await TPromise.join(zipLocationResolvers); + const promises: Promise<any>[] = []; + extensionsToSync.forEach((extensions, server) => { + let promise: Promise<any> = Promise.resolve(); + extensions.forEach(extension => { + const index = ids.indexOf(extension.galleryIdentifier.id); + const { location, vsix } = zipLocations[index]; + promise = promise.then(() => vsix ? server.extensionManagementService.install(location) : server.extensionManagementService.unzip(location, extension.type)); + }); + promises.push(promise); + }); + + await Promise.all(promises); + } + + private downloadFromGallery(extension: ILocalExtension): TPromise<URI> { + if (this.extensionGalleryService.isEnabled()) { + return this.extensionGalleryService.getExtension(extension.galleryIdentifier, extension.manifest.version) + .then(galleryExtension => galleryExtension ? this.extensionGalleryService.download(galleryExtension, InstallOperation.None).then(location => URI.file(location)) : null); + } + return TPromise.as(null); + } } \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 16af4dbf97a..4b9f49f66a5 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { tmpdir } from 'os'; import * as path from 'path'; import { TPromise } from 'vs/base/common/winjs.base'; import { distinct } from 'vs/base/common/arrays'; -import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; +import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/common/errors'; import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionManifest, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { assign, getOrDefault } from 'vs/base/common/objects'; @@ -24,6 +23,8 @@ import { readFile } from 'vs/base/node/pfs'; import { writeFileAndFlushSync } from 'vs/base/node/extfs'; import { generateUuid, isUUID } from 'vs/base/common/uuid'; import { values } from 'vs/base/common/map'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { wireCancellationToken } from 'vs/base/common/async'; interface IRawGalleryExtensionFile { assetType: string; @@ -278,8 +279,7 @@ function getIsPreview(flags: string): boolean { return flags.indexOf('preview') !== -1; } -function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUrl: string, index: number, query: Query, querySource?: string): IGalleryExtension { - const [version] = galleryExtension.versions; +function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, index: number, query: Query, querySource?: string): IGalleryExtension { const assets = { manifest: getVersionAsset(version, AssetType.Manifest), readme: getVersionAsset(version, AssetType.Details), @@ -362,6 +362,31 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return !!this.extensionsGalleryUrl; } + getExtension({ id, uuid }: IExtensionIdentifier, version?: string): TPromise<IGalleryExtension> { + let query = new Query() + .withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties) + .withPage(1, 1) + .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') + .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); + + if (uuid) { + query = query.withFilter(FilterType.ExtensionId, uuid); + } else { + query = query.withFilter(FilterType.ExtensionName, id); + } + + return this.queryGallery(query).then(({ galleryExtensions }) => { + if (galleryExtensions.length) { + const galleryExtension = galleryExtensions[0]; + const versionAsset = version ? galleryExtension.versions.filter(v => v.version === version)[0] : galleryExtension.versions[0]; + if (versionAsset) { + return toExtension(galleryExtension, versionAsset, 0, query); + } + } + return null; + }); + } + query(options: IQueryOptions = {}): TPromise<IPager<IGalleryExtension>> { if (!this.isEnabled()) { return TPromise.wrapError<IPager<IGalleryExtension>>(new Error('No extension gallery service configured.')); @@ -423,15 +448,21 @@ export class ExtensionGalleryService implements IExtensionGalleryService { } return this.queryGallery(query).then(({ galleryExtensions, total }) => { - const extensions = galleryExtensions.map((e, index) => toExtension(e, this.extensionsGalleryUrl, index, query, options.source)); + const extensions = galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, query, options.source)); const pageSize = query.pageSize; - const getPage = (pageIndex: number) => { + const getPage = (pageIndex: number, ct: CancellationToken) => { + if (ct.isCancellationRequested) { + return TPromise.wrapError(canceled()); + } + const nextPageQuery = query.withPage(pageIndex + 1); - return this.queryGallery(nextPageQuery) - .then(({ galleryExtensions }) => galleryExtensions.map((e, index) => toExtension(e, this.extensionsGalleryUrl, index, nextPageQuery, options.source))); + const promise = this.queryGallery(nextPageQuery) + .then(({ galleryExtensions }) => galleryExtensions.map((e, index) => toExtension(e, e.versions[0], index, nextPageQuery, options.source))); + + return wireCancellationToken(ct, promise); }; - return { firstPage: extensions, total, pageSize, getPage }; + return { firstPage: extensions, total, pageSize, getPage } as IPager<IGalleryExtension>; }); } @@ -485,35 +516,29 @@ export class ExtensionGalleryService implements IExtensionGalleryService { } download(extension: IGalleryExtension, operation: InstallOperation): TPromise<string> { - return this.loadCompatibleVersion(extension) - .then(extension => { - if (!extension) { - return TPromise.wrapError(new Error(localize('notCompatibleDownload', "Unable to download because the extension compatible with current version '{0}' of VS Code is not found.", pkg.version))); - } - const zipPath = path.join(tmpdir(), generateUuid()); - const data = getGalleryExtensionTelemetryData(extension); - const startTime = new Date().getTime(); - /* __GDPR__ - "galleryService:downloadVSIX" : { - "duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "${include}": [ - "${GalleryExtensionTelemetryData}" - ] - } - */ - const log = (duration: number) => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration })); + const zipPath = path.join(tmpdir(), generateUuid()); + const data = getGalleryExtensionTelemetryData(extension); + const startTime = new Date().getTime(); + /* __GDPR__ + "galleryService:downloadVSIX" : { + "duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "${include}": [ + "${GalleryExtensionTelemetryData}" + ] + } + */ + const log = (duration: number) => this.telemetryService.publicLog('galleryService:downloadVSIX', assign(data, { duration })); - const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : ''; - const downloadAsset = operationParam ? { - uri: `${extension.assets.download.uri}&${operationParam}=true`, - fallbackUri: `${extension.assets.download.fallbackUri}?${operationParam}=true` - } : extension.assets.download; + const operationParam = operation === InstallOperation.Install ? 'install' : operation === InstallOperation.Update ? 'update' : ''; + const downloadAsset = operationParam ? { + uri: `${extension.assets.download.uri}&${operationParam}=true`, + fallbackUri: `${extension.assets.download.fallbackUri}?${operationParam}=true` + } : extension.assets.download; - return this.getAsset(downloadAsset) - .then(context => download(zipPath, context)) - .then(() => log(new Date().getTime() - startTime)) - .then(() => zipPath); - }); + return this.getAsset(downloadAsset) + .then(context => download(zipPath, context)) + .then(() => log(new Date().getTime() - startTime)) + .then(() => zipPath); } getReadme(extension: IGalleryExtension): TPromise<string> { @@ -601,7 +626,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { for (let index = 0; index < result.galleryExtensions.length; index++) { const rawExtension = result.galleryExtensions[index]; if (ids.indexOf(rawExtension.extensionId) === -1) { - dependencies.push(toExtension(rawExtension, this.extensionsGalleryUrl, index, query, 'dependencies')); + dependencies.push(toExtension(rawExtension, rawExtension.versions[0], index, query, 'dependencies')); ids.push(rawExtension.extensionId); } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts similarity index 83% rename from src/vs/platform/extensionManagement/common/extensionManagementIpc.ts rename to src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index 4180af7c8a3..5a0c7a0b8de 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -6,8 +6,8 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from './extensionManagement'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement'; import { Event, buffer, mapEvent } from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; import { IURITransformer } from 'vs/base/common/uriIpc'; @@ -17,7 +17,10 @@ export interface IExtensionManagementChannel extends IChannel { listen(event: 'onDidInstallExtension'): Event<DidInstallExtensionEvent>; listen(event: 'onUninstallExtension'): Event<IExtensionIdentifier>; listen(event: 'onDidUninstallExtension'): Event<DidUninstallExtensionEvent>; - call(command: 'install', args: [string]): TPromise<void>; + + call(command: 'zip', args: [ILocalExtension]): TPromise<URI>; + call(command: 'unzip', args: [URI, LocalExtensionType]): TPromise<IExtensionIdentifier>; + call(command: 'install', args: [URI]): TPromise<IExtensionIdentifier>; call(command: 'installFromGallery', args: [IGalleryExtension]): TPromise<void>; call(command: 'uninstall', args: [ILocalExtension, boolean]): TPromise<void>; call(command: 'reinstallFromGallery', args: [ILocalExtension]): TPromise<void>; @@ -53,7 +56,9 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel { call(command: string, args?: any): TPromise<any> { switch (command) { - case 'install': return this.service.install(args[0]); + case 'zip': return this.service.zip(this._transform(args[0])); + case 'unzip': return this.service.unzip(URI.revive(args[0]), args[1]); + case 'install': return this.service.install(URI.revive(args[0])); case 'installFromGallery': return this.service.installFromGallery(args[0]); case 'uninstall': return this.service.uninstall(this._transform(args[0]), args[1]); case 'reinstallFromGallery': return this.service.reinstallFromGallery(this._transform(args[0])); @@ -81,8 +86,16 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer get onUninstallExtension(): Event<IExtensionIdentifier> { return this.channel.listen('onUninstallExtension'); } get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this.channel.listen('onDidUninstallExtension'); } - install(zipPath: string): TPromise<void> { - return this.channel.call('install', [zipPath]); + zip(extension: ILocalExtension): TPromise<URI> { + return this.channel.call('zip', [this._transformOutgoing(extension)]).then(result => URI.revive(this.uriTransformer.transformIncoming(result))); + } + + unzip(zipLocation: URI, type: LocalExtensionType): TPromise<IExtensionIdentifier> { + return this.channel.call('unzip', [this.uriTransformer.transformOutgoing(zipLocation), type]); + } + + install(vsix: URI): TPromise<IExtensionIdentifier> { + return this.channel.call('install', [this.uriTransformer.transformOutgoing(vsix)]); } installFromGallery(extension: IGalleryExtension): TPromise<void> { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 84989938f78..d235a328edb 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -12,7 +12,7 @@ import * as errors from 'vs/base/common/errors'; import { assign } from 'vs/base/common/objects'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { flatten } from 'vs/base/common/arrays'; -import { extract, buffer, ExtractError } from 'vs/base/node/zip'; +import { extract, buffer, ExtractError, zip, IFile } from 'vs/base/node/zip'; import { TPromise, ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, @@ -40,8 +40,12 @@ 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 { tmpdir } from 'os'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { optional } from 'vs/platform/instantiation/common/instantiation'; +import { Schemas } from 'vs/base/common/network'; -const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; @@ -108,6 +112,7 @@ export class ExtensionManagementService extends Disposable implements IExtension _serviceBrand: any; + private systemExtensionsPath: string; private extensionsPath: string; private uninstalledPath: string; private uninstalledFileLimiter: Limiter<void>; @@ -135,9 +140,11 @@ export class ExtensionManagementService extends Disposable implements IExtension @IDialogService private dialogService: IDialogService, @IExtensionGalleryService private galleryService: IExtensionGalleryService, @ILogService private logService: ILogService, + @optional(IDownloadService) private downloadService: IDownloadService, @ITelemetryService private telemetryService: ITelemetryService, ) { super(); + this.systemExtensionsPath = environmentService.builtinExtensionsPath; this.extensionsPath = environmentService.extensionsPath; this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); this.uninstalledFileLimiter = new Limiter(1); @@ -152,37 +159,87 @@ export class ExtensionManagementService extends Disposable implements IExtension })); } - install(zipPath: string): TPromise<void> { - zipPath = path.resolve(zipPath); + zip(extension: ILocalExtension): TPromise<URI> { + return TPromise.wrap(this.collectFiles(extension)) + .then(files => zip(path.join(tmpdir(), generateUuid()), files)) + .then(path => URI.file(path)); + } - return validateLocalExtension(zipPath) - .then(manifest => { - const identifier = { id: getLocalExtensionIdFromManifest(manifest) }; - if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) { - return TPromise.wrapError<void>(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version))); + unzip(zipLocation: URI, type: LocalExtensionType): TPromise<IExtensionIdentifier> { + return this.install(zipLocation, type); + } + + private collectFiles(extension: ILocalExtension): Promise<IFile[]> { + + const collectFilesFromDirectory = async (dir): Promise<string[]> => { + let entries = await pfs.readdir(dir); + entries = entries.map(e => path.join(dir, e)); + const stats = await Promise.all(entries.map(e => pfs.stat(e))); + let promise: Promise<string[]> = Promise.resolve([]); + stats.forEach((stat, index) => { + const entry = entries[index]; + if (stat.isFile()) { + promise = promise.then(result => ([...result, entry])); + } + if (stat.isDirectory()) { + promise = promise + .then(result => collectFilesFromDirectory(entry) + .then(files => ([...result, ...files]))); } - return this.removeIfExists(identifier.id) - .then( - () => this.checkOutdated(manifest) - .then(validated => { - if (validated) { - this.logService.info('Installing the extension:', identifier.id); - this._onInstallExtension.fire({ identifier, zipPath }); - return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) - .then( - metadata => this.installFromZipPath(identifier, zipPath, metadata, manifest), - error => this.installFromZipPath(identifier, zipPath, null, manifest)) - .then( - () => { this.logService.info('Successfully installed the extension:', identifier.id); }, - e => { - this.logService.error('Failed to install the extension:', identifier.id, e.message); - return TPromise.wrapError(e); - }); - } - return null; - }), - e => TPromise.wrapError(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", manifest.displayName || manifest.name)))); }); + return promise; + }; + + return collectFilesFromDirectory(extension.location.fsPath) + .then(files => files.map(f => (<IFile>{ path: `extension/${path.relative(extension.location.fsPath, f)}`, localPath: f }))); + + } + + install(vsix: URI, type: LocalExtensionType = LocalExtensionType.User): TPromise<IExtensionIdentifier> { + return this.downloadVsix(vsix) + .then(downloadLocation => { + const zipPath = path.resolve(downloadLocation.fsPath); + + return validateLocalExtension(zipPath) + .then(manifest => { + const identifier = { id: getLocalExtensionIdFromManifest(manifest) }; + if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) { + return TPromise.wrapError<IExtensionIdentifier>(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version))); + } + return this.removeIfExists(identifier.id) + .then( + () => this.checkOutdated(manifest) + .then(validated => { + if (validated) { + this.logService.info('Installing the extension:', identifier.id); + this._onInstallExtension.fire({ identifier, zipPath }); + return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) + .then( + metadata => this.installFromZipPath(identifier, zipPath, metadata, type), + error => this.installFromZipPath(identifier, zipPath, null, type)) + .then( + () => { this.logService.info('Successfully installed the extension:', identifier.id); return identifier; }, + e => { + this.logService.error('Failed to install the extension:', identifier.id, e.message); + return TPromise.wrapError(e); + }); + } + return null; + }), + e => TPromise.wrapError(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + }); + }); + } + + private downloadVsix(vsix: URI): TPromise<URI> { + if (vsix.scheme === Schemas.file) { + return TPromise.as(vsix); + } + if (!this.downloadService) { + throw new Error('Download service is not available'); + } + const downloadedLocation = path.join(tmpdir(), generateUuid()); + return this.downloadService.download(vsix, downloadedLocation).then(() => URI.file(downloadedLocation)); } private removeIfExists(id: string): TPromise<void> { @@ -214,11 +271,11 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<ILocalExtension> { + private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, type: LocalExtensionType): TPromise<ILocalExtension> { return this.toNonCancellablePromise(this.getInstalled() .then(installed => { const operation = this.getOperation({ id: getIdFromLocalExtensionId(identifier.id), uuid: identifier.uuid }, installed); - return this.installExtension({ zipPath, id: identifier.id, metadata }) + return this.installExtension({ zipPath, id: identifier.id, metadata }, type) .then(local => this.installDependenciesAndPackExtensions(local, null).then(() => local, error => this.uninstall(local, true).then(() => TPromise.wrapError(error), () => TPromise.wrapError(error)))) .then( local => { this._onDidInstallExtension.fire({ identifier, zipPath, local, operation }); return local; }, @@ -250,7 +307,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(local => always(pfs.rimraf(installableExtension.zipPath), () => null).then(() => local))) + .then(installableExtension => this.installExtension(installableExtension, LocalExtensionType.User).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)))); }) @@ -328,7 +385,7 @@ export class ExtensionManagementService extends Disposable implements IExtension compatible => { if (compatible) { this.logService.trace('Started downloading extension:', extension.name); - return this.galleryService.download(extension, operation) + return this.galleryService.download(compatible, operation) .then( zipPath => { this.logService.info('Downloaded extension:', extension.name, zipPath); @@ -346,14 +403,14 @@ export class ExtensionManagementService extends Disposable implements IExtension error => TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY))); } - private installExtension(installableExtension: InstallableExtension): TPromise<ILocalExtension> { + private installExtension(installableExtension: InstallableExtension, type: LocalExtensionType): TPromise<ILocalExtension> { return this.unsetUninstalledAndGetLocal(installableExtension.id) .then( local => { if (local) { return local; } - return this.extractAndInstall(installableExtension); + return this.extractAndInstall(installableExtension, type); }, e => { if (isMacintosh) { @@ -380,14 +437,15 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private extractAndInstall({ zipPath, id, metadata }: InstallableExtension): TPromise<ILocalExtension> { - const tempPath = path.join(this.extensionsPath, `.${id}`); - const extensionPath = path.join(this.extensionsPath, id); + private extractAndInstall({ zipPath, id, metadata }: InstallableExtension, type: LocalExtensionType): TPromise<ILocalExtension> { + const location = type === LocalExtensionType.User ? this.extensionsPath : this.systemExtensionsPath; + const tempPath = path.join(location, `.${id}`); + const extensionPath = path.join(location, id); return pfs.rimraf(extensionPath) .then(() => this.extractAndRename(id, zipPath, tempPath, extensionPath), e => TPromise.wrapError(new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, id), INSTALL_ERROR_DELETING))) .then(() => { this.logService.info('Installation completed.', id); - return this.scanExtension(id, this.extensionsPath, LocalExtensionType.User); + return this.scanExtension(id, location, type); }) .then(local => { if (metadata) { @@ -538,12 +596,20 @@ export class ExtensionManagementService extends Disposable implements IExtension private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise<void> { 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 +618,17 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private promptForPackAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): TPromise<void> { - 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<void> { + 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<void>(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 +638,6 @@ export class ExtensionManagementService extends Disposable implements IExtension }, error => TPromise.wrapError(errors.canceled())); } - private uninstallExtensionAsPack(extension: ILocalExtension, installed: ILocalExtension[]): TPromise<void> { - 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<void> { const dependents = this.getDependents(extension, installed); if (dependents.length) { @@ -721,7 +777,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private scanSystemExtensions(): TPromise<ILocalExtension[]> { this.logService.trace('Started scanning system extensions'); - return this.scanExtensions(SystemExtensionsRoot, LocalExtensionType.System) + return this.scanExtensions(this.systemExtensionsPath, LocalExtensionType.System) .then(result => { this.logService.info('Scanned system extensions:', result.length); return result; diff --git a/src/vs/platform/history/common/history.ts b/src/vs/platform/history/common/history.ts index f09d7fd44ba..0434680bcea 100644 --- a/src/vs/platform/history/common/history.ts +++ b/src/vs/platform/history/common/history.ts @@ -9,12 +9,13 @@ import { IPath } from 'vs/platform/windows/common/windows'; import { Event as CommonEvent } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import URI from 'vs/base/common/uri'; export const IHistoryMainService = createDecorator<IHistoryMainService>('historyMainService'); export interface IRecentlyOpened { workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]; - files: string[]; + files: URI[]; } export interface IHistoryMainService { @@ -22,9 +23,9 @@ export interface IHistoryMainService { onRecentlyOpenedChange: CommonEvent<void>; - addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: string[]): void; + addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: URI[]): void; getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened; - removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): void; + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | 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 2df1458fbd9..a3defec9a9a 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -5,29 +5,32 @@ '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, IWorkspaceSavedEvent, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspacesMainService, 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 } from 'vs/base/common/resources'; +import { getComparisonKey, isEqual as areResourcesEqual, dirname } from 'vs/base/common/resources'; import URI, { UriComponents } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { ILabelService } from 'vs/platform/label/common/label'; interface ISerializedRecentlyOpened { - workspaces: (IWorkspaceIdentifier | string | UriComponents)[]; - files: string[]; + workspaces2: (IWorkspaceIdentifier | string)[]; // IWorkspaceIdentifier or URI.toString() + files2: string[]; // files as URI.toString() +} + +interface ILegacySerializedRecentlyOpened { + workspaces: (IWorkspaceIdentifier | string | UriComponents)[]; // legacy (UriComponents was also supported for a few insider builds) + files: string[]; // files as paths } export class HistoryMainService implements IHistoryMainService { @@ -48,7 +51,7 @@ export class HistoryMainService implements IHistoryMainService { @IStateService private stateService: IStateService, @ILogService private logService: ILogService, @IWorkspacesMainService private workspacesMainService: IWorkspacesMainService, - @IEnvironmentService private environmentService: IEnvironmentService + @ILabelService private labelService: ILabelService ) { this.macOSRecentDocumentsUpdater = new RunOnceScheduler(() => this.updateMacOSRecentDocuments(), 800); @@ -65,7 +68,7 @@ export class HistoryMainService implements IHistoryMainService { this.addRecentlyOpened([e.workspace], []); } - addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: string[]): void { + addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: URI[]): void { if ((workspaces && workspaces.length > 0) || (files && files.length > 0)) { const mru = this.getRecentlyOpened(); @@ -87,13 +90,13 @@ export class HistoryMainService implements IHistoryMainService { // Files if (Array.isArray(files)) { - files.forEach((path) => { - mru.files.unshift(path); + files.forEach((fileUri) => { + mru.files.unshift(fileUri); mru.files = arrays.distinct(mru.files, file => this.distinctFn(file)); // Add to recent documents (Windows only, macOS later) - if (isWindows) { - app.addRecentDocument(path); + if (isWindows && fileUri.scheme === Schemas.file) { + app.addRecentDocument(fileUri.fsPath); } }); } @@ -112,7 +115,7 @@ export class HistoryMainService implements IHistoryMainService { } } - removeFromRecentlyOpened(pathsToRemove: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): void { + removeFromRecentlyOpened(pathsToRemove: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): void { const mru = this.getRecentlyOpened(); let update = false; @@ -124,11 +127,11 @@ export class HistoryMainService implements IHistoryMainService { return isWorkspaceIdentifier(workspace) && isEqual(pathToRemove.configPath, workspace.configPath, !isLinux /* ignorecase */); } if (isSingleFolderWorkspaceIdentifier(pathToRemove)) { - return isSingleFolderWorkspaceIdentifier(workspace) && areResourcesEqual(pathToRemove, workspace, hasToIgnoreCase(pathToRemove)); + return isSingleFolderWorkspaceIdentifier(workspace) && areResourcesEqual(pathToRemove, workspace); } if (typeof pathToRemove === 'string') { if (isSingleFolderWorkspaceIdentifier(workspace)) { - return workspace.scheme === Schemas.file && areResourcesEqual(URI.file(pathToRemove), workspace, hasToIgnoreCase(workspace)); + return workspace.scheme === Schemas.file && areResourcesEqual(URI.file(pathToRemove), workspace); } if (isWorkspaceIdentifier(workspace)) { return isEqual(pathToRemove, workspace.configPath, !isLinux /* ignorecase */); @@ -142,7 +145,10 @@ export class HistoryMainService implements IHistoryMainService { } // Remove file - index = arrays.firstIndex(mru.files, file => typeof pathToRemove === 'string' && isEqual(file, pathToRemove, !isLinux /* ignorecase */)); + const pathToRemoveURI = pathToRemove instanceof URI ? pathToRemove : typeof pathToRemove === 'string' ? URI.file(pathToRemove) : null; + if (pathToRemoveURI) { + index = arrays.firstIndex(mru.files, file => areResourcesEqual(file, pathToRemoveURI)); + } if (index >= 0) { mru.files.splice(index, 1); update = true; @@ -177,16 +183,27 @@ 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++) { + let nEntries = 0; + for (let i = 0; i < mru.workspaces.length && nEntries < HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES / 2; i++) { const workspace = mru.workspaces[i]; - app.addRecentDocument(isSingleFolderWorkspaceIdentifier(workspace) ? workspace.scheme === Schemas.file ? workspace.fsPath : workspace.toString() : workspace.configPath); - maxEntries--; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + if (workspace.scheme === Schemas.file) { + app.addRecentDocument(workspace.fsPath); + nEntries++; + } + } else { + app.addRecentDocument(workspace.configPath); + nEntries++; + } } // Take up to maxEntries files - for (let i = 0; i < mru.files.length && i < maxEntries; i++) { + for (let i = 0; i < mru.files.length && nEntries < maxEntries; i++) { const file = mru.files[i]; - app.addRecentDocument(file); + if (file.scheme === Schemas.file) { + app.addRecentDocument(file.fsPath); + nEntries++; + } } } @@ -200,7 +217,7 @@ export class HistoryMainService implements IHistoryMainService { getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened { let workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]; - let files: string[]; + let files: URI[]; // Get from storage const storedRecents = this.getRecentlyOpenedFromStorage(); @@ -219,7 +236,7 @@ export class HistoryMainService implements IHistoryMainService { // Add currently files to open to the beginning if any if (currentFiles) { - files.unshift(...currentFiles.map(f => f.filePath)); + files.unshift(...currentFiles.map(f => f.fileUri)); } // Clear those dupes @@ -232,42 +249,69 @@ export class HistoryMainService implements IHistoryMainService { return { workspaces, files }; } - private distinctFn(workspaceOrFile: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string): string { - if (isSingleFolderWorkspaceIdentifier(workspaceOrFile)) { + private distinctFn(workspaceOrFile: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): string { + if (workspaceOrFile instanceof URI) { return getComparisonKey(workspaceOrFile); } - if (typeof workspaceOrFile === 'string') { - return isLinux ? workspaceOrFile : workspaceOrFile.toLowerCase(); - } - return workspaceOrFile.id; } private getRecentlyOpenedFromStorage(): IRecentlyOpened { - const storedRecents: ISerializedRecentlyOpened = this.stateService.getItem<ISerializedRecentlyOpened>(HistoryMainService.recentlyOpenedStorageKey) || { workspaces: [], files: [] }; - const result: IRecentlyOpened = { workspaces: [], files: storedRecents.files }; - 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 { - result.workspaces.push(URI.revive(workspace)); + const storedRecents = this.stateService.getItem<ISerializedRecentlyOpened & ILegacySerializedRecentlyOpened>(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@martin 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.files2)) { + for (const file of storedRecents.files2) { + if (typeof file === 'string') { + result.files.push(URI.parse(file)); + } + } + } else if (Array.isArray(storedRecents.files)) { + for (const file of storedRecents.files) { + if (typeof file === 'string') { + result.files.push(URI.file(file)); + } + } } } return result; } private saveRecentlyOpened(recent: IRecentlyOpened): void { - const serialized: ISerializedRecentlyOpened = { workspaces: [], files: recent.files }; + const serialized: ISerializedRecentlyOpened = { workspaces2: [], files2: [] }; for (const workspace of recent.workspaces) { if (isSingleFolderWorkspaceIdentifier(workspace)) { - serialized.workspaces.push(<UriComponents>workspace.toJSON()); + serialized.workspaces2.push(workspace.toString()); } else { - serialized.workspaces.push(workspace); + serialized.workspaces2.push(workspace); } } - this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, recent); + for (const file of recent.files) { + serialized.files2.push(file.toString()); + } + this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, serialized); } updateWindowsJumpList(): void { @@ -300,27 +344,38 @@ export class HistoryMainService implements IHistoryMainService { // so we need to update our list of recent paths with the choice of the user to not add them again // Also: Windows will not show our custom category at all if there is any entry which was removed // by the user! See https://github.com/Microsoft/vscode/issues/15052 - this.removeFromRecentlyOpened(app.getJumpListSettings().removedItems.filter(r => !!r.args).map(r => trim(r.args, '"'))); + let toRemove: (ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier)[] = []; + for (let item of app.getJumpListSettings().removedItems) { + const args = item.args; + if (args) { + const match = /^--folder-uri\s+"([^"]+)"$/.exec(args); + if (match) { + if (args[0] === '-') { + toRemove.push(URI.parse(match[1])); + } else { + let configPath = match[1]; + toRemove.push({ id: this.workspacesMainService.getWorkspaceId(configPath), configPath }); + } + } + } + } + this.removeFromRecentlyOpened(toRemove); // Add entries jumpList.push({ type: 'custom', name: nls.localize('recentFolders', "Recent Workspaces"), items: this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(workspace => { - const title = getWorkspaceLabel(workspace, this.environmentService); - const description = isSingleFolderWorkspaceIdentifier(workspace) ? nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(path.dirname(workspace.path), this.environmentService)) : nls.localize('codeWorkspace', "Code Workspace"); + const title = this.labelService.getWorkspaceLabel(workspace); + let description; 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}"`; - } + description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.labelService.getUriLabel(dirname(workspace))); + args = `--folder-uri "${workspace.toString()}"`; } else { + description = nls.localize('codeWorkspace', "Code Workspace"); args = `"${workspace.configPath}"`; } - return <Electron.JumpListItem>{ type: 'task', title, diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 46d378dc8d4..fef600c10f5 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -74,6 +74,7 @@ export interface ProcessExplorerStyles extends WindowStyles { } export interface ProcessExplorerData extends WindowData { + pid: number; styles: ProcessExplorerStyles; } diff --git a/src/vs/platform/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueService.ts index cebb4e61237..475deedd92d 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueService.ts @@ -10,7 +10,7 @@ import { localize } from 'vs/nls'; import * as objects from 'vs/base/common/objects'; import { parseArgs } from 'vs/platform/environment/node/argv'; import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/common/issue'; -import { BrowserWindow, ipcMain, screen } from 'electron'; +import { BrowserWindow, ipcMain, screen, Event } from 'electron'; import { ILaunchService } from 'vs/code/electron-main/launch'; import { getPerformanceInfo, PerformanceInfo, getSystemInfo, SystemInfo } from 'vs/code/electron-main/diagnostics'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -34,22 +34,28 @@ export class IssueService implements IIssueService { ) { } openReporter(data: IssueReporterData): TPromise<void> { - ipcMain.on('issueSystemInfoRequest', event => { + ipcMain.on('vscode:issueSystemInfoRequest', (event: Event) => { this.getSystemInformation().then(msg => { - event.sender.send('issueSystemInfoResponse', msg); + event.sender.send('vscode:issueSystemInfoResponse', msg); }); }); - ipcMain.on('issuePerformanceInfoRequest', event => { + ipcMain.on('vscode:issuePerformanceInfoRequest', (event: Event) => { this.getPerformanceInfo().then(msg => { - event.sender.send('issuePerformanceInfoResponse', msg); + event.sender.send('vscode:issuePerformanceInfoResponse', msg); }); }); - ipcMain.on('workbenchCommand', (event, arg) => { + ipcMain.on('vscode:workbenchCommand', (event, arg) => { this._issueParentWindow.webContents.send('vscode:runAction', { id: arg, from: 'issueReporter' }); }); + ipcMain.on('vscode:closeIssueReporter', (event: Event) => { + if (this._issueWindow) { + this._issueWindow.close(); + } + }); + this._issueParentWindow = BrowserWindow.getFocusedWindow(); const position = this.getWindowPosition(this._issueParentWindow, 700, 800); if (!this._issueWindow) { @@ -91,9 +97,9 @@ export class IssueService implements IIssueService { } openProcessExplorer(data: ProcessExplorerData): TPromise<void> { - ipcMain.on('windowsInfoRequest', event => { + ipcMain.on('windowsInfoRequest', (event: Event) => { this.launchService.getMainProcessInfo().then(info => { - event.sender.send('windowsInfoResponse', info.windows); + event.sender.send('vscode:windowsInfoResponse', info.windows); }); }); diff --git a/src/vs/platform/issue/common/issueIpc.ts b/src/vs/platform/issue/node/issueIpc.ts similarity index 95% rename from src/vs/platform/issue/common/issueIpc.ts rename to src/vs/platform/issue/node/issueIpc.ts index d45c1d870ee..e58dff508b8 100644 --- a/src/vs/platform/issue/common/issueIpc.ts +++ b/src/vs/platform/issue/node/issueIpc.ts @@ -6,8 +6,8 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IIssueService, IssueReporterData, ProcessExplorerData } from './issue'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IIssueService, IssueReporterData, ProcessExplorerData } from '../common/issue'; import { Event } from 'vs/base/common/event'; export interface IIssueChannel extends IChannel { 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/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/label/common/label.ts b/src/vs/platform/label/common/label.ts new file mode 100644 index 00000000000..b5f776ea950 --- /dev/null +++ b/src/vs/platform/label/common/label.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * 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, IWorkspace } 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, getPathLabel } from 'vs/base/common/labels'; +import { ltrim } from 'vs/base/common/strings'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION, toWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { localize } from 'vs/nls'; +import { isParent } from 'vs/platform/files/common/files'; +import { basename, dirname, join } from 'vs/base/common/paths'; + +export interface ILabelService { + _serviceBrand: any; + getUriLabel(resource: URI, relative?: boolean, forceNoTildify?: boolean): string; + getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; + registerFormatter(schema: string, formatter: LabelRules): IDisposable; + onDidRegisterFormatter: Event<{ scheme: string, formatter: LabelRules }>; +} + +export interface LabelRules { + uri: { + label: string; // myLabel:/${path} + separator: '/' | '\\' | ''; + tildify?: boolean; + normalizeDriveLetter?: boolean; + }; + workspace?: { + suffix: string; + }; +} + +const LABEL_SERVICE_ID = 'label'; +const sepRegexp = /\//g; +const labelMatchingRegexp = /\$\{scheme\}|\$\{authority\}|\$\{path\}/g; + +function hasDriveLetter(path: string): boolean { + return isWindows && path && path[2] === ':'; +} + +export class LabelService implements ILabelService { + _serviceBrand: any; + + private readonly formatters = new Map<string, LabelRules>(); + private readonly _onDidRegisterFormatter = new Emitter<{ scheme: string, formatter: LabelRules }>(); + + constructor( + @IEnvironmentService private environmentService: IEnvironmentService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { } + + get onDidRegisterFormatter(): Event<{ scheme: string, formatter: LabelRules }> { + return this._onDidRegisterFormatter.event; + } + + getUriLabel(resource: URI, relative?: boolean, forceNoTildify?: boolean): string { + if (!resource) { + return undefined; + } + const formatter = this.formatters.get(resource.scheme); + if (!formatter) { + return getPathLabel(resource.path, this.environmentService, relative ? this.contextService : undefined); + } + + 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, formatter, forceNoTildify); + relativeLabel = ltrim(this.formatUri(resource, formatter, forceNoTildify).substring(baseResourceLabel.length), formatter.uri.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, formatter, forceNoTildify); + } + + getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string { + if (!isWorkspaceIdentifier(workspace) && !isSingleFolderWorkspaceIdentifier(workspace)) { + workspace = toWorkspaceIdentifier(workspace); + if (!workspace) { + return ''; + } + } + + // Workspace: Single Folder + if (isSingleFolderWorkspaceIdentifier(workspace)) { + // Folder on disk + const formatter = this.formatters.get(workspace.scheme); + const label = options && options.verbose ? this.getUriLabel(workspace) : basenameOrAuthority(workspace); + return formatter && formatter.workspace && formatter.workspace.suffix ? `${label} (${formatter.workspace.suffix})` : label; + } + + // Workspace: Untitled + if (isParent(workspace.configPath, this.environmentService.workspacesHome, !isLinux /* ignore case */)) { + return localize('untitledWorkspace', "Untitled (Workspace)"); + } + + // Workspace: Saved + 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)", this.getUriLabel(URI.file(join(dirname(workspace.configPath), workspaceName)))); + } + + return localize('workspaceName', "{0} (Workspace)", workspaceName); + } + + registerFormatter(scheme: string, formatter: LabelRules): IDisposable { + this.formatters.set(scheme, formatter); + this._onDidRegisterFormatter.fire({ scheme, formatter }); + + return { + dispose: () => this.formatters.delete(scheme) + }; + } + + private formatUri(resource: URI, formatter: LabelRules, forceNoTildify: boolean): string { + let label = formatter.uri.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 (formatter.uri.normalizeDriveLetter && hasDriveLetter(label)) { + label = label.charAt(1).toUpperCase() + label.substr(2); + } + + if (formatter.uri.tildify && !forceNoTildify) { + label = tildify(label, this.environmentService.userHome); + } + + return label.replace(sepRegexp, formatter.uri.separator); + } +} + +export const ILabelService = createDecorator<ILabelService>(LABEL_SERVICE_ID); diff --git a/src/vs/platform/label/electron-browser/label.contribution.ts b/src/vs/platform/label/electron-browser/label.contribution.ts new file mode 100644 index 00000000000..748d595cb70 --- /dev/null +++ b/src/vs/platform/label/electron-browser/label.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 { ILabelService } from 'vs/platform/label/common/label'; +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 LabelRegistrationContribution implements IWorkbenchContribution { + + constructor(@ILabelService labelService: ILabelService) { + labelService.onDidRegisterFormatter(data => { + ipc.send('vscode:labelRegisterFormater', data); + }); + } +} + +Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LabelRegistrationContribution, LifecyclePhase.Starting); diff --git a/src/vs/platform/label/test/label.test.ts b/src/vs/platform/label/test/label.test.ts new file mode 100644 index 00000000000..cae23fdbc44 --- /dev/null +++ b/src/vs/platform/label/test/label.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { LabelService } from 'vs/platform/label/common/label'; +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 Label', () => { + + let labelService: LabelService; + + setup(() => { + labelService = new LabelService(TestEnvironmentService, new TestContextService()); + }); + + test('file scheme', function () { + labelService.registerFormatter(Schemas.file, { + uri: { + 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(labelService.getUriLabel(uri1, true), isWindows ? 'a\\b\\c\\d' : 'a/b/c/d'); + assert.equal(labelService.getUriLabel(uri1, false), isWindows ? 'C:\\testWorkspace\\a\\b\\c\\d' : '/testWorkspace/a/b/c/d'); + + const uri2 = URI.file('c:\\1/2/3'); + assert.equal(labelService.getUriLabel(uri2, false), isWindows ? 'C:\\1\\2\\3' : '/c:\\1/2/3'); + }); + + test('custom scheme', function () { + labelService.registerFormatter(Schemas.vscode, { + uri: { + label: 'LABEL/${path}/${authority}/END', + separator: '/', + tildify: true, + normalizeDriveLetter: true + } + }); + + const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); + assert.equal(labelService.getUriLabel(uri1, false), 'LABEL//1/2/3/4/5/microsoft.com/END'); + }); +}); diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 3a92380ea5f..3dda9ff7a05 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -7,6 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { isThenable } from 'vs/base/common/async'; export const ILifecycleService = createDecorator<ILifecycleService>('lifecycleService'); @@ -23,7 +24,7 @@ export interface ShutdownEvent { /** * Allows to veto the shutdown. The veto can be a long running operation. */ - veto(value: boolean | TPromise<boolean>): void; + veto(value: boolean | Thenable<boolean>): void; /** * The reason why Code is shutting down. @@ -108,12 +109,12 @@ export const NullLifecycleService: ILifecycleService = { }; // Shared veto handling across main and renderer -export function handleVetos(vetos: (boolean | TPromise<boolean>)[], onError: (error: Error) => void): TPromise<boolean /* veto */> { +export function handleVetos(vetos: (boolean | Thenable<boolean>)[], onError: (error: Error) => void): TPromise<boolean /* veto */> { if (vetos.length === 0) { return TPromise.as(false); } - const promises: TPromise<void>[] = []; + const promises: Thenable<void>[] = []; let lazyValue = false; for (let valueOrPromise of vetos) { @@ -123,7 +124,7 @@ export function handleVetos(vetos: (boolean | TPromise<boolean>)[], onError: (er return TPromise.as(true); } - if (TPromise.is(valueOrPromise)) { + if (isThenable(valueOrPromise)) { promises.push(valueOrPromise.then(value => { if (value) { lazyValue = true; // veto, done diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index 34c1b8e99a9..08e31b2ad05 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -83,7 +83,7 @@ export class LifecycleService implements ILifecycleService { } private onBeforeUnload(reason: ShutdownReason): TPromise<boolean> { - const vetos: (boolean | TPromise<boolean>)[] = []; + const vetos: (boolean | Thenable<boolean>)[] = []; this._onWillShutdown.fire({ veto(value) { diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 9bd6cae4587..cc9318c6dcf 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -4,33 +4,35 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { ITree, ITreeConfiguration, ITreeOptions, IRenderer as ITreeRenderer } from 'vs/base/parts/tree/browser/tree'; -import { List, IListOptions, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, IMultipleSelectionController, IOpenController, DefaultStyleController } from 'vs/base/browser/ui/list/listWidget'; -import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IDisposable, toDisposable, combinedDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { IContextKeyService, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { PagedList, IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; -import { IVirtualDelegate, IRenderer, IListMouseEvent, IListTouchEvent } from 'vs/base/browser/ui/list/list'; +import { addClass, addStandardDisposableListener, createStyleSheet, getTotalHeight, removeClass } from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IListMouseEvent, IListTouchEvent, IRenderer, IVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IPagedRenderer, PagedList } from 'vs/base/browser/ui/list/listPaging'; +import { DefaultStyleController, IListOptions, IMultipleSelectionController, IOpenController, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent, List } from 'vs/base/browser/ui/list/listWidget'; +import { canceled, onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { combinedDisposable, Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { isUndefinedOrNull } from 'vs/base/common/types'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IFilter, ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; +import { ClickBehavior, DefaultController, DefaultTreestyler, IControllerOptions, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; -import { attachListStyler, defaultListStyles, computeStyles, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { attachInputBoxStyler, attachListStyler, computeStyles, defaultListStyles } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { localize } from 'vs/nls'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { DefaultController, IControllerOptions, OpenMode, ClickBehavior, DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults'; -import { isUndefinedOrNull } from 'vs/base/common/types'; -import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { Event, Emitter } from 'vs/base/common/event'; -import { createStyleSheet, addStandardDisposableListener, getTotalHeight, removeClass, addClass } from 'vs/base/browser/dom'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -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'; export type ListWidget = List<any> | PagedList<any> | ITree; @@ -561,16 +563,17 @@ export class TreeResourceNavigator extends Disposable { } } - -export interface IHighlightingRenderer extends ITreeRenderer { - /** - * Update hightlights and return the best matching element - */ - updateHighlights(tree: ITree, pattern: string): any; +export interface IHighlighter { + getHighlights(tree: ITree, element: any, pattern: string): FuzzyScore; + getHighlightsStorageKey?(element: any): any; } export interface IHighlightingTreeConfiguration extends ITreeConfiguration { - renderer: IHighlightingRenderer & ITreeRenderer; + highlighter: IHighlighter; +} + +export interface IHighlightingTreeOptions extends ITreeOptions { + filterOnType?: boolean; } export class HighlightingTreeController extends WorkbenchTreeController { @@ -578,7 +581,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); } @@ -591,16 +595,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; } @@ -608,18 +603,54 @@ export class HighlightingTreeController extends WorkbenchTreeController { } } +class HightlightsFilter implements IFilter { + + static add(config: ITreeConfiguration, options: IHighlightingTreeOptions): ITreeConfiguration { + let myFilter = new HightlightsFilter(); + myFilter.enabled = options.filterOnType; + if (!config.filter) { + config.filter = myFilter; + } else { + let otherFilter = config.filter; + config.filter = { + isVisible(tree: ITree, element: any): boolean { + return myFilter.isVisible(tree, element) && otherFilter.isVisible(tree, element); + } + }; + } + return config; + } + + enabled: boolean = true; + + isVisible(tree: ITree, element: any): boolean { + if (!this.enabled) { + return true; + } + let tree2 = (tree as HighlightingWorkbenchTree); + if (!tree2.isHighlighterScoring()) { + return true; + } + if (tree2.getHighlighterScore(element)) { + return true; + } + return false; + } +} + export class HighlightingWorkbenchTree extends WorkbenchTree { protected readonly domNode: HTMLElement; protected readonly inputContainer: HTMLElement; protected readonly input: InputBox; - protected readonly renderer: IHighlightingRenderer; + protected readonly highlighter: IHighlighter; + protected readonly highlights: Map<any, FuzzyScore>; constructor( parent: HTMLElement, treeConfiguration: IHighlightingTreeConfiguration, - treeOptions: ITreeOptions, + treeOptions: IHighlightingTreeOptions, listOptions: IInputOptions, @IContextKeyService contextKeyService: IContextKeyService, @IContextViewService contextViewService: IContextViewService, @@ -641,8 +672,9 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { // create tree treeConfiguration.controller = treeConfiguration.controller || instantiationService.createInstance(HighlightingTreeController, {}, () => this.onTypeInTree()); - super(treeContainer, treeConfiguration, treeOptions, contextKeyService, listService, themeService, instantiationService, configurationService); - this.renderer = treeConfiguration.renderer; + super(treeContainer, HightlightsFilter.add(treeConfiguration, treeOptions), treeOptions, contextKeyService, listService, themeService, instantiationService, configurationService); + this.highlighter = treeConfiguration.highlighter; + this.highlights = new Map<any, FuzzyScore>(); this.domNode = container; addClass(this.domNode, 'inactive'); @@ -657,17 +689,20 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { this.disposables.push(addStandardDisposableListener(this.input.inputElement, 'keydown', event => { //todo@joh make this command/context-key based switch (event.keyCode) { - case KeyCode.DownArrow: case KeyCode.UpArrow: + case KeyCode.DownArrow: + case KeyCode.Tab: this.domFocus(); + event.preventDefault(); break; case KeyCode.Enter: - case KeyCode.Tab: this.setSelection(this.getSelection()); + event.preventDefault(); break; case KeyCode.Escape: this.input.value = ''; this.domFocus(); + event.preventDefault(); break; } })); @@ -701,26 +736,57 @@ 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); + let topElement: any; + if (pattern) { + let nav = this.getNavigator(undefined, false); + let topScore: FuzzyScore; + while (nav.next()) { + let element = nav.current(); + let score = this.highlighter.getHighlights(this, element, pattern); + this.highlights.set(this._getHighlightsStorageKey(element), score); + element.foo = 1; + if (!topScore || score && topScore[0] < score[0]) { + topScore = score; + topElement = element; + } + } } else { - this.setSelection(defaultSelection, this); - this.refresh().then(undefined, onUnexpectedError); + // no pattern, clear highlights + this.highlights.clear(); } + + this.refresh().then(() => { + if (topElement) { + this.reveal(topElement, .5).then(_ => { + this.setSelection([topElement], this); + this.setFocus(topElement, this); + }); + } else { + this.setSelection(defaultSelection, this); + } + }, onUnexpectedError); + } + + isHighlighterScoring(): boolean { + return this.highlights.size > 0; + } + + getHighlighterScore(element: any): FuzzyScore { + return this.highlights.get(this._getHighlightsStorageKey(element)); + } + + private _getHighlightsStorageKey(element: any): any { + return typeof this.highlighter.getHighlightsStorageKey === 'function' + ? this.highlighter.getHighlightsStorageKey(element) + : element; } } @@ -746,20 +812,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/localizations/common/localizationsIpc.ts b/src/vs/platform/localizations/node/localizationsIpc.ts similarity index 96% rename from src/vs/platform/localizations/common/localizationsIpc.ts rename to src/vs/platform/localizations/node/localizationsIpc.ts index 2f06fb819ac..386f26d66e5 100644 --- a/src/vs/platform/localizations/common/localizationsIpc.ts +++ b/src/vs/platform/localizations/node/localizationsIpc.ts @@ -6,7 +6,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { Event, buffer } from 'vs/base/common/event'; import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/node/logIpc.ts similarity index 97% rename from src/vs/platform/log/common/logIpc.ts rename to src/vs/platform/log/node/logIpc.ts index 24bccff9463..ca2cb87d67c 100644 --- a/src/vs/platform/log/common/logIpc.ts +++ b/src/vs/platform/log/node/logIpc.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { TPromise } from 'vs/base/common/winjs.base'; import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log'; import { Event, buffer } from 'vs/base/common/event'; 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<IMenubarService>('menubarService' export interface IMenubarService { _serviceBrand: any; - updateMenubar(windowId: number, menus: IMenubarData): TPromise<void>; + updateMenubar(windowId: number, menus: IMenubarData, additionalKeybindings?: Array<IMenubarKeybinding>): TPromise<void>; } export interface IMenubarData { @@ -23,7 +23,13 @@ export interface IMenubarData { } export interface IMenubarMenu { - items: Array<IMenubarMenuItemAction | IMenubarMenuItemSeparator>; + items: Array<MenubarMenuItem>; +} + +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 (<IMenubarMenuItemSubmenu>menuItem).submenu !== undefined; +} + +export function isMenubarMenuItemAction(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemAction { + return (<IMenubarMenuItemAction>menuItem).checked !== undefined || (<IMenubarMenuItemAction>menuItem).enabled !== undefined; +} + +export function isMenubarMenuItemSeparator(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemSeparator { + return (<IMenubarMenuItemSeparator>menuItem).id === 'vscode.menubar.separator'; } \ 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<void> { + updateMenubar(windowId: number, menus: IMenubarData, additionalKeybindings?: Array<IMenubarKeybinding>): TPromise<void> { 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/menubar/common/menubarIpc.ts b/src/vs/platform/menubar/node/menubarIpc.ts similarity index 74% rename from src/vs/platform/menubar/common/menubarIpc.ts rename to src/vs/platform/menubar/node/menubarIpc.ts index 13699176ef5..c75535de80c 100644 --- a/src/vs/platform/menubar/common/menubarIpc.ts +++ b/src/vs/platform/menubar/node/menubarIpc.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { 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<any> { 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<void> { - return this.channel.call('updateMenubar', [windowId, menus]); + updateMenubar(windowId: number, menus: IMenubarData, additionalKeybindings?: Array<IMenubarKeybinding>): TPromise<void> { + return this.channel.call('updateMenubar', [windowId, menus, additionalKeybindings]); } } \ No newline at end of file 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/quickOpen/common/quickOpen.ts b/src/vs/platform/quickOpen/common/quickOpen.ts index 6a9b847fc09..b0b91b9e1b4 100644 --- a/src/vs/platform/quickOpen/common/quickOpen.ts +++ b/src/vs/platform/quickOpen/common/quickOpen.ts @@ -5,94 +5,9 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import uri from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { IQuickNavigateConfiguration, IAutoFocus, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; +import { IQuickNavigateConfiguration, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IAction } from 'vs/base/common/actions'; -import { FileKind } from 'vs/platform/files/common/files'; - -export interface IFilePickOpenEntry extends IPickOpenEntry { - resource: uri; - fileKind?: FileKind; -} - -export interface IPickOpenAction extends IAction { - run(item: IPickOpenItem): TPromise<any>; -} - -export interface IPickOpenEntry { - id?: string; - label: string; - description?: string; - detail?: string; - tooltip?: string; - separator?: ISeparator; - alwaysShow?: boolean; - run?: (context: IEntryRunContext) => void; - action?: IAction; - payload?: any; -} - -export interface IPickOpenItem { - index: number; - remove: () => void; - getId: () => string; - getResource: () => uri; - getPayload: () => any; -} - -export interface ISeparator { - border?: boolean; - label?: string; -} - -export interface IPickOptions { - - /** - * an optional string to show as place holder in the input box to guide the user what she picks on - */ - placeHolder?: string; - - /** - * optional auto focus settings - */ - autoFocus?: IAutoFocus; - - /** - * an optional flag to include the description when filtering the picks - */ - matchOnDescription?: boolean; - - /** - * an optional flag to include the detail when filtering the picks - */ - matchOnDetail?: boolean; - - /** - * an optional flag to not close the picker on focus lost - */ - ignoreFocusLost?: boolean; - - /** - * enables quick navigate in the picker to open an element without typing - */ - quickNavigateConfiguration?: IQuickNavigateConfiguration; - - /** - * a context key to set when this picker is active - */ - contextKey?: string; -} - -export interface IStringPickOptions extends IPickOptions { - onDidFocus?: (item: string) => void; -} - -export interface ITypedPickOptions<T extends IPickOpenEntry> extends IPickOptions { - onDidFocus?: (entry: T) => void; -} export interface IShowOptions { quickNavigateConfiguration?: IQuickNavigateConfiguration; @@ -115,18 +30,6 @@ export interface IQuickOpenService { */ show(prefix?: string, options?: IShowOptions): TPromise<void>; - /** - * A convenient way to bring up quick open as a picker with custom elements. This bypasses the quick open handler - * registry and just leverages the quick open widget to select any kind of entries. - * - * Passing in a promise will allow you to resolve the elements in the background while quick open will show a - * progress bar spinning. - */ - pick(picks: TPromise<string[]>, options?: IStringPickOptions, token?: CancellationToken): TPromise<string>; - pick<T extends IPickOpenEntry>(picks: TPromise<T[]>, options?: ITypedPickOptions<T>, token?: CancellationToken): TPromise<T>; - pick(picks: string[], options?: IStringPickOptions, token?: CancellationToken): TPromise<string>; - pick<T extends IPickOpenEntry>(picks: T[], options?: ITypedPickOptions<T>, token?: CancellationToken): TPromise<T>; - /** * Allows to navigate from the outside in an opened picker. */ diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index 0ec12af97a1..b384addd509 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -12,11 +12,25 @@ import URI from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; export interface IQuickPickItem { + type?: 'item'; id?: string; label: string; description?: string; detail?: string; + iconClasses?: string[]; + buttons?: IQuickInputButton[]; picked?: boolean; + alwaysShow?: boolean; +} + +export interface IQuickPickSeparator { + type: 'separator'; + label?: string; +} + +export interface IKeyMods { + readonly ctrlCmd: boolean; + readonly alt: boolean; } export interface IQuickNavigateConfiguration { @@ -50,7 +64,24 @@ export interface IPickOptions<T extends IQuickPickItem> { */ 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> | T; + + onKeyMods?: (keyMods: IKeyMods) => void; onDidFocus?: (entry: T) => void; + onDidTriggerItemButton?: (context: IQuickPickItemButtonContext<T>) => void; } export interface IInputOptions { @@ -98,6 +129,8 @@ export interface IQuickInput { enabled: boolean; + contextKey: string | undefined; + busy: boolean; ignoreFocusOut: boolean; @@ -125,7 +158,9 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput { readonly onDidTriggerButton: Event<IQuickInputButton>; - items: ReadonlyArray<T>; + readonly onDidTriggerItemButton: Event<IQuickPickItemButtonEvent<T>>; + + items: ReadonlyArray<T | IQuickPickSeparator>; canSelectMany: boolean; @@ -133,6 +168,8 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput { matchOnDetail: boolean; + quickNavigate: IQuickNavigateConfiguration | undefined; + activeItems: ReadonlyArray<T>; readonly onDidChangeActive: Event<T[]>; @@ -140,6 +177,8 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput { selectedItems: ReadonlyArray<T>; readonly onDidChangeSelection: Event<T[]>; + + readonly keyMods: IKeyMods; } export interface IInputBox extends IQuickInput { @@ -166,12 +205,26 @@ 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<T extends IQuickPickItem> { + button: IQuickInputButton; + item: T; +} + +export interface IQuickPickItemButtonContext<T extends IQuickPickItem> extends IQuickPickItemButtonEvent<T> { + removeItem(): void; } export const IQuickInputService = createDecorator<IQuickInputService>('quickInputService'); +export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; + +export type QuickPickInput<T = IQuickPickItem> = T | IQuickPickSeparator; + export interface IQuickInputService { _serviceBrand: any; @@ -179,7 +232,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<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: TPromise<T[]>, options?: O, token?: CancellationToken): TPromise<O extends { canPickMany: true } ? T[] : T>; + pick<T extends IQuickPickItem>(picks: TPromise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: true }, token?: CancellationToken): TPromise<T[]>; + pick<T extends IQuickPickItem>(picks: TPromise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: IPickOptions<T> & { canPickMany: false }, token?: CancellationToken): TPromise<T>; + pick<T extends IQuickPickItem>(picks: TPromise<QuickPickInput<T>[]> | QuickPickInput<T>[], options?: Omit<IPickOptions<T>, 'canPickMany'>, token?: CancellationToken): TPromise<T>; /** * 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/search/common/search.ts b/src/vs/platform/search/common/search.ts index 7cacfe626a9..b27650c5057 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -27,7 +27,7 @@ export interface ISearchService { search(query: ISearchQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise<ISearchComplete>; extendQuery(query: ISearchQuery): void; clearCache(cacheKey: string): TPromise<void>; - registerSearchResultProvider(scheme: string, provider: ISearchResultProvider): IDisposable; + registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; } export interface ISearchHistoryValues { @@ -45,6 +45,15 @@ 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, onProgress?: (p: ISearchProgressItem) => void): TPromise<ISearchComplete>; clearCache(cacheKey: string): TPromise<void>; @@ -76,6 +85,7 @@ export interface ICommonQueryOptions<U> { disregardExcludeSettings?: boolean; ignoreSymlinks?: boolean; maxFileSize?: number; + previewOptions?: ITextSearchPreviewOptions; } export interface IQueryOptions extends ICommonQueryOptions<uri> { @@ -123,15 +133,33 @@ export interface IPatternInfo { export interface IFileMatch<U extends UriComponents = uri> { resource?: U; - lineMatches?: ILineMatch[]; + matches?: ITextSearchResult[]; } export type IRawFileMatch2 = IFileMatch<UriComponents>; -export interface ILineMatch { - preview: string; - lineNumber: number; - offsetAndLengths: number[][]; +export interface ITextSearchPreviewOptions { + maxLines: number; + leadingChars: number; + totalChars: number; +} + +export interface ISearchRange { + readonly startLineNumber: number; + readonly startColumn: number; + readonly endLineNumber: number; + readonly endColumn: number; +} + +export interface ITextSearchResultPreview { + text: string; + match: ISearchRange; +} + +export interface ITextSearchResult { + uri?: uri; + range: ISearchRange; + preview: ITextSearchResultPreview; } export interface IProgress { @@ -146,53 +174,96 @@ export interface ISearchProgressItem extends IFileMatch, IProgress { export interface ISearchCompleteStats { limitHit?: boolean; - stats?: ISearchStats; + stats?: IFileSearchStats | ITextSearchStats; } export interface ISearchComplete extends ISearchCompleteStats { results: IFileMatch[]; } -export interface ISearchStats { +export interface ITextSearchStats { + type: 'textSearchProvider' | 'searchProcess'; +} + +export interface IFileSearchStats { fromCache: boolean; + detailStats: ISearchEngineStats | ICachedSearchStats | IFileSearchProviderStats | IFileIndexProviderStats; + resultCount: number; - unsortedResultTime?: number; - sortedResultTime?: number; + type: 'fileIndexProver' | 'fileSearchProvider' | 'searchProcess'; + sortingTime?: number; } -export interface ICachedSearchStats extends ISearchStats { - cacheLookupStartTime: number; - cacheFilterStartTime: number; - cacheLookupResultTime: number; +export interface ICachedSearchStats { + cacheWasResolved: boolean; + cacheLookupTime: number; + cacheFilterTime: number; cacheEntryCount: number; - joined?: ISearchStats; } -export interface IUncachedSearchStats extends ISearchStats { +export interface ISearchEngineStats { traversal: string; - errors: string[]; - fileWalkStartTime: number; - fileWalkResultTime: number; + fileWalkTime: number; directoriesWalked: number; filesWalked: number; - cmdForkStartTime?: number; - cmdForkResultTime?: number; + cmdTime: number; cmdResultCount?: number; } +export interface IFileSearchProviderStats { + providerTime: number; + postProcessTime: number; +} -// ---- very simple implementation of the search model -------------------- +export interface IFileIndexProviderStats { + providerTime: number; + providerResultCount: number; + fileWalkTime: number; + directoriesWalked: number; + filesWalked: number; +} export class FileMatch implements IFileMatch { - public lineMatches: LineMatch[] = []; + public matches: ITextSearchResult[] = []; constructor(public resource: uri) { // empty } } -export class LineMatch implements ILineMatch { - constructor(public preview: string, public lineNumber: number, public offsetAndLengths: number[][]) { - // empty +export class TextSearchResult implements ITextSearchResult { + range: ISearchRange; + preview: ITextSearchResultPreview; + + constructor(fullLine: string, range: ISearchRange, previewOptions?: ITextSearchPreviewOptions) { + this.range = range; + if (previewOptions) { + const previewStart = Math.max(range.startColumn - previewOptions.leadingChars, 0); + const previewEnd = Math.max(previewOptions.totalChars + previewStart, range.endColumn); + + this.preview = { + text: fullLine.substring(previewStart, previewEnd), + match: new OneLineRange(0, range.startColumn - previewStart, range.endColumn - previewStart) + }; + } else { + this.preview = { + text: fullLine, + match: new OneLineRange(0, range.startColumn, range.endColumn) + }; + } + } +} + +export class OneLineRange implements ISearchRange { + startLineNumber: number; + startColumn: number; + endLineNumber: number; + endColumn: number; + + constructor(lineNumber: number, startColumn: number, endColumn: number) { + this.startLineNumber = lineNumber; + this.startColumn = startColumn; + this.endLineNumber = lineNumber; + this.endColumn = endColumn; } } diff --git a/src/vs/platform/search/test/common/search.test.ts b/src/vs/platform/search/test/common/search.test.ts new file mode 100644 index 00000000000..28fe393b3cd --- /dev/null +++ b/src/vs/platform/search/test/common/search.test.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TextSearchResult, OneLineRange, ITextSearchResult, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; + +suite('TextSearchResult', () => { + + const previewOptions1: ITextSearchPreviewOptions = { + leadingChars: 10, + maxLines: 1, + totalChars: 100 + }; + + test('empty', () => { + assert.deepEqual( + new TextSearchResult('', new OneLineRange(5, 0, 0)), + <ITextSearchResult>{ + preview: { + text: '', + match: new OneLineRange(0, 0, 0) + }, + range: new OneLineRange(5, 0, 0) + }); + + assert.deepEqual( + new TextSearchResult('', new OneLineRange(5, 0, 0), previewOptions1), + <ITextSearchResult>{ + preview: { + text: '', + match: new OneLineRange(0, 0, 0) + }, + range: new OneLineRange(5, 0, 0) + }); + }); + + test('short', () => { + assert.deepEqual( + new TextSearchResult('foo bar', new OneLineRange(5, 4, 7)), + <ITextSearchResult>{ + preview: { + text: 'foo bar', + match: new OneLineRange(0, 4, 7) + }, + range: new OneLineRange(5, 4, 7) + }); + + assert.deepEqual( + new TextSearchResult('foo bar', new OneLineRange(5, 4, 7), previewOptions1), + <ITextSearchResult>{ + preview: { + text: 'foo bar', + match: new OneLineRange(0, 4, 7) + }, + range: new OneLineRange(5, 4, 7) + }); + }); + + test('leading', () => { + assert.deepEqual( + new TextSearchResult('long text very long text foo', new OneLineRange(5, 25, 28), previewOptions1), + <ITextSearchResult>{ + preview: { + text: 'long text foo', + match: new OneLineRange(0, 10, 13) + }, + range: new OneLineRange(5, 25, 28) + }); + }); + + test('trailing', () => { + assert.deepEqual( + new TextSearchResult('foo long text very long text long text very long text long text very long text long text very long text long text very long text', new OneLineRange(5, 0, 3), previewOptions1), + <ITextSearchResult>{ + preview: { + text: 'foo long text very long text long text very long text long text very long text long text very long t', + match: new OneLineRange(0, 0, 3) + }, + range: new OneLineRange(5, 0, 3) + }); + }); + + test('middle', () => { + assert.deepEqual( + new TextSearchResult('long text very long text long foo text very long text long text very long text long text very long text long text very long text', new OneLineRange(5, 30, 33), previewOptions1), + <ITextSearchResult>{ + preview: { + text: 'text long foo text very long text long text very long text long text very long text long text very l', + match: new OneLineRange(0, 10, 13) + }, + range: new OneLineRange(5, 30, 33) + }); + }); +}); \ No newline at end of file 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<IConfigurationRegistry>(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..0bc5f903798 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -113,8 +113,11 @@ const configurationValueWhitelist = [ 'editor.multiCursorModifier', 'editor.quickSuggestions', 'editor.quickSuggestionsDelay', - 'editor.parameterHints', + 'editor.parameterHints.enabled', + 'editor.parameterHints.cycle', 'editor.autoClosingBrackets', + 'editor.autoClosingQuotes', + 'editor.autoWrapping', 'editor.autoIndent', 'editor.formatOnType', 'editor.formatOnPaste', diff --git a/src/vs/platform/telemetry/common/telemetryIpc.ts b/src/vs/platform/telemetry/node/telemetryIpc.ts similarity index 96% rename from src/vs/platform/telemetry/common/telemetryIpc.ts rename to src/vs/platform/telemetry/node/telemetryIpc.ts index f08e0c0969c..4f5de9b6fa9 100644 --- a/src/vs/platform/telemetry/common/telemetryIpc.ts +++ b/src/vs/platform/telemetry/node/telemetryIpc.ts @@ -6,7 +6,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { Event } from 'vs/base/common/event'; 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 5be3b6ef6a1..3aeda392f74 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.")); @@ -191,11 +191,11 @@ 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('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.")); @@ -203,18 +203,18 @@ export const listHoverForeground = registerColor('list.hoverForeground', { dark: 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,11 +223,6 @@ 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 breadcrumbsPickerBackground = registerColor('breadcrumb.breadcrumbsPickerBackground', { light: '#ECECEC', dark: '#252526', hc: Color.black }, nls.localize('breadcrumbsSelectedBackground', "Background color of breadcrumb item picker.")); - /** * Editor background color. * Because of bug https://monacotools.visualstudio.com/DefaultCollection/Monaco/_workitems/edit/13254 @@ -294,6 +289,15 @@ export const diffRemovedOutline = registerColor('diffEditor.removedTextBorder', export const diffBorder = registerColor('diffEditor.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('diffEditorBorder', 'Border color between the two text editors.')); +/** + * Breadcrumb colors + */ +export const breadcrumbsForeground = registerColor('breadcrumb.foreground', { light: transparent(foreground, .8), dark: transparent(foreground, .8), hc: transparent(foreground, .8) }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); +export const breadcrumbsBackground = registerColor('breadcrumb.background', { light: editorBackground, dark: editorBackground, hc: editorBackground }, nls.localize('breadcrumbsBackground', "Background color of breadcrumb items.")); +export const breadcrumbsFocusForeground = registerColor('breadcrumb.focusForeground', { light: darken(foreground, .2), dark: lighten(foreground, .1), hc: lighten(foreground, .1) }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); +export const breadcrumbsActiveSelectionForeground = registerColor('breadcrumb.activeSelectionForeground', { light: darken(foreground, .2), dark: lighten(foreground, .1), hc: lighten(foreground, .1) }, nls.localize('breadcrumbsSelectedForegound', "Color of selected breadcrumb items.")); +export const breadcrumbsPickerBackground = registerColor('breadcrumbPicker.background', { light: editorWidgetBackground, dark: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('breadcrumbsSelectedBackground', "Background color of breadcrumb item picker.")); + /** * Merge-conflict colors */ diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 08c63b30296..50ef1ebe5c6 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, 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, breadcrumbsBackground, editorWidgetBorder } 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'; @@ -129,7 +129,8 @@ export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeSer listFocusOutline: (style && style.listFocusOutline) || activeContrastBorder, listHoverBackground: (style && style.listHoverBackground) || listHoverBackground, listHoverForeground: (style && style.listHoverForeground) || listHoverForeground, - listHoverOutline: (style && style.listFocusOutline) || activeContrastBorder + listHoverOutline: (style && style.listFocusOutline) || activeContrastBorder, + selectListBorder: (style && style.selectListBorder) || editorWidgetBorder } as ISelectBoxStyleOverrides, widget); } @@ -177,7 +178,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 +220,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, @@ -272,7 +273,7 @@ export interface IBreadcrumbsWidgetStyleOverrides extends IStyleOverrides { } export const defaultBreadcrumbsStyles = <IBreadcrumbsWidgetStyleOverrides>{ - breadcrumbsBackground: editorBackground, + breadcrumbsBackground: breadcrumbsBackground, breadcrumbsForeground: breadcrumbsForeground, breadcrumbsHoverForeground: breadcrumbsFocusForeground, breadcrumbsFocusForeground: breadcrumbsFocusForeground, 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.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index 01d80b45ea1..8ac8357ef63 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -52,7 +52,7 @@ export class DarwinUpdateService extends AbstractUpdateService { protected buildUpdateFeedUrl(quality: string): string | undefined { const url = createUpdateURL('darwin', quality); try { - electron.autoUpdater.setFeedURL(url); + electron.autoUpdater.setFeedURL({ url }); } catch (e) { // application is very likely not signed this.logService.error('Failed to set update feed URL', e); 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/update/common/updateIpc.ts b/src/vs/platform/update/node/updateIpc.ts similarity index 95% rename from src/vs/platform/update/common/updateIpc.ts rename to src/vs/platform/update/node/updateIpc.ts index aa2e8fd304e..f671ad3f004 100644 --- a/src/vs/platform/update/common/updateIpc.ts +++ b/src/vs/platform/update/node/updateIpc.ts @@ -6,10 +6,10 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IUpdateService, State } from './update'; +import { IUpdateService, State } from 'vs/platform/update/common/update'; export interface IUpdateChannel extends IChannel { listen(event: 'onStateChange'): Event<State>; diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/node/urlIpc.ts similarity index 92% rename from src/vs/platform/url/common/urlIpc.ts rename to src/vs/platform/url/node/urlIpc.ts index 84fdc5c2e65..e56f4e49260 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/node/urlIpc.ts @@ -6,11 +6,11 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IURLHandler, IURLService } from './url'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import URI from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; export interface IURLServiceChannel extends IChannel { call(command: 'open', url: string): TPromise<boolean>; @@ -39,7 +39,7 @@ export class URLServiceChannelClient implements IURLService { constructor(private channel: IChannel) { } - open(url: URI): TPromise<boolean, any> { + open(url: URI): TPromise<boolean> { return this.channel.call('open', url.toJSON()); } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 269f0bf03d0..eedeaca472b 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -14,7 +14,7 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { PerformanceEntry } from 'vs/base/common/performance'; +import { ExportData } from 'vs/base/common/performance'; import { LogLevel } from 'vs/platform/log/common/log'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import URI, { UriComponents } from 'vs/base/common/uri'; @@ -126,8 +126,8 @@ export interface IWindowsService { saveAndEnterWorkspace(windowId: number, path: string): TPromise<IEnterWorkspaceResult>; toggleFullScreen(windowId: number): TPromise<void>; setRepresentedFilename(windowId: number, fileName: string): TPromise<void>; - addRecentlyOpened(files: string[]): TPromise<void>; - removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void>; + addRecentlyOpened(files: URI[]): TPromise<void>; + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): TPromise<void>; clearRecentlyOpened(): TPromise<void>; getRecentlyOpened(windowId: number): TPromise<IRecentlyOpened>; focusWindow(windowId: number): TPromise<void>; @@ -143,6 +143,7 @@ export interface IWindowsService { relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise<void>; // macOS Native Tabs + newWindowTab(): TPromise<void>; showPreviousWindowTab(): TPromise<void>; showNextWindowTab(): TPromise<void>; moveWindowTabToNewWindow(): TPromise<void>; @@ -173,7 +174,6 @@ export interface IWindowsService { // TODO: this is a bit backwards startCrashReporter(config: CrashReporterStartOptions): TPromise<void>; - openAccessibilityOptions(): TPromise<void>; openAboutDialog(): TPromise<void>; } @@ -292,10 +292,25 @@ export enum ReadyState { READY } -export interface IPath { +export interface IPath extends IPathData { // the file path to open within a Code instance - filePath?: string; + fileUri?: URI; +} + +export interface IPathsToWaitFor extends IPathsToWaitForData { + paths: IPath[]; +} + +export interface IPathsToWaitForData { + paths: IPathData[]; + waitMarkerFilePath: string; +} + +export interface IPathData { + + // the file path to open within a Code instance + fileUri?: UriComponents; // the line number in the file path to open lineNumber?: number; @@ -304,16 +319,11 @@ export interface IPath { columnNumber?: number; } -export interface IPathsToWaitFor { - paths: IPath[]; - waitMarkerFilePath: string; -} - export interface IOpenFileRequest { - filesToOpen?: IPath[]; - filesToCreate?: IPath[]; - filesToDiff?: IPath[]; - filesToWait?: IPathsToWaitFor; + filesToOpen?: IPathData[]; + filesToCreate?: IPathData[]; + filesToDiff?: IPathData[]; + filesToWait?: IPathsToWaitForData; termProgram?: string; } @@ -321,11 +331,13 @@ export interface IAddFoldersRequest { foldersToAdd: UriComponents[]; } -export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest { +export interface IWindowConfiguration extends ParsedArgs { machineId: string; windowId: number; logLevel: LogLevel; + mainPid: number; + appRoot: string; execPath: string; isInitialStartup?: boolean; @@ -342,15 +354,19 @@ export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest { fullscreen?: boolean; maximized?: boolean; highContrast?: boolean; - baseTheme?: string; - backgroundColor?: string; frameless?: boolean; accessibilitySupport?: boolean; - perfEntries: PerformanceEntry[]; perfStartTime?: number; perfAppReady?: number; perfWindowLoadTime?: number; + perfEntries: ExportData; + + filesToOpen?: IPath[]; + filesToCreate?: IPath[]; + filesToDiff?: IPath[]; + filesToWait?: IPathsToWaitFor; + termProgram?: string; } export interface IRunActionInWindowRequest { @@ -391,4 +407,4 @@ export class ActiveWindowManager implements IDisposable { dispose() { this.disposables = dispose(this.disposables); } -} \ No newline at end of file +} diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 1c38b451768..20b7711912c 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -48,6 +48,8 @@ export interface ICodeWindow { readyState: ReadyState; ready(): TPromise<ICodeWindow>; + addTabbedWindow(window: ICodeWindow): void; + load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void; reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void; @@ -110,6 +112,7 @@ export interface IWindowsMainService { getLastActiveWindow(): ICodeWindow; waitForWindowCloseOrLoad(windowId: number): TPromise<void>; openNewWindow(context: OpenContext): ICodeWindow[]; + openNewTabbedWindow(context: OpenContext): ICodeWindow[]; sendToFocused(channel: string, ...args: any[]): void; sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void; getFocusedWindow(): ICodeWindow; @@ -127,6 +130,7 @@ export interface IOpenConfiguration { 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 0e2ace3e04f..491a34db330 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -13,7 +13,7 @@ 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'; @@ -226,14 +226,14 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(null); } - addRecentlyOpened(files: string[]): TPromise<void> { + addRecentlyOpened(files: URI[]): TPromise<void> { this.logService.trace('windowsService#addRecentlyOpened'); this.historyService.addRecentlyOpened(void 0, files); return TPromise.as(null); } - removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void> { + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): TPromise<void> { this.logService.trace('windowsService#removeFromRecentlyOpened'); this.historyService.removeFromRecentlyOpened(paths); @@ -258,6 +258,14 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(this.historyService.getRecentlyOpened()); } + newWindowTab(): TPromise<void> { + this.logService.trace('windowsService#newWindowTab'); + + this.windowsMainService.openNewTabbedWindow(OpenContext.API); + + return TPromise.as(void 0); + } + showPreviousWindowTab(): TPromise<void> { this.logService.trace('windowsService#showPreviousWindowTab'); Menu.sendActionToFirstResponder('selectPreviousTab:'); @@ -413,7 +421,9 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable openNewWindow(): TPromise<void> { this.logService.trace('windowsService#openNewWindow'); + this.windowsMainService.openNewWindow(OpenContext.API); + return TPromise.as(null); } @@ -491,29 +501,6 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(null); } - openAccessibilityOptions(): TPromise<void> { - 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<void> { this.logService.trace('windowsService#openAboutDialog'); const lastActiveWindow = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow(); diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts similarity index 94% rename from src/vs/platform/windows/common/windowsIpc.ts rename to src/vs/platform/windows/node/windowsIpc.ts index 7398b588718..48d66e15864 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -7,12 +7,12 @@ 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 { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, CrashReporterStartOptions, IMessageBoxResult, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IDevToolsOptions } from 'vs/platform/windows/common/windows'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, isSingleFolderWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } 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'; +import URI, { UriComponents } from 'vs/base/common/uri'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; export interface IWindowsChannel extends IChannel { @@ -40,10 +40,11 @@ export interface IWindowsChannel extends IChannel { call(command: 'saveAndEnterWorkspace', arg: [number, string]): TPromise<IEnterWorkspaceResult>; call(command: 'toggleFullScreen', arg: number): TPromise<void>; call(command: 'setRepresentedFilename', arg: [number, string]): TPromise<void>; - call(command: 'addRecentlyOpened', arg: string[]): TPromise<void>; - call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void>; + call(command: 'addRecentlyOpened', arg: UriComponents[]): TPromise<void>; + call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | UriComponents | string)[]): TPromise<void>; call(command: 'clearRecentlyOpened'): TPromise<void>; call(command: 'getRecentlyOpened', arg: number): TPromise<IRecentlyOpened>; + call(command: 'newWindowTab'): TPromise<void>; call(command: 'showPreviousWindowTab'): TPromise<void>; call(command: 'showNextWindowTab'): TPromise<void>; call(command: 'moveWindowTabToNewWindow'): TPromise<void>; @@ -73,7 +74,6 @@ export interface IWindowsChannel extends IChannel { call(command: 'getActiveWindowId'): TPromise<number>; call(command: 'openExternal', arg: string): TPromise<boolean>; call(command: 'startCrashReporter', arg: CrashReporterStartOptions): TPromise<void>; - call(command: 'openAccessibilityOptions'): TPromise<void>; call(command: 'openAboutDialog'): TPromise<void>; } @@ -139,9 +139,16 @@ export class WindowsChannel implements IWindowsChannel { case 'saveAndEnterWorkspace': return this.service.saveAndEnterWorkspace(arg[0], arg[1]); 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(isSingleFolderWorkspaceIdentifier(arg) ? URI.revive(arg) : arg); + case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg.map(URI.revive)); + case 'removeFromRecentlyOpened': { + let paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[] = arg; + if (Array.isArray(paths)) { + paths = paths.map(path => isWorkspaceIdentifier(path) ? 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(); @@ -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; @@ -256,11 +262,11 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('setRepresentedFilename', [windowId, fileName]); } - addRecentlyOpened(files: string[]): TPromise<void> { + addRecentlyOpened(files: URI[]): TPromise<void> { return this.channel.call('addRecentlyOpened', files); } - removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void> { + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI)[]): TPromise<void> { return this.channel.call('removeFromRecentlyOpened', paths); } @@ -272,10 +278,15 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('getRecentlyOpened', windowId) .then(recentlyOpened => { recentlyOpened.workspaces = recentlyOpened.workspaces.map(workspace => isWorkspaceIdentifier(workspace) ? workspace : URI.revive(workspace)); + recentlyOpened.files = recentlyOpened.files.map(URI.revive); return recentlyOpened; }); } + newWindowTab(): TPromise<void> { + return this.channel.call('newWindowTab'); + } + showPreviousWindowTab(): TPromise<void> { return this.channel.call('showPreviousWindowTab'); } @@ -392,10 +403,6 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('updateTouchBar', [windowId, items]); } - openAccessibilityOptions(): TPromise<void> { - return this.channel.call('openAccessibilityOptions'); - } - openAboutDialog(): TPromise<void> { return this.channel.call('openAboutDialog'); } diff --git a/src/vs/platform/workbench/common/contextkeys.ts b/src/vs/platform/workbench/common/contextkeys.ts index c4a740d8c28..d513b3e6c91 100644 --- a/src/vs/platform/workbench/common/contextkeys.ts +++ b/src/vs/platform/workbench/common/contextkeys.ts @@ -6,6 +6,11 @@ 'use strict'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform'; export const InputFocusedContextKey = 'inputFocus'; -export const InputFocusedContext = new RawContextKey<boolean>(InputFocusedContextKey, false); \ No newline at end of file +export const InputFocusedContext = new RawContextKey<boolean>(InputFocusedContextKey, false); +export const FileDialogContext = new RawContextKey<string>('fileDialog', 'local'); +export const IsMacContext = new RawContextKey<boolean>('isMac', isMacintosh); +export const IsLinuxContext = new RawContextKey<boolean>('isLinux', isLinux); +export const IsWindowsContext = new RawContextKey<boolean>('isWindows', isWindows); diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 4d2a8cc88d5..dcb2ca53a05 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -81,7 +81,6 @@ 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); } } @@ -93,11 +92,6 @@ export interface IWorkspace { */ readonly id: string; - /** - * the name of the workspace. - */ - readonly name: string; - /** * Folders in the workspace. */ @@ -151,7 +145,6 @@ export class Workspace implements IWorkspace { constructor( private _id: string, - private _name: string = '', folders: WorkspaceFolder[] = [], private _configuration: URI = null, private _ctime?: number @@ -161,7 +154,6 @@ export class Workspace implements IWorkspace { update(workspace: Workspace) { this._id = workspace.id; - this._name = workspace.name; this._configuration = workspace.configuration; this._ctime = workspace.ctime; this.folders = workspace.folders; @@ -184,14 +176,6 @@ export class Workspace implements IWorkspace { return this._ctime; } - get name(): string { - return this._name; - } - - set name(name: string) { - this._name = name; - } - get configuration(): URI { return this._configuration; } @@ -216,7 +200,7 @@ export class Workspace implements IWorkspace { } toJSON(): IWorkspace { - return { id: this.id, folders: this.folders, name: this.name, configuration: this.configuration }; + return { id: this.id, folders: this.folders, configuration: this.configuration }; } } @@ -234,7 +218,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 { @@ -278,7 +262,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/workspace/test/common/testWorkspace.ts b/src/vs/platform/workspace/test/common/testWorkspace.ts index 6a204873e0c..2fa9cd57e6e 100644 --- a/src/vs/platform/workspace/test/common/testWorkspace.ts +++ b/src/vs/platform/workspace/test/common/testWorkspace.ts @@ -13,7 +13,6 @@ export const TestWorkspace = testWorkspace(wsUri); export function testWorkspace(resource: URI): Workspace { return new Workspace( resource.toString(), - resource.fsPath, toWorkspaceFolders([{ path: resource.fsPath }]) ); -} \ No newline at end of file +} diff --git a/src/vs/platform/workspace/test/common/workspace.test.ts b/src/vs/platform/workspace/test/common/workspace.test.ts index ac6f0caf17f..58f24f3f4e4 100644 --- a/src/vs/platform/workspace/test/common/workspace.test.ts +++ b/src/vs/platform/workspace/test/common/workspace.test.ts @@ -14,7 +14,7 @@ suite('Workspace', () => { test('getFolder returns the folder with given uri', () => { const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 2 }); - let testObject = new Workspace('', '', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); const actual = testObject.getFolder(expected.uri); @@ -23,7 +23,7 @@ suite('Workspace', () => { test('getFolder returns the folder if the uri is sub', () => { const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 0 }); - let testObject = new Workspace('', '', [expected, new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); + let testObject = new Workspace('', [expected, new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); const actual = testObject.getFolder(URI.file('/src/test/a')); @@ -32,7 +32,7 @@ suite('Workspace', () => { test('getFolder returns the closest folder if the uri is sub', () => { const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 2 }); - let testObject = new Workspace('', '', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); const actual = testObject.getFolder(URI.file('/src/test/a')); @@ -40,7 +40,7 @@ suite('Workspace', () => { }); test('getFolder returns null if the uri is not sub', () => { - let testObject = new Workspace('', '', [new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })]); + let testObject = new Workspace('', [new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })]); const actual = testObject.getFolder(URI.file('/src/main/a')); diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 7bfa4235a63..9384c7bd4c1 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -7,16 +7,10 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { TPromise } from 'vs/base/common/winjs.base'; -import { isParent } from 'vs/platform/files/common/files'; import { localize } from 'vs/nls'; -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 { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; -import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import URI from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; export const IWorkspacesMainService = createDecorator<IWorkspacesMainService>('workspacesMainService'); export const IWorkspacesService = createDecorator<IWorkspacesService>('workspacesService'); @@ -112,34 +106,6 @@ export interface IWorkspacesService { createWorkspace(folders?: IWorkspaceFolderCreationData[]): TPromise<IWorkspaceIdentifier>; } -export function getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier), environmentService: IEnvironmentService, options?: { verbose: boolean }): string { - - // Workspace: Single Folder - if (isSingleFolderWorkspaceIdentifier(workspace)) { - // Folder on disk - if (workspace.scheme === Schemas.file) { - return options && options.verbose ? getPathLabel(workspace, environmentService) : getBaseLabel(workspace); - } - - // Remote folder - return options && options.verbose ? getPathLabel(workspace, environmentService) : `${getBaseLabel(workspace)} (${workspace.scheme})`; - } - - // Workspace: Untitled - if (isParent(workspace.configPath, environmentService.workspacesHome, !isLinux /* ignore case */)) { - return localize('untitledWorkspace', "Untitled (Workspace)"); - } - - // Workspace: Saved - 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('workspaceName', "{0} (Workspace)", workspaceName); -} - export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier { return obj instanceof URI; } @@ -149,3 +115,18 @@ export function isWorkspaceIdentifier(obj: any): obj is IWorkspaceIdentifier { return workspaceIdentifier && typeof workspaceIdentifier.id === 'string' && typeof workspaceIdentifier.configPath === 'string'; } + +export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { + if (workspace.configuration) { + return { + configPath: workspace.configuration.fsPath, + id: workspace.id + }; + } + if (workspace.folders.length === 1) { + return workspace.folders[0].uri; + } + + // Empty workspace + return undefined; +} diff --git a/src/vs/platform/workspaces/common/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts similarity index 97% rename from src/vs/platform/workspaces/common/workspacesIpc.ts rename to src/vs/platform/workspaces/node/workspacesIpc.ts index e123da0b4c7..67ef8942458 100644 --- a/src/vs/platform/workspaces/common/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -6,7 +6,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import URI from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 210d5253765..b49fa1f295e 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -366,14 +366,22 @@ suite('WorkspacesMainService', () => { assert.equal(0, untitled.length); return createWorkspace([process.cwd(), os.tmpdir()]).then(untitledOne => { + assert.ok(fs.existsSync(untitledOne.configPath)); + untitled = service.getUntitledWorkspacesSync(); assert.equal(1, untitled.length); assert.equal(untitledOne.id, untitled[0].id); return createWorkspace([os.tmpdir(), process.cwd()]).then(untitledTwo => { + assert.ok(fs.existsSync(untitledTwo.configPath)); + untitled = service.getUntitledWorkspacesSync(); + if (untitled.length === 1) { + assert.fail('Unexpected workspaces count, contents:\n' + fs.readFileSync(untitledTwo.configPath, 'utf8')); + } + assert.equal(2, untitled.length); service.deleteUntitledWorkspaceSync(untitledOne); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 607f418e181..2c063cbf7b9 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1231,24 +1231,41 @@ declare module 'vscode' { */ export class Uri { + /** + * Create an URI from a string, e.g. `http://www.msft.com/some/path`, + * `file:///usr/home`, or `scheme:with/path`. + * + * @see [Uri.toString](#Uri.toString) + * @param value The string value of an Uri. + * @return A new Uri instance. + */ + static parse(value: string): Uri; + /** * Create an URI from a file system path. The [scheme](#Uri.scheme) * will be `file`. + * + * The *difference* between `Uri#parse` and `Uri#file` is that the latter treats the argument + * as path, not as stringified-uri. E.g. `Uri.file(path)` is *not* the same as + * `Uri.parse('file://' + path)` because the path might contain characters that are + * interpreted (# and ?). See the following sample: + * ```ts + const good = URI.file('/coding/c#/project1'); + good.scheme === 'file'; + good.path === '/coding/c#/project1'; + good.fragment === ''; + + const bad = URI.parse('file://' + '/coding/c#/project1'); + bad.scheme === 'file'; + bad.path === '/coding/c'; // path is now broken + bad.fragment === '/project1'; + ``` * * @param path A file system or UNC path. * @return A new Uri instance. */ static file(path: string): Uri; - /** - * Create an URI from a string. Will throw if the given value is not - * valid. - * - * @param value The string value of an Uri. - * @return A new Uri instance. - */ - static parse(value: string): Uri; - /** * Use the `file` and `parse` factory functions to create new `Uri` objects. */ @@ -1285,8 +1302,21 @@ declare module 'vscode' { * The string representing the corresponding file system path of this Uri. * * Will handle UNC paths and normalize windows drive letters to lower-case. Also - * uses the platform specific path separator. Will *not* validate the path for - * invalid characters and semantics. Will *not* look at the scheme of this Uri. + * uses the platform specific path separator. + * + * * Will *not* validate the path for invalid characters and semantics. + * * Will *not* look at the scheme of this Uri. + * * The resulting string shall *not* be used for display purposes but + * for disk operations, like `readFile` et al. + * + * The *difference* to the [`path`](#Uri.path)-property is the use of the platform specific + * path separator and the handling of UNC paths. The sample below outlines the difference: + * ```ts + const u = URI.parse('file://server/c$/folder/file.txt') + u.authority === 'server' + u.path === '/shares/c$/file.txt' + u.fsPath === '\\server\c$\folder\file.txt' + ``` */ readonly fsPath: string; @@ -1308,8 +1338,10 @@ declare module 'vscode' { /** * Returns a string representation of this Uri. The representation and normalization - * of a URI depends on the scheme. The resulting string can be safely used with - * [Uri.parse](#Uri.parse). + * of a URI depends on the scheme. + * + * * The resulting string can be safely used with [Uri.parse](#Uri.parse). + * * The resulting string shall *not* be used for display purposes. * * @param skipEncoding Do not percentage-encode the result, defaults to `false`. Note that * the `#` and `?` characters occurring in the path will always be encoded. @@ -2483,7 +2515,7 @@ declare module 'vscode' { * @param name The name of the symbol. * @param kind The kind of the symbol. * @param containerName The name of the symbol containing the symbol. - * @param location The the location of the symbol. + * @param location The location of the symbol. */ constructor(name: string, kind: SymbolKind, containerName: string, location: Location); @@ -2707,13 +2739,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; @@ -2744,7 +2778,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. */ @@ -2766,6 +2801,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. * @@ -3522,6 +3584,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; @@ -3536,7 +3599,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 { /** @@ -3548,7 +3614,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 } @@ -4471,6 +4537,13 @@ declare module 'vscode' { * [`globalState`](#ExtensionContext.globalState) to store key value data. */ storagePath: string | undefined; + + /** + * An absolute file path of a directory in which the extension can create log files. + * The directory might not exist on disk and creation is up to the extension. However, + * the parent directory is guaranteed to be existent. + */ + logPath: string; } /** @@ -5500,6 +5573,11 @@ declare module 'vscode' { */ title: string; + /** + * Icon for the panel shown in UI. + */ + iconPath?: Uri | { light: Uri; dark: Uri }; + /** * Webview belonging to the panel. */ @@ -6842,7 +6920,7 @@ declare module 'vscode' { /** * Icon for the button. */ - readonly iconPath: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; + readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; /** * An optional tooltip. @@ -6853,7 +6931,7 @@ declare module 'vscode' { /** * Predefined buttons for [QuickPick](#QuickPick) and [InputBox](#InputBox). */ - export namespace QuickInputButtons { + export class QuickInputButtons { /** * A back button for [QuickPick](#QuickPick) and [InputBox](#InputBox). @@ -6861,7 +6939,12 @@ declare module 'vscode' { * 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 Back: QuickInputButton; + static readonly Back: QuickInputButton; + + /** + * @hidden + */ + private constructor(); } /** @@ -7168,12 +7251,17 @@ declare module 'vscode' { export function saveAll(includeUntitled?: boolean): Thenable<boolean>; /** - * 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. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 3a2231552c5..310306e30a9 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// This is the place for API experiments and proposal. - +// This is the place for API experiments and proposals. declare module 'vscode' { @@ -77,18 +76,33 @@ 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; + followSymlinks: boolean; + } + + /** + * Options to specify the size of the result text preview. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + */ + maxLines: number; /** - * The maximum number of results to be returned. + * The maximum number of characters included before the start of the match. */ - maxResults?: number; + leadingChars: number; + + /** + * The maximum number of characters included per line. + */ + totalChars: number; } /** @@ -96,9 +110,14 @@ declare module 'vscode' { */ export interface TextSearchOptions extends SearchOptions { /** - * TODO@roblou - total length? # of context lines? leading and trailing # of chars? + * The maximum number of results to be returned. */ - previewOptions?: any; + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; /** * Exclude files larger than `maxFileSize` in bytes. @@ -120,20 +139,26 @@ 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 { } + + /** + * A preview of the text result. + */ export interface TextSearchResultPreview { /** * The matching line of text, or a portion of the matching line that contains the match. @@ -162,15 +187,43 @@ declare module 'vscode' { range: Range; /** - * A preview of the matching line + * A preview of the text result. */ preview: TextSearchResultPreview; } /** - * 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<Uri[]>; + } + + /** + * 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. @@ -178,14 +231,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<Uri>, token: CancellationToken): Thenable<void>; - - /** - * 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<Uri[]>; + } + /** + * 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. @@ -193,7 +245,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<TextSearchResult>, token: CancellationToken): Thenable<void>; + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress<TextSearchResult>, token: CancellationToken): Thenable<void>; } /** @@ -236,9 +288,30 @@ declare module 'vscode' { * See the vscode setting `"files.encoding"` */ encoding?: string; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; } 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. * @@ -248,8 +321,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. @@ -310,12 +393,12 @@ declare module 'vscode' { //todo@joh -> make class export interface DecorationData { - priority?: number; + letter?: string; title?: string; - bubble?: boolean; - abbreviation?: string; color?: ThemeColor; - source?: string; + priority?: number; + bubble?: boolean; + source?: string; // hacky... we should remove it and use equality under the hood } export interface SourceControlResourceDecorations { @@ -387,39 +470,16 @@ declare module 'vscode' { Off = 7 } - /** - * A logger for writing to an extension's log file, and accessing its dedicated log directory. - */ - export interface Logger { - 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; - } - - export interface ExtensionContext { - /** - * This extension's logger - */ - logger: Logger; - - /** - * Path where an extension can write log files. - * - * Extensions must create this directory before writing to it. The parent directory will always exist. - */ - readonly logDirectory: string; - } - export namespace env { /** * Current logging level. - * - * @readonly */ export const logLevel: LogLevel; + + /** + * An [event](#Event) that fires when the log level has changed. + */ + export const onDidChangeLogLevel: Event<LogLevel>; } //#endregion @@ -474,6 +534,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<boolean>; + } + + //#endregion + //#region Comments /** * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. @@ -704,64 +781,6 @@ declare module 'vscode' { //#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<boolean>; - } - - //#endregion - //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 export interface FileRenameEvent { readonly oldUri: Uri; diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts index 56c7e90b3f2..ae49565113f 100644 --- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts @@ -90,10 +90,11 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { const cssClass = `extensionViewlet-test`; const icon = URI.parse(require.toUrl('./media/test.svg')); - this.registerCustomViewlet({ id: TEST_VIEW_CONTAINER_ID, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass); + this.registerCustomViewlet({ id: TEST_VIEW_CONTAINER_ID, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass, void 0); } private handleAndRegisterCustomViewContainers() { + let order = TEST_VIEW_CONTAINER_ORDER + 1; viewsContainersExtensionPoint.setHandler((extensions) => { for (let extension of extensions) { const { value, collector } = extension; @@ -103,7 +104,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { } switch (entry.key) { case 'activitybar': - this.registerCustomViewContainers(entry.value, extension.description); + order = this.registerCustomViewContainers(entry.value, extension.description, order); break; } }); @@ -139,22 +140,23 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { return true; } - private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription) { - containers.forEach((descriptor, index) => { + private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number): number { + containers.forEach(descriptor => { const cssClass = `extensionViewlet-${descriptor.id}`; const icon = resources.joinPath(extension.extensionLocation, descriptor.icon); - this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, TEST_VIEW_CONTAINER_ORDER + index + 1, cssClass); + this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, order++, cssClass, extension.id); }); + return order; } - private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string): void { + private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: string): void { const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry); const viewletRegistry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets); const id = descriptor.id; if (!viewletRegistry.getViewlet(id)) { - viewContainersRegistry.registerViewContainer(id); + viewContainersRegistry.registerViewContainer(id, extensionId); // Register as viewlet class CustomViewlet extends ViewContainerViewlet { diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index ddb907b9065..edc30f694e0 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -113,7 +113,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { } const registeredViews = ViewsRegistry.getViews(container); const viewIds = []; - const viewDescriptors = coalesce(entry.value.map(item => { + const viewDescriptors = coalesce(entry.value.map((item, index) => { // validate if (viewIds.indexOf(item.id) !== -1) { collector.error(localize('duplicateView1', "Cannot register multiple views with same id `{0}` in the view container `{1}`", item.id, container.id)); @@ -132,7 +132,8 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, collapsed: this.showCollapsed(container), - treeViewer: this.instantiationService.createInstance(CustomTreeViewer, item.id, container) + treeViewer: this.instantiationService.createInstance(CustomTreeViewer, item.id, container), + order: extension.description.id === container.extensionId ? index + 1 : void 0 }; viewIds.push(viewDescriptor.id); diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index b98563c70b1..3769f8b0c6b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -29,14 +29,22 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb private _debugAdapters: Map<number, ExtensionHostDebugAdapter>; private _debugAdaptersHandleCounter = 1; - constructor( extHostContext: IExtHostContext, @IDebugService private debugService: IDebugService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDebugService); this._toDispose = []; - this._toDispose.push(debugService.onDidNewSession(proc => this._proxy.$acceptDebugSessionStarted(<DebugSessionUUID>proc.getId(), proc.configuration.type, proc.getName(false)))); + this._toDispose.push(debugService.onDidNewSession(session => { + this._proxy.$acceptDebugSessionStarted(<DebugSessionUUID>session.getId(), session.configuration.type, session.getName(false)); + this._toDispose.push(session.onDidCustomEvent(event => { + if (event && event.sessionId) { + if (process) { + this._proxy.$acceptDebugSessionCustomEvent(event.sessionId, session.configuration.type, session.configuration.name, event); + } + } + })); + })); this._toDispose.push(debugService.onDidEndSession(proc => this._proxy.$acceptDebugSessionTerminated(<DebugSessionUUID>proc.getId(), proc.configuration.type, proc.getName(false)))); this._toDispose.push(debugService.getViewModel().onDidFocusSession(proc => { if (proc) { @@ -46,14 +54,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } })); - this._toDispose.push(debugService.onDidCustomEvent(event => { - if (event && event.sessionId) { - const process = this.debugService.getModel().getSessions().filter(p => p.getId() === event.sessionId).pop(); - if (process) { - this._proxy.$acceptDebugSessionCustomEvent(event.sessionId, process.configuration.type, process.configuration.name, event); - } - } - })); this._debugAdapters = new Map<number, ExtensionHostDebugAdapter>(); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 7e32ff27498..51a029b241e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -11,6 +11,7 @@ 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'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../node/extHost.protocol'; +import { LabelRules, ILabelService } from 'vs/platform/label/common/label'; @extHostNamedCustomer(MainContext.MainThreadFileSystem) export class MainThreadFileSystem implements MainThreadFileSystemShape { @@ -20,7 +21,8 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { constructor( extHostContext: IExtHostContext, - @IFileService private readonly _fileService: IFileService + @IFileService private readonly _fileService: IFileService, + @ILabelService private readonly _labelService: ILabelService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem); } @@ -39,6 +41,10 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { this._fileProvider.delete(handle); } + $setUriFormatter(scheme: string, formatter: LabelRules): void { + this._labelService.registerFormatter(scheme, formatter); + } + $onFileSystemChange(handle: number, changes: IFileChangeDto[]): void { this._fileProvider.get(handle).$onFileSystemChange(changes); } @@ -92,36 +98,34 @@ class RemoteFileSystemProvider implements IFileSystemProvider { }); } - readFile(resource: URI): TPromise<Uint8Array, any> { - return this._proxy.$readFile(this._handle, resource).then(encoded => { - return Buffer.from(encoded, 'base64'); - }); + readFile(resource: URI): TPromise<Uint8Array> { + return this._proxy.$readFile(this._handle, resource); } - writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise<void, any> { + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise<void> { let encoded = Buffer.isBuffer(content) - ? content.toString('base64') - : Buffer.from(content.buffer, content.byteOffset, content.byteLength).toString('base64'); + ? content + : Buffer.from(content.buffer, content.byteOffset, content.byteLength); return this._proxy.$writeFile(this._handle, resource, encoded, opts); } - delete(resource: URI, opts: FileDeleteOptions): TPromise<void, any> { + delete(resource: URI, opts: FileDeleteOptions): TPromise<void> { return this._proxy.$delete(this._handle, resource, opts); } - mkdir(resource: URI): TPromise<void, any> { + mkdir(resource: URI): TPromise<void> { 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<void, any> { + rename(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise<void> { return this._proxy.$rename(this._handle, resource, target, opts); } - copy(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise<void, any> { + copy(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise<void> { return this._proxy.$copy(this._handle, resource, target, opts); } } 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 59f93eb7f2f..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,11 +216,12 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { const timeout = this._configurationService.getValue('editor.formatOnSaveTimeout', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() }); return new Promise<ISingleEditOperation[]>((resolve, reject) => { - let request = getDocumentFormattingEdits(model, { tabSize, insertSpaces }); + 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)); - request.cancel(); + source.cancel(); }, timeout); request.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits)).then(resolve, err => { diff --git a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts index caffd57767b..1021d2d8c71 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts @@ -9,7 +9,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import URI, { UriComponents } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, QueryType } from 'vs/platform/search/common/search'; +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'; @@ -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 { @@ -70,7 +78,7 @@ class SearchOperation { addMatch(match: IFileMatch): void { if (this.matches.has(match.resource.toString())) { // Merge with previous IFileMatches - this.matches.get(match.resource.toString()).lineMatches.push(...match.lineMatches); + this.matches.get(match.resource.toString()).matches.push(...match.matches); } else { this.matches.set(match.resource.toString(), match); } @@ -86,11 +94,12 @@ 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 { @@ -103,16 +112,6 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { 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 TPromise((resolve, reject) => { @@ -139,7 +138,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { } clearCache(cacheKey: string): TPromise<void> { - return this._proxy.$clearCache(this._handle, cacheKey); + return this._proxy.$clearCache(cacheKey); } handleFindMatch(session: number, dataOrUri: (UriComponents | IRawFileMatch2)[]): void { @@ -150,10 +149,10 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { const searchOp = this._searches.get(session); dataOrUri.forEach(result => { - if ((<IRawFileMatch2>result).lineMatches) { + if ((<IRawFileMatch2>result).matches) { searchOp.addMatch({ resource: URI.revive((<IRawFileMatch2>result).resource), - lineMatches: (<IRawFileMatch2>result).lineMatches + matches: (<IRawFileMatch2>result).matches }); } else { searchOp.addMatch({ diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index cafb5b305b2..a1b5243fa6c 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -504,7 +504,7 @@ export class MainThreadTask implements MainThreadTaskShape { this._taskService.registerTaskSystem(key, { platform: platform, uriProvider: (path: string): URI => { - return URI.parse(`${info.scheme}://${info.host}:${info.port}${path}`); + return URI.parse(`${info.scheme}://${info.authority}${path}`); }, context: this._extHostContext, resolveVariables: (workspaceFolder: IWorkspaceFolder, variables: Set<string>): TPromise<Map<string, string>> => { 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<boolean> { @@ -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 8db9fa2331f..e2148ffffc2 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -9,26 +9,27 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import URI, { UriComponents } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IFileMatch, IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchQuery, ISearchService, QueryType, ISearchProgressItem } from 'vs/platform/search/common/search'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchProgressItem, ISearchQuery, ISearchService, QueryType } from 'vs/platform/search/common/search'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; 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 { private readonly _toDispose: IDisposable[] = []; - private readonly _activeSearches: { [id: number]: TPromise<URI[]> } = Object.create(null); + private readonly _activeSearches: { [id: number]: TPromise<any> } = Object.create(null); private readonly _proxy: ExtHostWorkspaceShape; constructor( @@ -40,6 +41,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { @IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService, @IStatusbarService private readonly _statusbarService: IStatusbarService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ILabelService private readonly _labelService: ILabelService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace); this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose); @@ -99,7 +101,13 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { } private _onDidChangeWorkspace(): void { - this._proxy.$acceptWorkspaceData(this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace()); + const workspace = this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace(); + this._proxy.$acceptWorkspaceData(workspace ? { + configuration: workspace.configuration, + folders: workspace.folders, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + } : null); } // --- search --- @@ -165,36 +173,57 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return search; } - $startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void, IFileMatch> { + $startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void> { 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 onProgress = (p: ISearchProgressItem) => { - if (p.lineMatches) { - this._proxy.$handleTextSearchResult(p, requestId); + const onProgress = (p: ISearchProgressItem) => { + if (p.matches) { + 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); } - }; - const search = this._searchService.search(query, onProgress).then( - () => { - delete this._activeSearches[requestId]; - resolve(null); - }, - err => { - delete this._activeSearches[requestId]; - if (!isPromiseCanceledError(err)) { - reject(TPromise.wrapError(err)); - } + return undefined; + }); - return undefined; - }); + this._activeSearches[requestId] = search; - this._activeSearches[requestId] = search; - }); + return search; + } + + $checkExists(query: ISearchQuery, requestId: number): TPromise<boolean> { + query.exists = true; + const search = this._searchService.search(query).then( + result => { + delete this._activeSearches[requestId]; + return result.limitHit; + }, + err => { + delete this._activeSearches[requestId]; + if (!isPromiseCanceledError(err)) { + return TPromise.wrapError(err); + } + + return undefined; + }); + + this._activeSearches[requestId] = search; + + return search; } $cancelSearch(requestId: number): Thenable<boolean> { @@ -230,4 +259,4 @@ CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (ac } 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 98eaf7888ca..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,6 +49,12 @@ 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], forceNewWindow); } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index d976ff3289e..b88ca3ef8f0 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -114,7 +114,7 @@ export function createApiFactory( 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)); @@ -227,7 +227,14 @@ export function createApiFactory( get language() { return platform.language; }, get appName() { return product.nameLong; }, get appRoot() { return initData.environment.appRoot; }, - get logLevel() { return extHostLogService.getLevel(); } + get logLevel() { + checkProposedApiEnabled(extension); + return extHostLogService.getLevel(); + }, + get onDidChangeLogLevel() { + checkProposedApiEnabled(extension); + return extHostLogService.onDidChangeLogLevel; + } }); // namespace: extensions @@ -266,7 +273,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); @@ -429,7 +436,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') { @@ -437,9 +444,9 @@ export function createApiFactory( } return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs); }, - createTerminalRenderer(name: string): vscode.TerminalRenderer { + createTerminalRenderer: proposedApiFunction(extension, (name: string) => { return extHostTerminalService.createTerminalRenderer(name); - }, + }), registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable { return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider); }, @@ -467,11 +474,6 @@ export function createApiFactory( }, }; - // namespace: QuickInputButtons - const QuickInputButtons: typeof vscode.QuickInputButtons = { - Back: extHostQuickOpen.backButton, - }; - // namespace: workspace const workspace: typeof vscode.workspace = { get rootPath() { @@ -487,7 +489,7 @@ export function createApiFactory( return extHostWorkspace.getWorkspaceFolders(); }, get name() { - return extHostWorkspace.workspace ? extHostWorkspace.workspace.name : undefined; + return extHostWorkspace.name; }, set name(value) { throw errors.readonly(); @@ -586,8 +588,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); @@ -684,39 +696,48 @@ export function createApiFactory( version: pkg.version, // namespaces commands, + debug, env, extensions, languages, + scm, + tasks, window, workspace, - scm, - debug, - tasks, // types Breakpoint: extHostTypes.Breakpoint, CancellationTokenSource: CancellationTokenSource, CodeAction: extHostTypes.CodeAction, CodeActionKind: extHostTypes.CodeActionKind, + CodeActionTrigger: extHostTypes.CodeActionTrigger, CodeLens: extHostTypes.CodeLens, Color: extHostTypes.Color, - ColorPresentation: extHostTypes.ColorPresentation, ColorInformation: extHostTypes.ColorInformation, - CodeActionTrigger: extHostTypes.CodeActionTrigger, - EndOfLine: extHostTypes.EndOfLine, + ColorPresentation: extHostTypes.ColorPresentation, + CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, CompletionList: extHostTypes.CompletionList, CompletionTriggerKind: extHostTypes.CompletionTriggerKind, + ConfigurationTarget: extHostTypes.ConfigurationTarget, DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable, + DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, Diagnostic: extHostTypes.Diagnostic, DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation, - DiagnosticTag: extHostTypes.DiagnosticTag, DiagnosticSeverity: extHostTypes.DiagnosticSeverity, + DiagnosticTag: extHostTypes.DiagnosticTag, Disposable: extHostTypes.Disposable, DocumentHighlight: extHostTypes.DocumentHighlight, DocumentHighlightKind: extHostTypes.DocumentHighlightKind, DocumentLink: extHostTypes.DocumentLink, + DocumentSymbol: extHostTypes.DocumentSymbol, + EndOfLine: extHostTypes.EndOfLine, EventEmitter: Emitter, + FileChangeType: extHostTypes.FileChangeType, + FileSystemError: extHostTypes.FileSystemError, + FileType: files.FileType, + FoldingRange: extHostTypes.FoldingRange, + FoldingRangeKind: extHostTypes.FoldingRangeKind, FunctionBreakpoint: extHostTypes.FunctionBreakpoint, Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, @@ -726,52 +747,41 @@ export function createApiFactory( OverviewRulerLane: OverviewRulerLane, ParameterInformation: extHostTypes.ParameterInformation, Position: extHostTypes.Position, - QuickInputButtons, + ProcessExecution: extHostTypes.ProcessExecution, + ProgressLocation: extHostTypes.ProgressLocation, + QuickInputButtons: extHostTypes.QuickInputButtons, Range: extHostTypes.Range, + RelativePattern: extHostTypes.RelativePattern, Selection: extHostTypes.Selection, + ShellExecution: extHostTypes.ShellExecution, + ShellQuoting: extHostTypes.ShellQuoting, SignatureHelp: extHostTypes.SignatureHelp, SignatureInformation: extHostTypes.SignatureInformation, SnippetString: extHostTypes.SnippetString, SourceBreakpoint: extHostTypes.SourceBreakpoint, + SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType, StatusBarAlignment: extHostTypes.StatusBarAlignment, SymbolInformation: extHostTypes.SymbolInformation, - DocumentSymbol: extHostTypes.DocumentSymbol, SymbolKind: extHostTypes.SymbolKind, - SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType, + Task: extHostTypes.Task, + TaskGroup: extHostTypes.TaskGroup, + TaskPanelKind: extHostTypes.TaskPanelKind, + TaskRevealKind: extHostTypes.TaskRevealKind, + TaskScope: extHostTypes.TaskScope, TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason, TextEdit: extHostTypes.TextEdit, TextEditorCursorStyle: TextEditorCursorStyle, TextEditorLineNumbersStyle: extHostTypes.TextEditorLineNumbersStyle, TextEditorRevealType: extHostTypes.TextEditorRevealType, TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind, - DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, + ThemeColor: extHostTypes.ThemeColor, + ThemeIcon: extHostTypes.ThemeIcon, + TreeItem: extHostTypes.TreeItem, + TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, Uri: URI, ViewColumn: extHostTypes.ViewColumn, WorkspaceEdit: extHostTypes.WorkspaceEdit, - ProgressLocation: extHostTypes.ProgressLocation, - TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, - ThemeIcon: extHostTypes.ThemeIcon, - TreeItem: extHostTypes.TreeItem, - ThemeColor: extHostTypes.ThemeColor, // functions - TaskRevealKind: extHostTypes.TaskRevealKind, - TaskPanelKind: extHostTypes.TaskPanelKind, - TaskGroup: extHostTypes.TaskGroup, - ProcessExecution: extHostTypes.ProcessExecution, - ShellExecution: extHostTypes.ShellExecution, - ShellQuoting: extHostTypes.ShellQuoting, - TaskScope: extHostTypes.TaskScope, - Task: extHostTypes.Task, - ConfigurationTarget: extHostTypes.ConfigurationTarget, - RelativePattern: extHostTypes.RelativePattern, - - FileChangeType: extHostTypes.FileChangeType, - FileType: files.FileType, - FileSystemError: extHostTypes.FileSystemError, - FoldingRange: extHostTypes.FoldingRange, - FoldingRangeKind: extHostTypes.FoldingRangeKind, - - CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState }; }; } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index d77f0ed0e8b..da27bbb931d 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -22,10 +22,11 @@ import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files'; +import { LabelRules } from 'vs/platform/label/common/label'; import { LogLevel } from 'vs/platform/log/common/log'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IPatternInfo, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats } from 'vs/platform/search/common/search'; +import { IPatternInfo, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; @@ -46,7 +47,7 @@ export interface IEnvironment { isExtensionDevelopmentDebug: boolean; appRoot: string; appSettingsHome: string; - extensionDevelopmentPath: string; + extensionDevelopmentLocationURI: URI; extensionTestsPath: string; } @@ -426,11 +427,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<boolean>; @@ -464,6 +471,7 @@ export interface ExtHostUrlsShape { export interface MainThreadWorkspaceShape extends IDisposable { $startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>; $startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number): TPromise<void>; + $checkExists(query: ISearchQuery, requestId: number): TPromise<boolean>; $cancelSearch(requestId: number): Thenable<boolean>; $saveAll(includeUntitled?: boolean): Thenable<boolean>; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>; @@ -477,11 +485,14 @@ export interface IFileChangeDto { export interface MainThreadFileSystemShape extends IDisposable { $registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): void; $unregisterProvider(handle: number): void; + $setUriFormatter(scheme: string, formatter: LabelRules): void; $onFileSystemChange(handle: number, resource: IFileChangeDto[]): void; } 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; @@ -670,8 +681,8 @@ export interface ExtHostWorkspaceShape { export interface ExtHostFileSystemShape { $stat(handle: number, resource: UriComponents): TPromise<IStat>; $readdir(handle: number, resource: UriComponents): TPromise<[string, FileType][]>; - $readFile(handle: number, resource: UriComponents): TPromise<string>; - $writeFile(handle: number, resource: UriComponents, base64Encoded: string, opts: FileWriteOptions): TPromise<void>; + $readFile(handle: number, resource: UriComponents): TPromise<Buffer>; + $writeFile(handle: number, resource: UriComponents, content: Buffer, opts: FileWriteOptions): TPromise<void>; $rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>; $copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>; $mkdir(handle: number, resource: UriComponents): TPromise<void>; @@ -682,7 +693,7 @@ export interface ExtHostFileSystemShape { export interface ExtHostSearchShape { $provideFileSearchResults(handle: number, session: number, query: IRawSearchQuery): TPromise<ISearchCompleteStats>; - $clearCache(handle: number, cacheKey: string): TPromise<void>; + $clearCache(cacheKey: string): TPromise<void>; $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, query: IRawSearchQuery): TPromise<ISearchCompleteStats>; } @@ -875,6 +886,7 @@ export interface ExtHostSCMShape { $onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void>; $executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): TPromise<void>; $validateInput(sourceControlHandle: number, value: string, cursorPosition: number): TPromise<[string, number] | undefined>; + $setSelectedSourceControls(selectedSourceControlHandles: number[]): TPromise<void>; } export interface ExtHostTaskShape { diff --git a/src/vs/workbench/api/node/extHostDecorations.ts b/src/vs/workbench/api/node/extHostDecorations.ts index 77d9b441fc8..9d1e58cfa35 100644 --- a/src/vs/workbench/api/node/extHostDecorations.ts +++ b/src/vs/workbench/api/node/extHostDecorations.ts @@ -11,21 +11,26 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { asWinJsPromise } from 'vs/base/common/async'; +interface ProviderData { + provider: vscode.DecorationProvider; + extensionId: string; +} + export class ExtHostDecorations implements ExtHostDecorationsShape { private static _handlePool = 0; - private readonly _provider = new Map<number, vscode.DecorationProvider>(); + private readonly _provider = new Map<number, ProviderData>(); private readonly _proxy: MainThreadDecorationsShape; constructor(mainContext: IMainContext) { this._proxy = mainContext.getProxy(MainContext.MainThreadDecorations); } - registerDecorationProvider(provider: vscode.DecorationProvider, label: string): vscode.Disposable { + registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: string): vscode.Disposable { const handle = ExtHostDecorations._handlePool++; - this._provider.set(handle, provider); - this._proxy.$registerDecorationProvider(handle, label); + this._provider.set(handle, { provider, extensionId }); + this._proxy.$registerDecorationProvider(handle, extensionId); const listener = provider.onDidChangeDecorations(e => { this._proxy.$onDidChange(handle, !e ? null : Array.isArray(e) ? e : [e]); @@ -42,13 +47,16 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { const result: DecorationReply = Object.create(null); return TPromise.join(requests.map(request => { const { handle, uri, id } = request; - const provider = this._provider.get(handle); - if (!provider) { + if (!this._provider.has(handle)) { // might have been unregistered in the meantime return void 0; } + const { provider, extensionId } = this._provider.get(handle); return asWinJsPromise(token => provider.provideDecoration(URI.revive(uri), token)).then(data => { - result[id] = data && <DecorationData>[data.priority, data.bubble, data.title, data.abbreviation, data.color, data.source]; + if (data && data.letter && data.letter.length !== 1) { + console.warn(`INVALID decoration from extension '${extensionId}'. The 'letter' must be set and be one character, not '${data.letter}'.`); + } + result[id] = data && <DecorationData>[data.priority, data.bubble, data.title, data.letter, data.color, data.source]; }, err => { console.error(err); }); diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index c37e7b2dfe7..5218f66cda6 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -10,7 +10,6 @@ import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtHostLogger } from 'vs/workbench/api/node/extHostLogService'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0); @@ -27,8 +26,7 @@ export interface IExtensionContext { extensionPath: string; storagePath: string; asAbsolutePath(relativePath: string): string; - logger: ExtHostLogger; - readonly logDirectory: string; + readonly logPath: string; } /** diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 61ec236d842..2fe2cf9cd2e 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; -import { createApiFactory, initializeExtensionApi, checkProposedApiEnabled } from 'vs/workbench/api/node/extHost.api.impl'; +import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl'; import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol'; import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; @@ -361,14 +361,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, storagePath: this._storagePath.value(extensionDescription), asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionLocation.fsPath, relativePath); }, - get logger() { - checkProposedApiEnabled(extensionDescription); - return that._extHostLogService.getExtLogger(extensionDescription.id); - }, - get logDirectory() { - checkProposedApiEnabled(extensionDescription); - return that._extHostLogService.getLogDirectory(extensionDescription.id); - } + logPath: that._extHostLogService.getLogDirectory(extensionDescription.id) }); }); } diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index d40aad904b9..140efe9d694 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -15,6 +15,7 @@ import { values } from 'vs/base/common/map'; import { Range, FileChangeType } from 'vs/workbench/api/node/extHostTypes'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; import { Schemas } from 'vs/base/common/network'; +import { LabelRules } from 'vs/platform/label/common/label'; class FsLinkProvider implements vscode.DocumentLinkProvider { @@ -141,44 +142,48 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { }); } + setUriFormatter(scheme: string, formatter: LabelRules): void { + this._proxy.$setUriFormatter(scheme, formatter); + } + private static _asIStat(stat: vscode.FileStat): files.IStat { const { type, ctime, mtime, size } = stat; return { type, ctime, mtime, size }; } - $stat(handle: number, resource: UriComponents): TPromise<files.IStat, any> { + $stat(handle: number, resource: UriComponents): TPromise<files.IStat> { 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))); } - $readFile(handle: number, resource: UriComponents): TPromise<string> { + $readFile(handle: number, resource: UriComponents): TPromise<Buffer> { return asWinJsPromise(() => { return this._fsProvider.get(handle).readFile(URI.revive(resource)); }).then(data => { - return Buffer.isBuffer(data) ? data.toString('base64') : Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('base64'); + return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength); }); } - $writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileWriteOptions): TPromise<void, any> { - return asWinJsPromise(() => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts)); + $writeFile(handle: number, resource: UriComponents, content: Buffer, opts: files.FileWriteOptions): TPromise<void> { + return asWinJsPromise(() => this._fsProvider.get(handle).writeFile(URI.revive(resource), content, opts)); } - $delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): TPromise<void, any> { + $delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): TPromise<void> { return asWinJsPromise(() => this._fsProvider.get(handle).delete(URI.revive(resource), opts)); } - $rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void, any> { + $rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void> { 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<void, any> { + $copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void> { return asWinJsPromise(() => this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts)); } - $mkdir(handle: number, resource: UriComponents): TPromise<void, any> { + $mkdir(handle: number, resource: UriComponents): TPromise<void> { 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 8dc9a268ce5..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 @@ -273,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<CodeActionDto[]> { @@ -314,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, @@ -838,6 +849,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { private _heapService: ExtHostHeapService; private _diagnostics: ExtHostDiagnostics; private _adapter = new Map<number, Adapter>(); + private readonly _logService: ILogService; constructor( mainContext: IMainContext, @@ -845,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); @@ -853,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[] { @@ -1024,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/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts index 6708690a2f4..31b0113188f 100644 --- a/src/vs/workbench/api/node/extHostLogService.ts +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as vscode from 'vscode'; import { join } from 'vs/base/common/paths'; import { LogLevel } from 'vs/workbench/api/node/extHostTypes'; import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log'; @@ -14,8 +13,6 @@ import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol'; export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape { - private _loggers: Map<string, ExtHostLogger> = new Map(); - constructor( private _windowId: number, logLevel: LogLevel, @@ -28,54 +25,7 @@ export class ExtHostLogService extends DelegatedLogService implements ILogServic this.setLevel(level); } - getExtLogger(extensionID: string): ExtHostLogger { - let logger = this._loggers.get(extensionID); - if (!logger) { - logger = this.createLogger(extensionID); - this._loggers.set(extensionID, logger); - } - return logger; - } - getLogDirectory(extensionID: string): string { return join(this._logsPath, `${extensionID}_${this._windowId}`); } - - private createLogger(extensionID: string): ExtHostLogger { - const logsDirPath = this.getLogDirectory(extensionID); - const logService = createSpdLogService(extensionID, this.getLevel(), logsDirPath); - this._register(this.onDidChangeLogLevel(level => logService.setLevel(level))); - return new ExtHostLogger(logService); - } -} - -export class ExtHostLogger implements vscode.Logger { - - constructor( - private readonly _logService: ILogService - ) { } - - trace(message: string, ...args: any[]): void { - return this._logService.trace(message, ...args); - } - - debug(message: string, ...args: any[]): void { - return this._logService.debug(message, ...args); - } - - info(message: string, ...args: any[]): void { - return this._logService.info(message, ...args); - } - - warn(message: string, ...args: any[]): void { - return this._logService.warn(message, ...args); - } - - error(message: string | Error, ...args: any[]): void { - return this._logService.error(message, ...args); - } - - critical(message: string | Error, ...args: any[]): void { - return this._logService.critical(message, ...args); - } } 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<T extends QuickPickItem>(extensionId: string): QuickPick<T> { 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<TransferQuickInputButton>((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<boolean>(); + 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<vscode.SourceControl>(); get onDidChangeActiveProvider(): Event<vscode.SourceControl> { return this._onDidChangeActiveProvider.event; } + private _selectedSourceControlHandles = new Set<number>(); + 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<void> { + this.logService.trace('ExtHostSCM#$setSelectedSourceControls', selectedSourceControlHandles); + + const set = new Set<number>(); + + 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..c4e2ab5fc70 --- /dev/null +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -0,0 +1,730 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { StopWatch } from 'vs/base/common/stopwatch'; +import * as strings from 'vs/base/common/strings'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { ICachedSearchStats, IFileMatch, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, IFileSearchStats, IFileIndexProviderStats } from 'vs/platform/search/common/search'; +import * as vscode from 'vscode'; +import { canceled } from 'vs/base/common/errors'; + +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<boolean>): TPromise<boolean> { + 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<T = IFileSearchStats> { + limitHit: boolean; + results: IInternalFileMatch[]; + stats: T; +} + +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 filesWalked = 0; + private dirsWalked = 0; + + private activeCancellationTokens: Set<CancellationTokenSource>; + + 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<CancellationTokenSource>(); + + 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, stats: IFileIndexProviderStats }> { + if (this.config.folderQueries.length !== 1) { + throw new Error('Searches just one folder'); + } + + // Searches a single folder + const folderQuery = this.config.folderQueries[0]; + + return new TPromise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => { + const onResult = (match: IInternalFileMatch) => { + this.resultCount++; + _onResult(match); + }; + + if (this.isCanceled) { + throw canceled(); + } + + // 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(stats => { + resolve({ + isLimitHit: this.isLimitHit, + stats + }); + }, (errs: Error[]) => { + const errMsg = errs + .map(err => toErrorMessage(err)) + .filter(msg => !!msg)[0]; + + reject(new Error(errMsg)); + }); + }); + } + + private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): TPromise<IFileIndexProviderStats> { + 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); + }; + + let providerSW: StopWatch; + let providerTime: number; + let fileWalkTime: number; + new TPromise(resolve => process.nextTick(resolve)) + .then(() => { + this.activeCancellationTokens.add(cancellation); + providerSW = StopWatch.create(); + return this.provider.provideFileIndex(options, cancellation.token); + }) + .then(results => { + providerTime = providerSW.elapsed(); + const postProcessSW = StopWatch.create(); + this.activeCancellationTokens.delete(cancellation); + if (this.isCanceled) { + return null; + } + + results.forEach(onProviderResult); + + this.matchDirectoryTree(tree, queryTester, onResult); + fileWalkTime = postProcessSW.elapsed(); + return null; + }).then( + () => { + cancellation.dispose(); + resolve(<IFileIndexProviderStats>{ + providerTime, + fileWalkTime, + directoriesWalked: this.dirsWalked, + filesWalked: this.filesWalked + }); + }, + err => { + cancellation.dispose(); + reject(err); + }); + }); + } + + private getSearchOptionsForFolder(fq: IFolderQuery<URI>): 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.dirsWalked++; + 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<string, Set<string>>(); + + public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void): TPromise<ISearchCompleteStats> { + 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<ISearchCompleteStats>((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 <ISearchCompleteStats>{ + limitHit: complete.limitHit, + stats: { + type: 'fileIndexProver', + detailStats: complete.stats, + fromCache: false, + resultCount: complete.results.length + } + }; + }); + } + + 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<IInternalSearchComplete> { + let searchPromise: TPromise<void>; + let allResultsPromise = new TPromise<IInternalSearchComplete<IFileIndexProviderStats>>((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); + const cacheRow: ICacheRow = { + promise: allResultsPromise, + resolved: false + }; + cache.resultsToSearchCache[config.filePattern] = cacheRow; + allResultsPromise.then(() => { + cacheRow.resolved = true; + }, err => { + delete cache.resultsToSearchCache[config.filePattern]; + }); + allResultsPromise = this.preventCancellation(allResultsPromise); + } + + let chained: TPromise<void>; + return new TPromise<IInternalSearchComplete>((c, e) => { + chained = allResultsPromise.then(complete => { + const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); + const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(); + return this.sortResults(config, complete.results, scorerCache) + .then(sortedResults => { + // sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened. + // Contrasting with findFiles which is not sorted and will have sortingTime: undefined + const sortingTime = sortSW ? sortSW.elapsed() : -1; + c(<IInternalSearchComplete>{ + limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults, // ?? + results: sortedResults, + stats: { + detailStats: complete.stats, + fromCache: false, + resultCount: sortedResults.length, + sortingTime, + type: 'fileIndexProver' + } + }); + }); + }, 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<IInternalSearchComplete> { + 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<void>; + return new TPromise<IInternalSearchComplete>((c, e) => { + chained = cached.then(complete => { + const sortSW = StopWatch.create(); + return this.sortResults(config, complete.results, cache.scorerCache) + .then(sortedResults => { + c(<IInternalSearchComplete<IFileSearchStats>>{ + limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults, + results: sortedResults, + stats: { + fromCache: true, + detailStats: complete.stats, + type: 'fileIndexProver', + resultCount: sortedResults.length, + sortingTime: sortSW.elapsed() + } + }); + }); + }, e); + }, () => { + chained.cancel(); + }); + } + return undefined; + } + + private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache): TPromise<IInternalFileMatch[]> { + // 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<IInternalSearchComplete<ICachedSearchStats>> { + const cacheLookupSW = StopWatch.create(); + + 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 cacheRow: ICacheRow; + 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 row = cache.resultsToSearchCache[previousSearch]; + cacheRow = { + promise: this.preventCancellation(row.promise), + resolved: row.resolved + }; + break; + } + } + + if (!cacheRow) { + return null; + } + + const cacheLookupTime = cacheLookupSW.elapsed(); + const cacheFilterSW = StopWatch.create(); + + return new TPromise<IInternalSearchComplete<ICachedSearchStats>>((c, e) => { + cacheRow.promise.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(<IInternalSearchComplete<ICachedSearchStats>>{ + limitHit: complete.limitHit, + results, + stats: { + cacheWasResolved: cacheRow.resolved, + cacheLookupTime, + cacheFilterTime: cacheFilterSW.elapsed(), + cacheEntryCount: complete.results.length + } + }); + }, e); + }, () => { + cacheRow.promise.cancel(); + }); + } + + private doSearch(engine: FileIndexSearchEngine): TPromise<IInternalSearchComplete<IFileIndexProviderStats>> { + const results: IInternalFileMatch[] = []; + const onResult = match => results.push(match); + return new TPromise<IInternalSearchComplete<IFileIndexProviderStats>>((c, e) => { + engine.search(onResult).then(result => { + c(<IInternalSearchComplete<IFileIndexProviderStats>>{ + limitHit: result.isLimitHit, + results, + stats: result.stats + }); + }, e); + }, () => { + engine.cancel(); + }); + } + + public clearCache(cacheKey: string): TPromise<void> { + 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<C>(promise: TPromise<C>): TPromise<C> { + return new TPromise<C>((c, e) => { + // Allow for piled up cancellations to come through first. + process.nextTick(() => { + promise.then(c, e); + }); + }, () => { + // Do not propagate. + }); + } +} + +interface ICacheRow { + promise: TPromise<IInternalSearchComplete<IFileIndexProviderStats>>; + resolved: boolean; +} + +class Cache { + + public resultsToSearchCache: { [searchValue: string]: ICacheRow; } = Object.create(null); + + public scorerCache: ScorerCache = Object.create(null); +} + +const FileMatchItemAccessor = new class implements IItemAccessor<IInternalFileMatch> { + + 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 e7bcb39550d..58b755902c6 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -5,17 +5,20 @@ 'use strict'; import * as path from 'path'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; import * as glob from 'vs/base/common/glob'; +import { toDisposable } from 'vs/base/common/lifecycle'; import * as resources from 'vs/base/common/resources'; +import { StopWatch } from 'vs/base/common/stopwatch'; import URI, { UriComponents } from 'vs/base/common/uri'; 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 { IFileMatch, IFileSearchProviderStats, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, ITextSearchResult } from 'vs/platform/search/common/search'; +import { FileIndexSearchManager, IDirectoryEntry, IDirectoryTree, IInternalFileMatch, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/api/node/extHostSearch.fileIndex'; import * as vscode from 'vscode'; import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol'; -import { toDisposable } from 'vs/base/common/lifecycle'; export interface ISchemeTransformer { transformOutgoing(scheme: string): string; @@ -24,14 +27,18 @@ export interface ISchemeTransformer { export class ExtHostSearch implements ExtHostSearchShape { private readonly _proxy: MainThreadSearchShape; - private readonly _searchProvider = new Map<number, vscode.SearchProvider>(); + private readonly _fileSearchProvider = new Map<number, vscode.FileSearchProvider>(); + private readonly _textSearchProvider = new Map<number, vscode.TextSearchProvider>(); + private readonly _fileIndexProvider = new Map<number, vscode.FileIndexProvider>(); 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 { @@ -41,40 +48,80 @@ 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)); + this._fileSearchProvider.set(handle, provider); + this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme)); return toDisposable(() => { - this._searchProvider.delete(handle); + this._fileSearchProvider.delete(handle); this._proxy.$unregisterProvider(handle); }); } - $provideFileSearchResults(handle: number, session: number, rawQuery: IRawSearchQuery): TPromise<ISearchCompleteStats> { - const provider = this._searchProvider.get(handle); - if (!provider.provideFileSearchResults) { - return TPromise.as(undefined); - } - - const query = reviveQuery(rawQuery); - return this._fileSearchManager.fileSearch(query, provider, progress => { - this._proxy.$handleFileMatch(handle, session, progress.map(p => p.resource)); + 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); }); } - $clearCache(handle: number, cacheKey: string): TPromise<void> { - const provider = this._searchProvider.get(handle); - if (!provider.clearCache) { - return TPromise.as(undefined); - } + 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 + }); + } - return TPromise.as( - this._fileSearchManager.clearCache(cacheKey, provider)); + $provideFileSearchResults(handle: number, session: number, rawQuery: IRawSearchQuery): TPromise<ISearchCompleteStats> { + const provider = this._fileSearchProvider.get(handle); + const query = reviveQuery(rawQuery); + if (provider) { + let cancelSource = new CancellationTokenSource(); + return new TPromise((c, e) => { + this._fileSearchManager.fileSearch(query, provider, batch => { + this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource)); + }, cancelSource.token).then(c, err => { + if (!isPromiseCanceledError(err)) { + e(err); + } + }); + }, () => { + // TODO IPC promise cancellation #53526 + cancelSource.cancel(); + cancelSource.dispose(); + }); + } 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)); + }).then(null, err => { + if (!isPromiseCanceledError(err)) { + throw err; + } + + return null; + }); + } else { + throw new Error('something went wrong'); + } + } + } + + $clearCache(cacheKey: string): TPromise<void> { + // 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<ISearchCompleteStats> { - const provider = this._searchProvider.get(handle); + const provider = this._textSearchProvider.get(handle); if (!provider.provideTextSearchResults) { return TPromise.as(undefined); } @@ -85,22 +132,6 @@ export class ExtHostSearch implements ExtHostSearchShape { } } -/** - * 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, @@ -141,22 +172,16 @@ class TextSearchResultsCollector { if (!this._currentFileMatch) { this._currentFileMatch = { resource: data.uri, - lineMatches: [] + matches: [] }; } - // TODO@roblou - line text is sent for every match - const matchRange = data.preview.match; - this._currentFileMatch.lineMatches.push({ - lineNumber: data.range.start.line, - preview: data.preview.text, - offsetAndLengths: [[matchRange.start.character, matchRange.end.character - matchRange.start.character]] - }); + this._currentFileMatch.matches.push(extensionResultToFrontendResult(data)); } private pushToCollector(): void { const size = this._currentFileMatch ? - this._currentFileMatch.lineMatches.reduce((acc, match) => acc + match.offsetAndLengths.length, 0) : + this._currentFileMatch.matches.length : 0; this._batchedCollector.addItem(this._currentFileMatch, size); } @@ -171,6 +196,26 @@ class TextSearchResultsCollector { } } +function extensionResultToFrontendResult(data: vscode.TextSearchResult): ITextSearchResult { + return { + preview: { + match: { + startLineNumber: data.preview.match.start.line, + startColumn: data.preview.match.start.character, + endLineNumber: data.preview.match.end.line, + endColumn: data.preview.match.end.character + }, + text: data.preview.text + }, + range: { + startLineNumber: data.range.start.line, + startColumn: data.range.start.character, + endLineNumber: data.range.end.line, + endColumn: data.range.end.character + } + }; +} + /** * Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every * set of items collected. @@ -253,107 +298,6 @@ class BatchedCollector<T> { } } -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<boolean>): TPromise<boolean> { - 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<CancellationTokenSource>(); @@ -363,7 +307,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 { @@ -372,10 +316,10 @@ class TextSearchEngine { this.activeCancellationTokens = new Set(); } - public search(onProgress: (matches: IFileMatch[]) => void): TPromise<{ limitHit: boolean }> { + public search(onProgress: (matches: IFileMatch[]) => void): TPromise<ISearchCompleteStats> { const folderQueries = this.config.folderQueries; - return new TPromise<{ limitHit: boolean }>((resolve, reject) => { + return new TPromise<ISearchCompleteStats>((resolve, reject) => { this.collector = new TextSearchResultsCollector(onProgress); const onResult = (match: vscode.TextSearchResult, folderIdx: number) => { @@ -399,7 +343,12 @@ class TextSearchEngine { return this.searchInFolder(fq, r => onResult(r, i)); })).then(() => { this.collector.flush(); - resolve({ limitHit: this.isLimitHit }); + resolve({ + limitHit: this.isLimitHit, + stats: { + type: 'textSearchProvider' + } + }); }, (errs: Error[]) => { const errMsg = errs .map(err => toErrorMessage(err)) @@ -479,7 +428,8 @@ class TextSearchEngine { followSymlinks: !this.config.ignoreSymlinks, encoding: this.config.fileEncoding, maxFileSize: this.config.maxFileSize, - maxResults: this.config.maxResults + maxResults: this.config.maxResults, + previewOptions: this.config.previewOptions }; } } @@ -506,7 +456,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; @@ -535,7 +485,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 @@ -556,8 +506,11 @@ class FileSearchEngine { // For each root folder TPromise.join(folderQueries.map(fq => { return this.searchInFolder(fq, onResult); - })).then(cacheKeys => { - resolve({ limitHit: this.isLimitHit, cacheKeys }); + })).then(stats => { + resolve({ + limitHit: this.isLimitHit, + stats: stats[0] // Only looking at single-folder workspace stats... + }); }, (errs: Error[]) => { const errMsg = errs .map(err => toErrorMessage(err)) @@ -568,7 +521,7 @@ class FileSearchEngine { }); } - private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): TPromise<string> { + private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): TPromise<IFileSearchProviderStats> { let cancellation = new CancellationTokenSource(); return new TPromise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); @@ -577,52 +530,57 @@ class FileSearchEngine { 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; + let providerSW: StopWatch; new TPromise(_resolve => process.nextTick(_resolve)) .then(() => { this.activeCancellationTokens.add(cancellation); - folderCacheKey = this.config.cacheKey && (this.config.cacheKey + '_' + fq.folder.fsPath); - + providerSW = StopWatch.create(); return this.provider.provideFileSearchResults( { - pattern: this.config.filePattern || '', - cacheKey: folderCacheKey + pattern: this.config.filePattern || '' }, options, - { report: onProviderResult }, cancellation.token); }) - .then(() => { + .then(results => { + const providerTime = providerSW.elapsed(); + const postProcessSW = StopWatch.create(); + + if (this.isCanceled) { + return null; + } + + 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; } this.matchDirectoryTree(tree, queryTester, onResult); - return null; + return <IFileSearchProviderStats>{ + providerTime, + postProcessTime: postProcessSW.elapsed() + }; }).then( - () => { + stats => { cancellation.dispose(); - resolve(folderCacheKey); + resolve(stats); }, err => { cancellation.dispose(); @@ -731,85 +689,77 @@ class FileSearchEngine { interface IInternalSearchComplete { limitHit: boolean; - cacheKeys: string[]; + stats?: IFileSearchProviderStats; } class FileSearchManager { private static readonly BATCH_SIZE = 512; - private readonly expandedCacheKeys = new Map<string, string[]>(); + fileSearch(config: ISearchQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise<ISearchCompleteStats> { + const engine = new FileSearchEngine(config, provider); - fileSearch(config: ISearchQuery, provider: vscode.SearchProvider, onResult: (matches: IFileMatch[]) => void): TPromise<ISearchCompleteStats> { - let searchP: TPromise; - return new TPromise<ISearchCompleteStats>((c, e) => { - const engine = new FileSearchEngine(config, provider); + let resultCount = 0; + const onInternalResult = (batch: IInternalFileMatch[]) => { + resultCount += batch.length; + onBatch(batch.map(m => this.rawMatchToSearchItem(m))); + }; - const onInternalResult = (progress: IInternalFileMatch[]) => { - onResult(progress.map(m => this.rawMatchToSearchItem(m))); - }; - - searchP = this.doSearch(engine, FileSearchManager.BATCH_SIZE, onInternalResult).then( - result => { - if (config.cacheKey) { - this.expandedCacheKeys.set(config.cacheKey, result.cacheKeys); + return this.doSearch(engine, FileSearchManager.BATCH_SIZE, onInternalResult, token).then( + result => { + return <ISearchCompleteStats>{ + limitHit: result.limitHit, + stats: { + fromCache: false, + type: 'fileSearchProvider', + resultCount, + detailStats: result.stats } - - c({ - limitHit: result.limitHit - }); - }, - e); - }, () => { - if (searchP) { - searchP.cancel(); - } - }); - } - - 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, onResultBatch: (matches: IInternalFileMatch[]) => void): TPromise<IInternalSearchComplete> { - 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(_onResult).then(result => { - if (batch.length) { - onResultBatch(batch); - } - - c(result); - }, error => { - if (batch.length) { - onResultBatch(batch); - } - - e(error); - }); - }, () => { + private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): TPromise<IInternalSearchComplete> { + token.onCancellationRequested(() => { engine.cancel(); }); + + const _onResult = match => { + if (match) { + batch.push(match); + if (batchSize > 0 && batch.length >= batchSize) { + onResultBatch(batch); + batch = []; + } + } + }; + + let batch: IInternalFileMatch[] = []; + return engine.search(_onResult).then(result => { + if (batch.length) { + onResultBatch(batch); + } + + return result; + }, error => { + if (batch.length) { + onResultBatch(batch); + } + + return TPromise.wrapError(error); + }); } } 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<boolean> { 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/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<WebviewPanelHandle, ExtHostWebviewPanel>(); private readonly _serializers = new Map<string, vscode.WebviewPanelSerializer>(); @@ -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 ae1f6739927..9d2cfe912d6 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { posix, relative, join } from 'path'; +import { join, relative } 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 { Counter } from 'vs/base/common/numbers'; import { normalize } from 'vs/base/common/paths'; import { isLinux } from 'vs/base/common/platform'; -import { basenameOrAuthority, isEqual } from 'vs/base/common/resources'; +import { basenameOrAuthority, dirname, isEqual } 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'; @@ -107,8 +108,8 @@ class ExtHostWorkspaceImpl extends Workspace { private readonly _workspaceFolders: vscode.WorkspaceFolder[] = []; private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>(); - private constructor(id: string, name: string, folders: vscode.WorkspaceFolder[]) { - super(id, name, folders.map(f => new WorkspaceFolder(f))); + private constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[]) { + super(id, folders.map(f => new WorkspaceFolder(f))); // setup the workspace folder data structure folders.forEach(folder => { @@ -117,6 +118,10 @@ class ExtHostWorkspaceImpl extends Workspace { }); } + get name(): string { + return this._name; + } + get workspaceFolders(): vscode.WorkspaceFolder[] { return this._workspaceFolders.slice(0); } @@ -124,7 +129,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()); } @@ -136,8 +141,6 @@ class ExtHostWorkspaceImpl extends Workspace { export class ExtHostWorkspace implements ExtHostWorkspaceShape { - private static _requestIdPool = 0; - private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>(); private readonly _proxy: MainThreadWorkspaceShape; @@ -148,12 +151,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event; - private readonly _activeSearchCallbacks = []; + private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = []; constructor( mainContext: IMainContext, data: IWorkspaceData, - private _logService: ILogService + private _logService: ILogService, + private _requestIdProvider: Counter ) { this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace); this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService); @@ -166,6 +170,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { return this._actualWorkspace; } + get name(): string { + return this._actualWorkspace ? this._actualWorkspace.name : undefined; + } + private get _actualWorkspace(): ExtHostWorkspaceImpl { return this._unconfirmedWorkspace || this._confirmedWorkspace; } @@ -340,7 +348,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { findFiles(include: vscode.GlobPattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> { this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId}, entryPoint: findFiles`); - const requestId = ExtHostWorkspace._requestIdPool++; + const requestId = this._requestIdProvider.getNext(); let includePattern: string; let includeFolder: string; @@ -374,7 +382,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token?: vscode.CancellationToken) { this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`); - const requestId = ExtHostWorkspace._requestIdPool++; + const requestId = this._requestIdProvider.getNext(); const globPatternToString = (pattern: vscode.GlobPattern | string) => { if (typeof pattern === 'string') { @@ -390,26 +398,36 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { disregardExcludeSettings: options.exclude === null, fileEncoding: options.encoding, maxResults: options.maxResults, + previewOptions: options.previewOptions, includePattern: options.include && globPatternToString(options.include), excludePattern: options.exclude && globPatternToString(options.exclude) }; + let isCanceled = false; + this._activeSearchCallbacks[requestId] = p => { - p.lineMatches.forEach(lineMatch => { - lineMatch.offsetAndLengths.forEach(offsetAndLength => { - const range = new Range(lineMatch.lineNumber, offsetAndLength[0], lineMatch.lineNumber, offsetAndLength[0] + offsetAndLength[1]); - callback({ - uri: URI.revive(p.resource), - preview: { text: lineMatch.preview, match: range }, - range - }); + if (isCanceled) { + return; + } + + p.matches.forEach(match => { + callback({ + uri: URI.revive(p.resource), + preview: { + text: match.preview.text, + match: new Range(match.preview.match.startLineNumber, match.preview.match.startColumn, match.preview.match.endLineNumber, match.preview.match.endColumn) + }, + range: new Range(match.range.startLineNumber, match.range.startColumn, match.range.endLineNumber, match.range.endColumn) }); }); }; 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/api/shared/tasks.ts b/src/vs/workbench/api/shared/tasks.ts index ee98ba6cccd..c03895bfb41 100644 --- a/src/vs/workbench/api/shared/tasks.ts +++ b/src/vs/workbench/api/shared/tasks.ts @@ -107,7 +107,6 @@ export interface TaskFilterDTO { export interface TaskSystemInfoDTO { scheme: string; - host: string; - port: number; + authority: string; platform: string; } \ No newline at end of file diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index a473438cb5a..20cb58d1da7 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 { ILabelService } from 'vs/platform/label/common/label'; +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<IQuickPickItem>, CancellationToken]) { + const quickInputService = accessor.get(IQuickInputService); + const labelService = accessor.get(ILabelService); 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: labelService.getUriLabel(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<IQuickPickItem>; 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 8db6cf1d71e..ec1f0660606 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -187,7 +187,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); } const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({ diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 2de5aa211ec..fa7213df83f 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 { ILabelService } from 'vs/platform/label/common/label'; 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, + @ILabelService protected labelService: ILabelService ) { super(container, options); @@ -193,8 +191,7 @@ 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.labelService.getUriLabel(resource); } iconLabelOptions.title = this.computedPathLabel; @@ -262,7 +259,6 @@ export class EditorLabel extends ResourceLabel { export interface IFileLabelOptions extends IResourceLabelOptions { hideLabel?: boolean; hidePath?: boolean; - root?: uri; } export class FileLabel extends ResourceLabel { @@ -271,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, + @ILabelService labelService: ILabelService ) { - super(container, options, extensionService, contextService, configurationService, modeService, modelService, environmentService, decorationsService, themeService); + super(container, options, extensionService, configurationService, modeService, modelService, decorationsService, themeService, labelService); } setFile(resource: uri, options?: IFileLabelOptions): void { @@ -302,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.labelService.getUriLabel(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..5008ce7eb35 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,21 +64,17 @@ 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; private _panelHeight: number; private _panelWidth: number; - // Take parts as an object bag since instatation service does not have typings for constructors with 9+ arguments constructor( private parent: HTMLElement, private workbenchContainer: HTMLElement, private parts: { titlebar: TitlebarPart, - menubar: MenubarPart, activitybar: ActivitybarPart, editor: EditorPart, sidebar: SidebarPart, @@ -224,9 +219,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 +311,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 +412,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 +425,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 +564,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 +572,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 +593,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 +609,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 +647,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 +657,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr } getVerticalSashTop(sash: Sash): number { - return this.headingHeight; + return this.titlebarHeight; } getVerticalSashLeft(sash: Sash): number { @@ -703,7 +684,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 { @@ -722,7 +703,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr return this.panelMaximized; } - // change part size along the main axis resizePart(part: Parts, sizeChange: number): void { const panelPosition = this.partService.getPanelPosition(); const sizeChangePxWidth = this.workbenchSize.width * (sizeChange / 100); 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..005e8bfdd3e 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'; @@ -122,28 +122,29 @@ export class GlobalActivityActionItem extends ActivityActionItem { // Context menus are triggered on mouse down so that an item can be picked // and executed with releasing the mouse over it - this.$container.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { + + this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => { DOM.EventHelper.stop(e, true); const event = new StandardMouseEvent(e); this.showContextMenu({ x: event.posx, y: event.posy }); - }); + })); - this.$container.on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => { + this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { DOM.EventHelper.stop(e, true); - this.showContextMenu(this.$container.getHTMLElement()); + this.showContextMenu(this.container); } - }); + })); - this.$container.on(TouchEventType.Tap, (e: GestureEvent) => { + this._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => { DOM.EventHelper.stop(e, true); const event = new StandardMouseEvent(e); this.showContextMenu({ x: event.posx, y: event.posy }); - }); + })); } private showContextMenu(location: HTMLElement | { x: number, y: number }): void { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 13f5812793c..70aa55141f3 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'; @@ -20,16 +19,19 @@ import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/s import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/toggleActivityBarVisibility'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +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 { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; -import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; -import { Dimension } from 'vs/base/browser/dom'; +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, 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/compositeBarActions'; +import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; interface IPlaceholderComposite { id: string; @@ -63,6 +65,7 @@ export class ActivitybarPart extends Part { @IInstantiationService private instantiationService: IInstantiationService, @IPartService private partService: IPartService, @IThemeService themeService: IThemeService, + @ILifecycleService private lifecycleService: ILifecycleService, @IStorageService private storageService: IStorageService, @IExtensionService private extensionService: IExtensionService ) { @@ -84,20 +87,15 @@ export class ActivitybarPart extends Part { overflowActionSize: ActivitybarPart.ACTION_HEIGHT })); - const previousState = this.storageService.get(ActivitybarPart.PLACEHOLDER_VIEWLETS, StorageScope.GLOBAL, void 0); - if (previousState) { - let parsedPreviousState = <IPlaceholderComposite[]>JSON.parse(previousState); - parsedPreviousState.forEach((s) => { - if (typeof s.iconUrl === 'object') { - s.iconUrl = URI.revive(s.iconUrl); - } else { - s.iconUrl = void 0; - } - }); - this.placeholderComposites = parsedPreviousState; - } else { - this.placeholderComposites = this.compositeBar.getCompositesFromStorage().map(id => (<IPlaceholderComposite>{ id, iconUrl: void 0 })); - } + const previousState = this.storageService.get(ActivitybarPart.PLACEHOLDER_VIEWLETS, StorageScope.GLOBAL, '[]'); + this.placeholderComposites = <IPlaceholderComposite[]>JSON.parse(previousState); + this.placeholderComposites.forEach((s) => { + if (typeof s.iconUrl === 'object') { + s.iconUrl = URI.revive(s.iconUrl); + } else { + s.iconUrl = void 0; + } + }); this.registerListeners(); this.updateCompositebar(); @@ -116,7 +114,7 @@ export class ActivitybarPart extends Part { if (enabled) { this.compositeBar.addComposite(this.viewletService.getViewlet(id)); } else { - this.removeComposite(id); + this.removeComposite(id, true); } })); @@ -124,7 +122,7 @@ export class ActivitybarPart extends Part { } private onDidRegisterExtensions(): void { - this.removeNotExistingPlaceholderComposites(); + this.removeNotExistingComposites(); this.updateCompositebar(); } @@ -152,35 +150,61 @@ 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); - return $result.getHTMLElement(); + 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 + // using --webkit-mask with a background color that is different from the image + // The workaround is to promote the element onto its own drawing layer. We do + // this only after the workbench has loaded because otherwise there is ugly flicker. + if (isMacintosh) { + this.lifecycleService.when(LifecyclePhase.Running).then(() => { + scheduleAtNextAnimationFrame(() => { // another delay... + scheduleAtNextAnimationFrame(() => { // ...to prevent more flickering on startup + registerThemingParticipant((theme, collector) => { + const activityBarForeground = theme.getColor(ACTIVITY_BAR_FOREGROUND); + if (activityBarForeground && !activityBarForeground.equals(Color.white)) { + // only apply this workaround if the color is different from the image one (white) + collector.addRule('.monaco-workbench .activitybar > .content .monaco-action-bar .action-label { will-change: transform; }'); + } + }); + }); + }); + }); + } + + 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 { @@ -254,17 +278,21 @@ export class ActivitybarPart extends Part { } } - private removeNotExistingPlaceholderComposites(): void { - const viewlets = this.viewletService.getViewlets(); + private removeNotExistingComposites(): void { + const viewlets = this.viewletService.getAllViewlets(); for (const { id } of this.placeholderComposites) { if (viewlets.every(viewlet => viewlet.id !== id)) { - this.removeComposite(id); + this.removeComposite(id, false); } } } - private removeComposite(compositeId: string): void { - this.compositeBar.removeComposite(compositeId); + private removeComposite(compositeId: string, hide: boolean): void { + if (hide) { + this.compositeBar.hideComposite(compositeId); + } else { + this.compositeBar.removeComposite(compositeId); + } const compositeActions = this.compositeActions[compositeId]; if (compositeActions) { compositeActions.activityAction.dispose(); @@ -308,7 +336,7 @@ export class ActivitybarPart extends Part { } shutdown(): void { - const state = this.viewletService.getViewlets().map(viewlet => ({ id: viewlet.id, iconUrl: viewlet.iconUrl })); + const state = this.viewletService.getAllViewlets().map(({ id, iconUrl }) => ({ id, iconUrl })); this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEWLETS, JSON.stringify(state), StorageScope.GLOBAL); super.shutdown(); diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts similarity index 84% rename from src/vs/workbench/browser/parts/compositebar/compositeBar.ts rename to src/vs/workbench/browser/parts/compositeBar.ts index 54f2748037a..f1a10123c37 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -14,12 +14,14 @@ 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, DraggedCompositeIdentifier } from 'vs/workbench/browser/parts/compositeBarActions'; import { TPromise } from 'vs/base/common/winjs.base'; import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Widget } from 'vs/base/browser/ui/widget'; +import { isUndefinedOrNull } from 'vs/base/common/types'; +import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; export interface ICompositeBarOptions { icon: boolean; @@ -46,26 +48,23 @@ export class CompositeBar extends Widget implements ICompositeBar { private compositeOverflowActionItem: CompositeOverflowActivityActionItem; private model: CompositeBarModel; - private storedState: ISerializedCompositeBarItem[]; private visibleComposites: string[]; private compositeSizeInBar: Map<string, number>; + private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier>; + constructor( private options: ICompositeBarOptions, @IInstantiationService private instantiationService: IInstantiationService, - @IStorageService private storageService: IStorageService, + @IStorageService storageService: IStorageService, @IContextMenuService private contextMenuService: IContextMenuService ) { super(); - this.model = new CompositeBarModel(options); - this.storedState = this.loadCompositeItemsFromStorage(); + this.model = new CompositeBarModel(options, storageService); this.visibleComposites = []; this.compositeSizeInBar = new Map<string, number>(); - } - - getCompositesFromStorage(): string[] { - return this.storedState.map(s => s.id); + this.compositeTransfer = LocalSelectionTransfer.getInstance<DraggedCompositeIdentifier>(); } create(parent: HTMLElement): HTMLElement { @@ -88,12 +87,13 @@ export class CompositeBar extends Widget implements ICompositeBar { // Allow to drop at the end to move composites to the end this._register(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId) { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { EventHelper.stop(e, true); - CompositeActionItem.clearDraggedComposite(); - const targetItem = this.model.items[this.model.items.length - 1]; + const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id; + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); + + const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1]; if (targetItem && targetItem.id !== draggedCompositeId) { this.move(draggedCompositeId, targetItem.id); } @@ -113,38 +113,17 @@ export class CompositeBar extends Widget implements ICompositeBar { if (this.compositeSizeInBar.size === 0) { // Compute size of each composite by getting the size from the css renderer // Size is later used for overflow computation - this.computeSizes(this.model.items); + this.computeSizes(this.model.visibleItems); } this.updateCompositeSwitcher(); } addComposite({ id, name, order }: { id: string; name: string, order: number }): void { - const state = this.storedState.filter(s => s.id === id)[0]; - const pinned = state ? state.pinned : true; - let index = order >= 0 ? order : this.model.items.length; - - if (state) { - // Find the index by looking its previous item - index = 0; - for (let i = this.storedState.indexOf(state) - 1; i >= 0; i--) { - const previousItemId = this.storedState[i].id; - const previousItemIndex = this.model.findIndex(previousItemId); - if (previousItemIndex !== -1) { - index = previousItemIndex + 1; - break; - } - } - } - // Add to the model - if (this.model.add(id, name, order, index)) { + if (this.model.add(id, name, order)) { this.computeSizes([this.model.findItem(id)]); - if (pinned) { - this.pin(id); - } else { - this.updateCompositeSwitcher(); - } + this.updateCompositeSwitcher(); } } @@ -161,6 +140,12 @@ export class CompositeBar extends Widget implements ICompositeBar { } } + hideComposite(id: string): void { + if (this.model.hide(id)) { + this.updateCompositeSwitcher(); + } + } + activateComposite(id: string): void { const previousActiveItem = this.model.activeItem; if (this.model.activate(id)) { @@ -282,7 +267,7 @@ export class CompositeBar extends Widget implements ICompositeBar { return; // We have not been rendered yet so there is nothing to update. } - let compositesToShow = this.model.items.filter(item => + let compositesToShow = this.model.visibleItems.filter(item => item.pinned || (this.model.activeItem && this.model.activeItem.id === item.id) /* Show the active composite even if it is not pinned */ ).map(item => item.id); @@ -385,11 +370,11 @@ export class CompositeBar extends Widget implements ICompositeBar { } // Persist - this.saveCompositeItems(); + this.model.saveState(); } private getOverflowingComposites(): { id: string, name: string }[] { - let overflowingIds = this.model.items.filter(item => item.pinned).map(item => item.id); + let overflowingIds = this.model.visibleItems.filter(item => item.pinned).map(item => item.id); // Show the active composite even if it is not pinned if (this.model.activeItem && !this.model.activeItem.pinned) { @@ -397,13 +382,13 @@ export class CompositeBar extends Widget implements ICompositeBar { } overflowingIds = overflowingIds.filter(compositeId => this.visibleComposites.indexOf(compositeId) === -1); - return this.model.items.filter(c => overflowingIds.indexOf(c.id) !== -1); + return this.model.visibleItems.filter(c => overflowingIds.indexOf(c.id) !== -1); } private showContextMenu(e: MouseEvent): void { EventHelper.stop(e, true); const event = new StandardMouseEvent(e); - const actions: IAction[] = this.model.items + const actions: IAction[] = this.model.visibleItems .map(({ id, name, activityAction }) => (<IAction>{ id, label: name, @@ -427,24 +412,13 @@ export class CompositeBar extends Widget implements ICompositeBar { getActions: () => TPromise.as(actions), }); } - - private loadCompositeItemsFromStorage(): ISerializedCompositeBarItem[] { - const storedStates = <Array<string | ISerializedCompositeBarItem>>JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, '[]')); - const compositeStates = <ISerializedCompositeBarItem[]>storedStates.map(c => - typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true } : c); - return compositeStates; - } - - private saveCompositeItems(): void { - this.storedState = this.model.toJSON(); - this.storageService.store(this.options.storageId, JSON.stringify(this.storedState), StorageScope.GLOBAL); - } } interface ISerializedCompositeBarItem { id: string; pinned: boolean; order: number; + visible: boolean; } interface ICompositeBarItem extends ISerializedCompositeBarItem { @@ -456,15 +430,28 @@ interface ICompositeBarItem extends ISerializedCompositeBarItem { class CompositeBarModel { - readonly items: ICompositeBarItem[] = []; + private readonly options: ICompositeBarOptions; + readonly items: ICompositeBarItem[]; + activeItem: ICompositeBarItem; - constructor(private options: ICompositeBarOptions) { } + constructor( + options: ICompositeBarOptions, + private storageService: IStorageService, + ) { + this.options = options; + this.items = this.loadItemStates(); + } - private createCompositeBarItem(id: string, name: string, order: number, pinned: boolean): ICompositeBarItem { + get visibleItems(): ICompositeBarItem[] { + return this.items.filter(item => item.visible); + } + + private createCompositeBarItem(id: string, name: string, order: number, pinned: boolean, visible: boolean): ICompositeBarItem { const options = this.options; return { - id, name, pinned, order, activity: [], + id, name, pinned, order, visible, + activity: [], get activityAction() { return options.getActivityAction(id); }, @@ -474,20 +461,31 @@ class CompositeBarModel { }; } - add(id: string, name: string, order: number, index: number): boolean { + add(id: string, name: string, order: number): boolean { const item = this.findItem(id); if (item) { - item.order = order; + let changed = false; item.name = name; - return false; + if (!isUndefinedOrNull(order)) { + changed = item.order !== order; + item.order = order; + } + if (!item.visible) { + item.visible = true; + changed = true; + } + return changed; } else { - if (index === void 0) { - index = 0; + const item = this.createCompositeBarItem(id, name, order, true, true); + if (isUndefinedOrNull(order)) { + this.items.push(item); + } else { + let index = 0; while (index < this.items.length && this.items[index].order < order) { index++; } + this.items.splice(index, 0, item); } - this.items.splice(index, 0, this.createCompositeBarItem(id, name, order, false)); return true; } } @@ -502,6 +500,19 @@ class CompositeBarModel { return false; } + hide(id: string): boolean { + for (const item of this.items) { + if (item.id === id) { + if (item.visible) { + item.visible = false; + return true; + } + return false; + } + } + return false; + } + move(compositeId: string, toCompositeId: string): boolean { const fromIndex = this.findIndex(compositeId); @@ -610,7 +621,7 @@ class CompositeBarModel { return this.items.filter(item => item.id === id)[0]; } - findIndex(id: string): number { + private findIndex(id: string): number { for (let index = 0; index < this.items.length; index++) { if (this.items[index].id === id) { return index; @@ -619,7 +630,16 @@ class CompositeBarModel { return -1; } - toJSON(): ISerializedCompositeBarItem[] { - return this.items.map(({ id, pinned, order }) => ({ id, pinned, order })); + private loadItemStates(): ICompositeBarItem[] { + const storedStates = <Array<string | ISerializedCompositeBarItem>>JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, '[]')); + return <ICompositeBarItem[]>storedStates.map(c => { + const serialized: ISerializedCompositeBarItem = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: void 0, visible: true } : c; + return this.createCompositeBarItem(serialized.id, void 0, serialized.order, serialized.pinned, isUndefinedOrNull(serialized.visible) ? true : serialized.visible); + }); + } + + saveState(): void { + const serialized = this.items.map(({ id, pinned, order, visible }) => ({ id, pinned, order, visible })); + this.storageService.store(this.options.storageId, JSON.stringify(serialized), StorageScope.GLOBAL); } } diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts similarity index 71% rename from src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts rename to src/vs/workbench/browser/parts/compositeBarActions.ts index 6d6647e8c5a..59d52a54eb3 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -9,7 +9,6 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; import * as dom from 'vs/base/browser/dom'; -import { Builder, $ } from 'vs/base/browser/builder'; import { BaseActionItem, IBaseActionItemOptions, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { dispose, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -22,6 +21,7 @@ import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IActivity } from 'vs/workbench/common/activity'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event, Emitter } from 'vs/base/common/event'; +import { DragAndDropObserver, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; export interface ICompositeActivity { badge: IBadge; @@ -124,12 +124,12 @@ export interface IActivityActionItemOptions extends IBaseActionItemOptions { } export class ActivityActionItem extends BaseActionItem { - protected $container: Builder; - protected $label: Builder; - protected $badge: Builder; + protected container: HTMLElement; + protected label: HTMLElement; + protected badge: HTMLElement; protected options: IActivityActionItemOptions; - private $badgeContent: Builder; + private badgeContent: HTMLElement; private badgeDisposable: IDisposable = Disposable.None; private mouseUpTimeout: number; @@ -140,9 +140,9 @@ export class ActivityActionItem extends BaseActionItem { ) { super(null, action, options); - this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose); - action.onDidChangeActivity(this.updateActivity, this, this._callOnDispose); - action.onDidChangeBadge(this.updateBadge, this, this._callOnDispose); + this._register(this.themeService.onThemeChange(this.onThemeChange, this)); + this._register(action.onDidChangeActivity(this.updateActivity, this)); + this._register(action.onDidChangeBadge(this.updateBadge, this)); } protected get activity(): IActivity { @@ -153,58 +153,59 @@ export class ActivityActionItem extends BaseActionItem { const theme = this.themeService.getTheme(); // Label - if (this.$label && this.options.icon) { + if (this.label && this.options.icon) { const background = theme.getColor(this.options.colors.backgroundColor); - this.$label.style('background-color', background ? background.toString() : null); + this.label.style.backgroundColor = background ? background.toString() : null; } // Badge - if (this.$badgeContent) { + if (this.badgeContent) { const badgeForeground = theme.getColor(this.options.colors.badgeForeground); const badgeBackground = theme.getColor(this.options.colors.badgeBackground); const contrastBorderColor = theme.getColor(contrastBorder); - this.$badgeContent.style('color', badgeForeground ? badgeForeground.toString() : null); - this.$badgeContent.style('background-color', badgeBackground ? badgeBackground.toString() : null); + this.badgeContent.style.color = badgeForeground ? badgeForeground.toString() : null; + this.badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : null; - this.$badgeContent.style('border-style', contrastBorderColor ? 'solid' : null); - this.$badgeContent.style('border-width', contrastBorderColor ? '1px' : null); - this.$badgeContent.style('border-color', contrastBorderColor ? contrastBorderColor.toString() : null); + this.badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : null; + this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : null; + this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : null; } } render(container: HTMLElement): void { super.render(container); + this.container = container; + // Make the container tab-able for keyboard navigation - this.$container = $(container).attr({ - tabIndex: '0', - role: 'button' - }); + this.container.tabIndex = 0; + this.container.setAttribute('role', 'button'); // Try hard to prevent keyboard only focus feedback when using mouse - this.$container.on(dom.EventType.MOUSE_DOWN, () => { - this.$container.addClass('clicked'); - }); + this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_DOWN, () => { + dom.addClass(this.container, 'clicked'); + })); - this.$container.on(dom.EventType.MOUSE_UP, () => { + this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_UP, () => { if (this.mouseUpTimeout) { clearTimeout(this.mouseUpTimeout); } this.mouseUpTimeout = setTimeout(() => { - this.$container.removeClass('clicked'); + dom.removeClass(this.container, 'clicked'); }, 800); // delayed to prevent focus feedback from showing on mouse up - }); + })); // Label - this.$label = $('a.action-label').appendTo(this.builder); + this.label = dom.append(this.element, dom.$('a.action-label')); - this.$badge = this.builder.clone().div({ 'class': 'badge' }, badge => { - this.$badgeContent = badge.div({ 'class': 'badge-content' }); - }); - this.$badge.hide(); + // Badge + this.badge = dom.append(this.element, dom.$('.badge')); + this.badgeContent = dom.append(this.badge, dom.$('.badge-content')); + + dom.hide(this.badge); this.updateActivity(); this.updateStyles(); @@ -222,7 +223,7 @@ export class ActivityActionItem extends BaseActionItem { protected updateBadge(): void { const action = this.getAction(); - if (!this.$badge || !this.$badgeContent || !(action instanceof ActivityAction)) { + if (!this.badge || !this.badgeContent || !(action instanceof ActivityAction)) { return; } @@ -232,8 +233,8 @@ export class ActivityActionItem extends BaseActionItem { this.badgeDisposable.dispose(); this.badgeDisposable = Disposable.None; - this.$badgeContent.empty(); - this.$badge.hide(); + dom.clearNode(this.badgeContent); + dom.hide(this.badge); if (badge) { @@ -246,30 +247,30 @@ export class ActivityActionItem extends BaseActionItem { } else if (badge.number > 999) { number = number.charAt(0) + 'k'; } - this.$badgeContent.text(number); - this.$badge.show(); + this.badgeContent.textContent = number; + dom.show(this.badge); } } // Text else if (badge instanceof TextBadge) { - this.$badgeContent.text(badge.text); - this.$badge.show(); + this.badgeContent.textContent = badge.text; + dom.show(this.badge); } // Text else if (badge instanceof IconBadge) { - this.$badge.show(); + dom.show(this.badge); } // Progress else if (badge instanceof ProgressBadge) { - this.$badge.show(); + dom.show(this.badge); } if (clazz) { - this.$badge.addClass(clazz); - this.badgeDisposable = toDisposable(() => this.$badge.removeClass(clazz)); + dom.addClasses(this.badge, clazz); + this.badgeDisposable = toDisposable(() => dom.removeClasses(this.badge, clazz)); } } @@ -289,18 +290,18 @@ export class ActivityActionItem extends BaseActionItem { private updateLabel(): void { if (this.activity.cssClass) { - this.$label.addClass(this.activity.cssClass); + dom.addClasses(this.label, this.activity.cssClass); } if (!this.options.icon) { - this.$label.text(this.getAction().label); + this.label.textContent = this.getAction().label; } } private updateTitle(title: string): void { - [this.$label, this.$badge, this.$container].forEach(b => { - if (b) { - b.attr('aria-label', title); - b.title(title); + [this.label, this.badge, this.container].forEach(element => { + if (element) { + element.setAttribute('aria-label', title); + element.title = title; } }); } @@ -312,7 +313,7 @@ export class ActivityActionItem extends BaseActionItem { clearTimeout(this.mouseUpTimeout); } - this.$badge.destroy(); + dom.removeNode(this.badge); } } @@ -359,7 +360,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem { this.actions = this.getActions(); this.contextMenuService.showContextMenu({ - getAnchor: () => this.builder.getHTMLElement(), + getAnchor: () => this.element, getActions: () => TPromise.as(this.actions), onHide: () => dispose(this.actions) }); @@ -408,13 +409,21 @@ class ManageExtensionAction extends Action { } } +export class DraggedCompositeIdentifier { + constructor(private _compositeId: string) { } + + get id(): string { + return this._compositeId; + } +} + export class CompositeActionItem extends ActivityActionItem { private static manageExtensionAction: ManageExtensionAction; - private static draggedCompositeId: string; private compositeActivity: IActivity; private cssClass: string; + private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier>; constructor( private compositeActivityAction: ActivityAction, @@ -430,12 +439,13 @@ export class CompositeActionItem extends ActivityActionItem { super(compositeActivityAction, { draggable: true, colors, icon }, themeService); this.cssClass = compositeActivityAction.class; + this.compositeTransfer = LocalSelectionTransfer.getInstance<DraggedCompositeIdentifier>(); if (!CompositeActionItem.manageExtensionAction) { CompositeActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction); } - compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = null; this.updateActivity(); }, this, this._callOnDispose); + this._register(compositeActivityAction.onDidChangeActivity(() => { this.compositeActivity = null; this.updateActivity(); }, this)); } protected get activity(): IActivity { @@ -473,71 +483,64 @@ export class CompositeActionItem extends ActivityActionItem { this._updateChecked(); this._updateEnabled(); - this.$container.on('contextmenu', e => { + this._register(dom.addDisposableListener(this.container, dom.EventType.CONTEXT_MENU, e => { dom.EventHelper.stop(e, true); this.showContextMenu(container); - }); + })); // Allow to drag - this.$container.on(dom.EventType.DRAG_START, (e: DragEvent) => { + this._register(dom.addDisposableListener(this.container, dom.EventType.DRAG_START, (e: DragEvent) => { e.dataTransfer.effectAllowed = 'move'; - this.setDraggedComposite(this.activity.id); + + // Registe as dragged to local transfer + this.compositeTransfer.setData([new DraggedCompositeIdentifier(this.activity.id)], DraggedCompositeIdentifier.prototype); // Trigger the action even on drag start to prevent clicks from failing that started a drag if (!this.getAction().checked) { this.getAction().run(); } - }); + })); - // Drag enter - let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470 - this.$container.on(dom.EventType.DRAG_ENTER, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId && draggedCompositeId !== this.activity.id) { - counter++; - this.updateFromDragging(container, true); - } - }); + this._register(new DragAndDropObserver(this.container, { + onDragEnter: e => { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id !== this.activity.id) { + this.updateFromDragging(container, true); + } + }, - // Drag leave - this.$container.on(dom.EventType.DRAG_LEAVE, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId) { - counter--; - if (counter === 0) { + onDragLeave: e => { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { this.updateFromDragging(container, false); } + }, + + onDragEnd: e => { + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { + this.updateFromDragging(container, false); + + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); + } + }, + + onDrop: e => { + dom.EventHelper.stop(e, true); + + if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) { + const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id; + if (draggedCompositeId !== this.activity.id) { + this.updateFromDragging(container, false); + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); + + this.compositeBar.move(draggedCompositeId, this.activity.id); + } + } } - }); - - // Drag end - this.$container.on(dom.EventType.DRAG_END, (e: DragEvent) => { - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId) { - counter = 0; - this.updateFromDragging(container, false); - - CompositeActionItem.clearDraggedComposite(); - } - }); - - // Drop - this.$container.on(dom.EventType.DROP, (e: DragEvent) => { - dom.EventHelper.stop(e, true); - - const draggedCompositeId = CompositeActionItem.getDraggedCompositeId(); - if (draggedCompositeId && draggedCompositeId !== this.activity.id) { - this.updateFromDragging(container, false); - CompositeActionItem.clearDraggedComposite(); - - this.compositeBar.move(draggedCompositeId, this.activity.id); - } - }); + })); // Activate on drag over to reveal targets - [this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => { - if (!CompositeActionItem.getDraggedCompositeId() && !this.getAction().checked) { + [this.badge, this.label].forEach(b => new DelayedDragHandler(b, () => { + if (!this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && !this.getAction().checked) { this.getAction().run(); } })); @@ -552,18 +555,6 @@ export class CompositeActionItem extends ActivityActionItem { element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null; } - static getDraggedCompositeId(): string { - return CompositeActionItem.draggedCompositeId; - } - - private setDraggedComposite(compositeId: string): void { - CompositeActionItem.draggedCompositeId = compositeId; - } - - static clearDraggedComposite(): void { - CompositeActionItem.draggedCompositeId = void 0; - } - private showContextMenu(container: HTMLElement): void { const actions: Action[] = [this.toggleCompositePinnedAction]; if ((<any>this.compositeActivityAction.activity).extensionId) { @@ -587,42 +578,44 @@ export class CompositeActionItem extends ActivityActionItem { } focus(): void { - this.$container.domFocus(); + this.container.focus(); } protected _updateClass(): void { if (this.cssClass) { - this.$label.removeClass(this.cssClass); + dom.removeClasses(this.label, this.cssClass); } this.cssClass = this.getAction().class; if (this.cssClass) { - this.$label.addClass(this.cssClass); + dom.addClasses(this.label, this.cssClass); } } protected _updateChecked(): void { if (this.getAction().checked) { - this.$container.addClass('checked'); + dom.addClass(this.container, 'checked'); + this.container.setAttribute('aria-label', nls.localize('compositeActive', "{0} active", this.container.title)); } else { - this.$container.removeClass('checked'); + dom.removeClass(this.container, 'checked'); + this.container.setAttribute('aria-label', this.container.title); } } protected _updateEnabled(): void { if (this.getAction().enabled) { - this.builder.removeClass('disabled'); + dom.removeClass(this.element, 'disabled'); } else { - this.builder.addClass('disabled'); + dom.addClass(this.element, 'disabled'); } } dispose(): void { super.dispose(); - CompositeActionItem.clearDraggedComposite(); + this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype); - this.$label.destroy(); + dom.removeNode(this.label); } } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 52578559e00..70b3371097b 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -10,7 +10,6 @@ import * as nls from 'vs/nls'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Builder, $ } from 'vs/base/browser/builder'; import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; @@ -35,7 +34,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension } from 'vs/base/browser/dom'; +import { Dimension, append, $, addClass, hide, removeNode, show, addClasses } from 'vs/base/browser/dom'; export interface ICompositeTitleLabel { @@ -58,7 +57,7 @@ export abstract class CompositePart<T extends Composite> extends Part { protected toolBar: ToolBar; private instantiatedCompositeListeners: IDisposable[]; - private mapCompositeToCompositeContainer: { [compositeId: string]: Builder; }; + private mapCompositeToCompositeContainer: { [compositeId: string]: HTMLElement; }; private mapActionsBindingToComposite: { [compositeId: string]: () => void; }; private mapProgressServiceToComposite: { [compositeId: string]: IProgressService; }; private activeComposite: Composite; @@ -222,13 +221,12 @@ export abstract class CompositePart<T extends Composite> extends Part { if (!compositeContainer) { // Build Container off-DOM - compositeContainer = $().div({ - 'class': ['composite', this.compositeCSSClass], - id: composite.getId() - }, div => { - createCompositePromise = composite.create(div.getHTMLElement()).then(() => { - composite.updateStyles(); - }); + compositeContainer = $('.composite'); + addClasses(compositeContainer, this.compositeCSSClass); + compositeContainer.id = composite.getId(); + + createCompositePromise = composite.create(compositeContainer).then(() => { + composite.updateStyles(); }); // Remember composite container @@ -255,8 +253,8 @@ export abstract class CompositePart<T extends Composite> extends Part { } // Take Composite on-DOM and show - compositeContainer.build(this.getContentArea()); - compositeContainer.show(); + this.getContentArea().appendChild(compositeContainer); + show(compositeContainer); // Setup action runner this.toolBar.actionRunner = composite.getActionRunner(); @@ -389,8 +387,8 @@ export abstract class CompositePart<T extends Composite> extends Part { return composite.setVisible(false).then(() => { // Take Container Off-DOM and hide - compositeContainer.offDOM(); - compositeContainer.hide(); + removeNode(compositeContainer); + hide(compositeContainer); // Clear any running Progress this.progressBar.stop().hide(); @@ -406,45 +404,38 @@ export abstract class CompositePart<T extends Composite> extends Part { createTitleArea(parent: HTMLElement): HTMLElement { // Title Area Container - const titleArea = $(parent).div({ - 'class': ['composite', 'title'] - }); + const titleArea = append(parent, $('.composite')); + addClass(titleArea, 'title'); // Left Title Label - this.titleLabel = this.createTitleLabel(titleArea.getHTMLElement()); + this.titleLabel = this.createTitleLabel(titleArea); // Right Actions Container - $(titleArea).div({ - 'class': 'title-actions' - }, div => { + const titleActionsContainer = append(titleArea, $('.title-actions')); - // Toolbar - this.toolBar = this._register(new ToolBar(div.getHTMLElement(), this.contextMenuService, { - actionItemProvider: action => this.actionItemProvider(action as Action), - orientation: ActionsOrientation.HORIZONTAL, - getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id) - })); - }); + // Toolbar + this.toolBar = this._register(new ToolBar(titleActionsContainer, this.contextMenuService, { + actionItemProvider: action => this.actionItemProvider(action as Action), + orientation: ActionsOrientation.HORIZONTAL, + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id) + })); - return titleArea.getHTMLElement(); + return titleArea; } protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel { - let titleLabel: Builder; - $(parent).div({ - 'class': 'title-label' - }, div => { - titleLabel = div.element('h2'); - }); + const titleContainer = append(parent, $('.title-label')); + const titleLabel = append(titleContainer, $('h2')); const $this = this; return { updateTitle: (id, title, keybinding) => { - titleLabel.safeInnerHtml(title); - titleLabel.title(keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title); + titleLabel.innerHTML = strings.escape(title); + titleLabel.title = keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title; }, + updateStyles: () => { - titleLabel.style('color', $this.getColor($this.titleForegroundColor)); + titleLabel.style.color = $this.getColor($this.titleForegroundColor); } }; } @@ -467,13 +458,13 @@ export abstract class CompositePart<T extends Composite> extends Part { } createContentArea(parent: HTMLElement): HTMLElement { - return $(parent).div({ - 'class': 'content' - }, div => { - this.progressBar = this._register(new ProgressBar(div.getHTMLElement())); - this._register(attachProgressBarStyler(this.progressBar, this.themeService)); - this.progressBar.hide(); - }).getHTMLElement(); + const contentContainer = append(parent, $('.content')); + + this.progressBar = this._register(new ProgressBar(contentContainer)); + this._register(attachProgressBarStyler(this.progressBar, this.themeService)); + this.progressBar.hide(); + + return contentContainer; } private onError(error: any): void { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 83f309f828b..5a11594b7d8 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -6,15 +6,16 @@ 'use strict'; import { BreadcrumbsWidget } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { GroupIdentifier } from 'vs/workbench/common/editor'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Emitter, Event } from 'vs/base/common/event'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import * as glob from 'vs/base/common/glob'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; +import { IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { GroupIdentifier } from 'vs/workbench/common/editor'; export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditorBreadcrumbsService'); @@ -57,8 +58,10 @@ registerSingleton(IBreadcrumbsService, BreadcrumbsService); export abstract class BreadcrumbsConfig<T> { name: string; - value: T; - onDidChange: Event<T>; + onDidChange: Event<void>; + + abstract getValue(overrides?: IConfigurationOverrides): T; + abstract updateValue(value: T, overrides?: IConfigurationOverrides): Thenable<void>; abstract dispose(): void; private constructor() { @@ -69,30 +72,30 @@ export abstract class BreadcrumbsConfig<T> { static UseQuickPick = BreadcrumbsConfig._stub<boolean>('breadcrumbs.useQuickPick'); static FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath'); static SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath'); + static FilterOnType = BreadcrumbsConfig._stub<boolean>('breadcrumbs.filterOnType'); + + static FileExcludes = BreadcrumbsConfig._stub<glob.IExpression>('files.exclude'); private static _stub<T>(name: string): { bindTo(service: IConfigurationService): BreadcrumbsConfig<T> } { return { bindTo(service) { - let value: T = service.getValue(name); - let onDidChange = new Emitter<T>(); + let onDidChange = new Emitter<void>(); let listener = service.onDidChangeConfiguration(e => { if (e.affectsConfiguration(name)) { - value = service.getValue(name); - onDidChange.fire(value); + onDidChange.fire(undefined); } }); - return { - name, - get value() { - return value; - }, - set value(newValue: T) { - service.updateValue(name, newValue); - value = newValue; - }, - onDidChange: onDidChange.event, + return new class implements BreadcrumbsConfig<T>{ + readonly name = name; + readonly onDidChange = onDidChange.event; + getValue(overrides?: IConfigurationOverrides): T { + return service.getValue(name, overrides); + } + updateValue(newValue: T, overrides?: IConfigurationOverrides): Thenable<void> { + return service.updateValue(name, newValue, overrides); + } dispose(): void { listener.dispose(); onDidChange.dispose(); @@ -114,13 +117,13 @@ Registry.as<IConfigurationRegistry>(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'], @@ -131,7 +134,7 @@ Registry.as<IConfigurationRegistry>(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'], @@ -141,6 +144,11 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat localize('symbolpath.last', "Only show the current symbol in the breadcrumbs view."), ] }, + // 'breadcrumbs.filterOnType': { + // description: localize('filterOnType', "Controls whether the breadcrumb picker filters or highlights when typing."), + // type: 'boolean', + // default: false + // }, } }); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 14ab5ecc5b8..892108aad9e 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -9,37 +9,41 @@ 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 { tail } from 'vs/base/common/arrays'; +import { timeout } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { combinedDisposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; +import URI from 'vs/base/common/uri'; import 'vs/css!./media/breadcrumbscontrol'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; +import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; import { symbolKindToCssClass } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { localize } from 'vs/nls'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; 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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IListService, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; 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 { BreadcrumbsPicker, createBreadcrumbsPicker } 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 { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { localize } from 'vs/nls'; -import { WorkbenchListFocusContextKey, IListService } from 'vs/platform/list/browser/listService'; -import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; class Item extends BreadcrumbsItem { @@ -87,6 +91,7 @@ class Item extends BreadcrumbsItem { // 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) { @@ -116,13 +121,15 @@ export interface IBreadcrumbsControlOptions { showSymbolIcons: boolean; showDecorationColors: boolean; extraClasses: string[]; + breadcrumbsBackground: ColorIdentifier; } export class BreadcrumbsControl { - static HEIGHT = 25; + static HEIGHT = 22; static readonly Payload_Reveal = {}; + static readonly Payload_RevealAside = {}; static readonly Payload_Pick = {}; static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); @@ -164,7 +171,7 @@ export class BreadcrumbsControl { this._widget.onDidSelectItem(this._onSelectEvent, this, this._disposables); this._widget.onDidFocusItem(this._onFocusEvent, this, this._disposables); this._widget.onDidChangeFocus(this._updateCkBreadcrumbsActive, this, this._disposables); - this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService)); + this._disposables.push(attachBreadcrumbsStyler(this._widget, this._themeService, { breadcrumbsBackground: _options.breadcrumbsBackground })); this._ckBreadcrumbsVisible = BreadcrumbsControl.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService); @@ -233,7 +240,7 @@ export class BreadcrumbsControl { this._breadcrumbsDisposables.push({ dispose: () => { if (this._breadcrumbsPickerShowing) { - this._contextViewService.hideContextView(); + this._contextViewService.hideContextView(this); } } }); @@ -255,15 +262,16 @@ 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(event, element); + this._revealInEditor(event, element, group); return; } - if (this._cfUseQuickPick.value) { + if (this._cfUseQuickPick.getValue()) { // using quick pick this._widget.setFocused(undefined); this._widget.setSelection(undefined); @@ -273,35 +281,63 @@ export class BreadcrumbsControl { // show picker let picker: BreadcrumbsPicker; + let editor = this._editorGroup.activeControl.getControl() as ICodeEditor; + let editorDecorations: string[] = []; + let editorViewState: ICodeEditorViewState; + this._contextViewService.showContextView({ render: (parent: HTMLElement) => { picker = createBreadcrumbsPicker(this._instantiationService, parent, element); - let listener = picker.onDidPickElement(data => { - this._contextViewService.hideContextView(); - this._widget.setFocused(undefined); - this._widget.setSelection(undefined); - this._revealInEditor(event, data); + let selectListener = picker.onDidPickElement(data => { + if (data.target) { + editorViewState = undefined; + } + this._contextViewService.hideContextView(this); + this._revealInEditor(event, data.target, this._getEditorGroup(data.payload && data.payload.originalEvent)); + }); + let focusListener = picker.onDidFocusElement(data => { + if (!editor || !(data.target instanceof OutlineElement)) { + return; + } + if (!editorViewState) { + editorViewState = editor.saveViewState(); + } + const { symbol } = data.target; + editor.revealRangeInCenter(symbol.range, ScrollType.Smooth); + editorDecorations = editor.deltaDecorations(editorDecorations, [{ + range: symbol.range, + options: { + className: 'rangeHighlight', + isWholeLine: true + } + }]); + }); this._breadcrumbsPickerShowing = true; this._updateCkBreadcrumbsActive(); - return combinedDisposable([listener, picker]); + return combinedDisposable([selectListener, focusListener, picker]); }, - getAnchor() { - + getAnchor: () => { + let maxInnerWidth = window.innerWidth - 8 /*a little less the the full widget*/; let pickerHeight = 330; - let pickerWidth = Math.max(220, dom.getTotalWidth(event.node)); + let pickerWidth = Math.min(maxInnerWidth, Math.max(240, maxInnerWidth / 4.17)); 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 (x + pickerWidth >= maxInnerWidth) { + x = maxInnerWidth - pickerWidth; } if (event.payload instanceof StandardMouseEvent) { - pickerArrowOffset = event.payload.posx - x - pickerArrowSize; + let maxPickerArrowOffset = pickerWidth - 2 * pickerArrowSize; + pickerArrowOffset = event.payload.posx - x; + if (pickerArrowOffset > maxPickerArrowOffset) { + x = Math.min(maxInnerWidth - pickerWidth, x + pickerArrowOffset - maxPickerArrowOffset); + pickerArrowOffset = maxPickerArrowOffset; + } } else { pickerArrowOffset = (data.left + (data.width * .3)) - x; } @@ -309,9 +345,19 @@ export class BreadcrumbsControl { picker.setInput(element); return { x, y }; }, - onHide: () => { + onHide: (data) => { + if (editor) { + editor.deltaDecorations(editorDecorations, []); + if (editorViewState) { + editor.restoreViewState(editorViewState); + } + } this._breadcrumbsPickerShowing = false; this._updateCkBreadcrumbsActive(); + if (data === this) { + this._widget.setFocused(undefined); + this._widget.setSelection(undefined); + } } }); } @@ -321,11 +367,11 @@ export class BreadcrumbsControl { this._ckBreadcrumbsActive.set(value); } - private _revealInEditor(event: IBreadcrumbsItemEvent, data: any): void { - if (data instanceof FileElement) { - if (data.kind === FileKind.FILE) { + 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: data.uri }); + this._editorService.openEditor({ resource: element.uri }, group); } else { // show next picker let items = this._widget.getItems(); @@ -334,27 +380,38 @@ export class BreadcrumbsControl { this._widget.setSelection(items[idx + 1], BreadcrumbsControl.Payload_Pick); } - } else if (data instanceof OutlineElement) { + } else if (element instanceof OutlineElement) { // open symbol in editor - let model = OutlineModel.get(data); + 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), + revealInCenterIfOutsideViewport: true + } + }, 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 +// toggle command MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: 'breadcrumbs.focus', - title: localize('cmd.focus', "Focus Breadcrumbs") + id: 'breadcrumbs.toggle', + title: localize('cmd.toggle', "Toggle Breadcrumbs"), + category: localize('cmd.category', "View") } }); MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { @@ -367,27 +424,63 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { }); CommandsRegistry.registerCommand('breadcrumbs.toggle', accessor => { let config = accessor.get(IConfigurationService); - let value = BreadcrumbsConfig.IsEnabled.bindTo(config).value; - BreadcrumbsConfig.IsEnabled.bindTo(config).value = !value; + let value = BreadcrumbsConfig.IsEnabled.bindTo(config).getValue(); + BreadcrumbsConfig.IsEnabled.bindTo(config).updateValue(!value); }); +// focus/focus-and-select +async function focusAndSelectHandler(accessor: ServicesAccessor, select: boolean): Promise<void> { + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + const config = accessor.get(IConfigurationService); + // check if enabled and iff not enable + const isEnabled = BreadcrumbsConfig.IsEnabled.bindTo(config); + if (!isEnabled.getValue()) { + await isEnabled.updateValue(true); + await timeout(50); // hacky - the widget might not be ready yet... + } + // find widget and focus/select + const widget = breadcrumbs.getWidget(groups.activeGroup.id); + if (widget) { + const item = tail(widget.getItems()); + widget.setFocused(item); + if (select) { + widget.setSelection(item, BreadcrumbsControl.Payload_Pick); + } + } +} +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'breadcrumbs.focusAndSelect', + title: localize('cmd.focus', "Focus Breadcrumbs"), + precondition: BreadcrumbsControl.CK_BreadcrumbsVisible + } +}); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.focusAndSelect', + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT, + when: undefined, + handler: accessor => focusAndSelectHandler(accessor, true) +}); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focus', 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(); - } + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_SEMICOLON, + when: undefined, + handler: accessor => focusAndSelectHandler(accessor, false) }); + +// navigation KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focusNext', 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); @@ -399,7 +492,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focusPrevious', 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); @@ -423,8 +520,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.revealFocused', weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.Shift | KeyCode.Enter, - secondary: [KeyCode.Space], + primary: KeyCode.Space, + secondary: [KeyMod.CtrlCmd | KeyCode.Enter], when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { const groups = accessor.get(IEditorGroupsService); @@ -435,9 +532,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.selectEditor', - weight: KeybindingWeight.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); @@ -448,14 +544,30 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'breadcrumbs.pickFromTree', + id: 'breadcrumbs.revealFocusedFromTreeAside', weight: KeybindingWeight.WorkbenchContrib, - primary: KeyCode.Tab, + primary: KeyMod.CtrlCmd | KeyCode.Enter, when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive, WorkbenchListFocusContextKey), handler(accessor) { - const list = accessor.get(IListService).lastFocusedList; - if (list instanceof Tree) { - list.setSelection([list.getFocus()]); + const editors = accessor.get(IEditorService); + const lists = accessor.get(IListService); + const element = <OutlineElement | IFileStat>lists.lastFocusedList.getFocus(); + if (element instanceof OutlineElement) { + // open symbol in editor + return editors.openEditor({ + resource: OutlineModel.get(element).textModel.uri, + options: { selection: Range.collapseToStart(element.symbol.selectionRange) } + }, SIDE_GROUP); + + } else if (URI.isUri(element.resource)) { + // open file in editor + return editors.openEditor({ + resource: element.resource, + }, SIDE_GROUP); + + } else { + // ignore + return undefined; } } }); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 40d9babb573..ba716837a6f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -12,8 +12,7 @@ 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'; @@ -82,16 +81,16 @@ export class EditorBreadcrumbsModel { let result: BreadcrumbElement[] = []; // file path elements - if (this._cfgFilePath.value === 'on') { + if (this._cfgFilePath.getValue() === 'on') { result = result.concat(this._fileInfo.path); - } else if (this._cfgFilePath.value === 'last' && this._fileInfo.path.length > 0) { + } else if (this._cfgFilePath.getValue() === 'last' && this._fileInfo.path.length > 0) { result = result.concat(this._fileInfo.path.slice(-1)); } // symbol path elements - if (this._cfgSymbolPath.value === 'on') { + if (this._cfgSymbolPath.getValue() === 'on') { result = result.concat(this._outlineElements); - } else if (this._cfgSymbolPath.value === 'last' && this._outlineElements.length > 0) { + } else if (this._cfgSymbolPath.getValue() === 'last' && this._outlineElements.length > 0) { result = result.concat(this._outlineElements.slice(-1)); } @@ -117,7 +116,7 @@ export class EditorBreadcrumbsModel { break; } info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); - uri = uri.with({ path: paths.dirname(uri.path) }); + uri = dirname(uri); } if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -177,7 +176,7 @@ export class EditorBreadcrumbsModel { this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { timeout.cancelAndSet(() => { - if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId()) { + if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && this._editor.getModel()) { this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); } }, 150); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index c29f3e10307..07b284cb200 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -7,26 +7,29 @@ import * as dom from 'vs/base/browser/dom'; import { compareFileNames } from 'vs/base/common/comparers'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; +import { createMatches, FuzzyScore, fuzzyScore } from 'vs/base/common/filters'; +import * as glob from 'vs/base/common/glob'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { dirname, isEqual } from 'vs/base/common/resources'; +import { basename, dirname, isEqual } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IDataSource, IRenderer, ISelectionEvent, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; +import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./media/breadcrumbscontrol'; import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; -import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; -import { HighlightingWorkbenchTree, IHighlightingTreeConfiguration, IHighlightingRenderer } from 'vs/platform/list/browser/listService'; -import { IThemeService, DARK } from 'vs/platform/theme/common/themeService'; +import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { HighlightingWorkbenchTree, IHighlighter, IHighlightingTreeConfiguration, IHighlightingTreeOptions } from 'vs/platform/list/browser/listService'; +import { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; +import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { FileLabel } from 'vs/workbench/browser/labels'; +import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; -import { onUnexpectedError } from 'vs/base/common/errors'; -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 { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { let ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; @@ -42,21 +45,24 @@ export abstract class BreadcrumbsPicker { protected readonly _tree: HighlightingWorkbenchTree; protected readonly _focus: dom.IFocusTracker; - protected readonly _onDidPickElement = new Emitter<any>(); + private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>(); + readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event; - readonly onDidPickElement: Event<any> = this._onDidPickElement.event; + private readonly _onDidFocusElement = new Emitter<{ target: any, payload: any }>(); + readonly onDidFocusElement: Event<{ target: any, payload: any }> = this._onDidFocusElement.event; constructor( parent: HTMLElement, @IInstantiationService protected readonly _instantiationService: IInstantiationService, - @IThemeService protected readonly _themeService: IThemeService, + @IWorkbenchThemeService protected readonly _themeService: IWorkbenchThemeService, + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons'; 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); @@ -71,22 +77,46 @@ export abstract class BreadcrumbsPicker { 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._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`; this._domNode.appendChild(this._treeContainer); - const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined }); + const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService); + this._disposables.push(filterConfig); + + const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined }); this._tree = this._instantiationService.createInstance( HighlightingWorkbenchTree, this._treeContainer, treeConifg, - { useShadows: false }, + <IHighlightingTreeOptions>{ useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 }, { 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._getTargetFromEvent(e.selection[0], e.payload); + if (target) { + 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._disposables.push(this._tree.onDidChangeFocus(e => { + const target = this._getTargetFromEvent(e.focus, e.payload); + if (target) { + this._onDidFocusElement.fire({ target, payload: e.payload }); + } + })); + + // tree icon theme specials + dom.addClass(this._treeContainer, 'file-icon-themable-tree'); + dom.addClass(this._treeContainer, 'show-file-icons'); + const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => { + dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons); + dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true); + }; + this._disposables.push(_themeService.onDidFileIconThemeChange(onFileIconThemeChange)); + onFileIconThemeChange(_themeService.getFileIconTheme()); this._domNode.focus(); } @@ -117,12 +147,20 @@ export abstract class BreadcrumbsPicker { } layout(height: number, width: number, arrowSize: number, arrowOffset: number) { - this._domNode.style.height = `${height}px`; + + 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 = `${height - 2 * arrowSize}px`; + this._treeContainer.style.height = `${treeHeight}px`; this._treeContainer.style.width = `${width}px`; this._tree.layout(); } @@ -130,7 +168,7 @@ export abstract class BreadcrumbsPicker { 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 _getTargetFromEvent(element: any, payload: any): any | undefined; } //#region - Files @@ -189,12 +227,66 @@ export class FileDataSource implements IDataSource { } } -export class FileRenderer implements IRenderer, IHighlightingRenderer { +export class FileFilter implements IFilter { - private readonly _scores = new Map<object, FuzzyScore>(); + private readonly _cachedExpressions = new Map<string, glob.ParsedExpression>(); + private readonly _disposables: IDisposable[] = []; constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, + @IConfigurationService configService: IConfigurationService, + ) { + const config = BreadcrumbsConfig.FileExcludes.bindTo(configService); + const update = () => { + _workspaceService.getWorkspace().folders.forEach(folder => { + const excludesConfig = config.getValue({ resource: folder.uri }); + if (excludesConfig) { + this._cachedExpressions.set(folder.uri.toString(), glob.parse(excludesConfig)); + } + }); + }; + update(); + this._disposables.push( + config, + config.onDidChange(update), + _workspaceService.onDidChangeWorkspaceFolders(update) + ); + } + + dispose(): void { + dispose(this._disposables); + } + + isVisible(tree: ITree, element: IWorkspaceFolder | IFileStat): boolean { + if (IWorkspaceFolder.isIWorkspaceFolder(element)) { + // not a file + return true; + } + const folder = this._workspaceService.getWorkspaceFolder(element.resource); + if (!folder || !this._cachedExpressions.has(folder.uri.toString())) { + // no folder or no filer + return true; + } + + const expression = this._cachedExpressions.get(folder.uri.toString()); + return !expression(element.resource.path.substr(folder.uri.path.length), basename(element.resource)); + } +} + +export class FileHighlighter implements IHighlighter { + getHighlightsStorageKey(element: IFileStat | IWorkspaceFolder): string { + return IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString(); + } + getHighlights(tree: ITree, element: IFileStat | IWorkspaceFolder, pattern: string): FuzzyScore { + return fuzzyScore(pattern, element.name, undefined, true); + } +} + +export class FileRenderer implements IRenderer { + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationService private readonly _configService: IConfigurationService, ) { } getHeight(tree: ITree, element: any): number { @@ -210,42 +302,28 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer { } 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)) { - templateData.setFile(element.uri, { - hidePath: true, - fileKind: FileKind.ROOT_FOLDER, - fileDecorations: { colors: true, badges: true }, - matches: createMatches((this._scores.get(element) || [, []])[1]) - }); + resource = element.uri; + fileKind = FileKind.ROOT_FOLDER; } else { - templateData.setFile(element.resource, { - hidePath: true, - fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE, - fileDecorations: { colors: true, badges: true }, - matches: createMatches((this._scores.get(element) || [, []])[1]) - }); + resource = element.resource; + fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE; } + templateData.setFile(resource, { + fileKind, + hidePath: true, + fileDecorations: fileDecorations, + matches: createMatches((tree as HighlightingWorkbenchTree).getHighlighterScore(element)), + extraClasses: ['picker-item'] + }); } disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void { templateData.dispose(); } - - updateHighlights(tree: ITree, pattern: string): any { - let nav = tree.getNavigator(undefined, false); - let topScore: FuzzyScore; - let topElement: any; - while (nav.next()) { - let element = nav.current() as IFileStat | IWorkspaceFolder; - let score = fuzzyScore(pattern, element.name, undefined, true); - this._scores.set(element, score); - if (!topScore || score && topScore[0] < score[0]) { - topScore = score; - topElement = element; - } - } - return topElement; - } } export class FileSorter implements ISorter { @@ -270,10 +348,11 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { constructor( parent: HTMLElement, @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IConfigurationService configService: IConfigurationService, @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, ) { - super(parent, instantiationService, themeService); + super(parent, instantiationService, themeService, configService); } protected _getInput(input: BreadcrumbElement): any { @@ -300,16 +379,20 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration { // todo@joh reuse explorer implementations? + const filter = this._instantiationService.createInstance(FileFilter); + this._disposables.push(filter); + config.dataSource = this._instantiationService.createInstance(FileDataSource); config.renderer = this._instantiationService.createInstance(FileRenderer); config.sorter = new FileSorter(); + config.highlighter = new FileHighlighter(); + config.filter = filter; return config; } - protected _onDidChangeSelection(e: ISelectionEvent): void { - let [first] = e.selection; - if (first && !IWorkspaceFolder.isIWorkspaceFolder(first) && !(first as IFileStat).isDirectory) { - this._onDidPickElement.fire(new FileElement((first as IFileStat).resource, FileKind.FILE)); + protected _getTargetFromEvent(element: any, _payload: any): any | undefined { + if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) { + return new FileElement((element as IFileStat).resource, FileKind.FILE); } } } @@ -317,11 +400,10 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { //#region - Symbols -class HighlightingOutlineRenderer extends OutlineRenderer implements IHighlightingRenderer { - - updateHighlights(tree: ITree, pattern: string): any { - let model = OutlineModel.get(tree.getInput()); - return model.updateMatches(pattern); +class OutlineHighlighter implements IHighlighter { + getHighlights(tree: ITree, element: OutlineElement, pattern: string): FuzzyScore { + OutlineModel.get(element).updateMatches(pattern); + return element.score; } } @@ -340,18 +422,18 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration { config.dataSource = this._instantiationService.createInstance(OutlineDataSource); - config.renderer = this._instantiationService.createInstance(HighlightingOutlineRenderer); + config.renderer = this._instantiationService.createInstance(OutlineRenderer); config.sorter = new OutlineItemComparator(); + config.highlighter = new OutlineHighlighter(); return config; } - protected _onDidChangeSelection(e: ISelectionEvent): void { - if (e.payload && e.payload.didClickOnTwistie) { + protected _getTargetFromEvent(element: any, payload: any): any | undefined { + if (payload && payload.didClickOnTwistie) { return; } - let [first] = e.selection; - if (first instanceof OutlineElement) { - this._onDidPickElement.fire(first); + if (element instanceof OutlineElement) { + return element; } } } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index f7865c8bb40..c5dd8e397d1 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -671,3 +671,177 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { }, 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 600ece0164f..f58df55c5dc 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -22,7 +22,8 @@ import { distinct } from 'vs/base/common/arrays'; import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService'; 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 { CommandsRegistry, ICommandHandler } 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'; @@ -46,6 +47,8 @@ export const SPLIT_EDITOR_RIGHT = 'workbench.action.splitEditorRight'; export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt '; export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active '; +export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex'; + export interface ActiveEditorMoveArguments { to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next'; by?: 'tab' | 'group'; @@ -224,7 +227,7 @@ function registerDiffEditorCommands(): void { id: 'workbench.action.compareEditor.nextChange', weight: KeybindingWeight.WorkbenchContrib, when: TextCompareEditorVisibleContext, - primary: null, + primary: KeyMod.Alt | KeyCode.F5, handler: accessor => navigateInDiffEditor(accessor, true) }); @@ -232,7 +235,7 @@ function registerDiffEditorCommands(): void { id: 'workbench.action.compareEditor.previousChange', weight: KeybindingWeight.WorkbenchContrib, when: TextCompareEditorVisibleContext, - primary: null, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F5, handler: accessor => navigateInDiffEditor(accessor, false) }); @@ -263,9 +266,33 @@ 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 { + const openEditorAtIndex: ICommandHandler = (accessor: ServicesAccessor, editorIndex: number): void => { + const editorService = accessor.get(IEditorService); + const activeControl = editorService.activeControl; + if (activeControl) { + const editor = activeControl.group.getEditor(editorIndex); + if (editor) { + editorService.openEditor(editor); + } + } + }; + + // This command takes in the editor index number to open as an argument + CommandsRegistry.registerCommand({ + id: OPEN_EDITOR_AT_INDEX_COMMAND_ID, + handler: openEditorAtIndex + }); // Keybindings to focus a specific index in the tab folder if tabs are enabled for (let i = 0; i < 9; i++) { @@ -273,24 +300,12 @@ function registerOpenEditorAtIndexCommands(): void { const visibleIndex = i + 1; KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.openEditorAtIndex' + visibleIndex, + id: OPEN_EDITOR_AT_INDEX_COMMAND_ID + visibleIndex, weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: KeyMod.Alt | toKeyCode(visibleIndex), mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) }, - handler: accessor => { - const editorService = accessor.get(IEditorService); - - const activeControl = editorService.activeControl; - if (activeControl) { - const editor = activeControl.group.getEditor(editorIndex); - if (editor) { - return editorService.openEditor(editor).then(() => void 0); - } - } - - return void 0; - } + handler: accessor => openEditorAtIndex(accessor, editorIndex) }); } diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 2772e4746ca..f1469891010 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -17,6 +17,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/group/common/editorGroupsService'; import { toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { RunOnceScheduler } from 'vs/base/common/async'; interface IDropOperation { splitDirection?: GroupDirection; @@ -32,6 +33,8 @@ class DropOverlay extends Themable { private currentDropOperation: IDropOperation; private _disposed: boolean; + private cleanupOverlayScheduler: RunOnceScheduler; + private readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>(); private readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>(); @@ -43,6 +46,8 @@ class DropOverlay extends Themable { ) { super(themeService); + this.cleanupOverlayScheduler = this._register(new RunOnceScheduler(() => this.dispose(), 300)); + this.create(); } @@ -118,6 +123,11 @@ class DropOverlay extends Themable { // Position overlay this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup); + + // Make sure to stop any running cleanup scheduler to remove the overlay + if (this.cleanupOverlayScheduler.isScheduled()) { + this.cleanupOverlayScheduler.cancel(); + } }, onDragLeave: e => this.dispose(), @@ -144,9 +154,9 @@ class DropOverlay extends Themable { // To protect against this issue we always destroy the overlay as soon as we detect a // mouse event over it. The delay is used to guarantee we are not interfering with the // actual DROP event that can also trigger a mouse over event. - setTimeout(() => { - this.dispose(); - }, 300); + if (!this.cleanupOverlayScheduler.isScheduled()) { + this.cleanupOverlayScheduler.schedule(); + } })); } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index c55b73e43ca..3d7d84cc022 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -723,6 +723,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { openEditor(editor: EditorInput, options?: EditorOptions): TPromise<void> { + // Guard against invalid inputs + if (!editor) { + return TPromise.as(void 0); + } + // Editor opening event allows for prevention const event = new EditorOpeningEvent(this._group.id, editor, options); this._onWillOpenEditor.fire(event); @@ -958,7 +963,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.doCloseInactiveEditor(editor); } - // Forward to title control & breadcrumbs + // Forward to title control this.titleAreaControl.closeEditor(editor); } @@ -1373,6 +1378,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..eac59365565 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<T extends IView> 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; @@ -425,13 +419,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Mark preferred size as changed this.resetPreferredSize(); - // Events for groupd that got added + // Events for groups that got added this.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(groupView => { if (currentGroupViews.indexOf(groupView) === -1) { this._onDidAddGroup.fire(groupView); } }); + // Update labels + this.updateGroupLabels(); + // Restore focus as needed if (gridHasFocus) { this._activeGroup.focus(); @@ -481,6 +478,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Event this._onDidAddGroup.fire(newGroupView); + // Update labels + this.updateGroupLabels(); + return newGroupView; } @@ -637,12 +637,8 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this._activeGroup.focus(); } - // Update labels: since our labels are created using the index of the - // group, removing a group might produce gaps. So we iterate over all - // groups and reassign the label based on the index. - this.getGroups(GroupsOrder.CREATION_TIME).forEach((group, index) => { - group.setLabel(this.getGroupLabel(index + 1)); - }); + // Update labels + this.updateGroupLabels(); // Update container this.updateContainer(); @@ -654,10 +650,6 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this._onDidRemoveGroup.fire(groupView); } - private getGroupLabel(index: number): string { - return localize('groupLabel', "Group {0}", index); - } - moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView { const sourceView = this.assertGroupView(group); const targetView = this.assertGroupView(location); @@ -708,7 +700,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 +820,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,127 +881,25 @@ 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()); } + private updateGroupLabels(): void { + + // Since our labels are created using the index of the + // group, adding/removing a group might produce gaps. + // So we iterate over all groups and reassign the label + // based on the index. + this.getGroups(GroupsOrder.GRID_APPEARANCE).forEach((group, index) => { + group.setLabel(this.getGroupLabel(index + 1)); + }); + } + + private getGroupLabel(index: number): string { + return localize('groupLabel', "Group {0}", index); + } + private isEmpty(): boolean { return this.groupViews.size === 1 && this._activeGroup.isEmpty(); } diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index edbf302a694..58db9249439 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -117,7 +117,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler { // Sorting if (query.value) { - const groups = this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME); + const groups = this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE); entries.sort((e1, e2) => { if (e1.group !== e2.group) { return groups.indexOf(e1.group) - groups.indexOf(e2.group); // older groups first @@ -206,7 +206,7 @@ export class AllEditorsPicker extends BaseEditorPicker { protected getEditorEntries(): EditorPickerEntry[] { const entries: EditorPickerEntry[] = []; - this.editorGroupService.getGroups(GroupsOrder.CREATION_TIME).forEach(group => { + this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(group => { group.editors.forEach(editor => { entries.push(this.instantiationService.createInstance(EditorPickerEntry, editor, group)); }); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 7b24eca3f73..9a47132500a 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -31,7 +31,7 @@ import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpa import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor'; import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -58,6 +58,8 @@ 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, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; +import { getIconClasses } from 'vs/workbench/browser/labels'; class SideBySideEditorEncodingSupport implements IEncodingSupport { constructor(private master: IEncodingSupport, private details: IEncodingSupport) { } @@ -833,7 +835,7 @@ export class ChangeModeAction extends Action { @IModelService private modelService: IModelService, @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<any> { 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(); @@ -865,7 +867,7 @@ export class ChangeModeAction extends Action { // All languages are valid picks const languages = this.modeService.getRegisteredLanguageNames(); - const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => { + const picks: QuickPickInput[] = languages.sort().map((lang, index) => { let description: string; if (currentModeId === lang) { description = nls.localize('languageDescription', "({0}) - Configured Language", this.modeService.getModeIdForLanguageName(lang.toLowerCase())); @@ -885,20 +887,20 @@ export class ChangeModeAction extends Action { } } - return <IFilePickOpenEntry>{ + return <IQuickPickItem>{ label: lang, - resource: fakeResource, + iconClasses: getIconClasses(this.modelService, this.modeService, fakeResource), description }; }); if (hasLanguageSupport) { - picks[0].separator = { border: true, label: nls.localize('languagesPicks', "languages (identifier)") }; + picks.unshift({ type: 'separator', label: nls.localize('languagesPicks', "languages (identifier)") }); } // Offer action to configure via settings - let configureModeAssociations: IPickOpenEntry; - let configureModeSettings: IPickOpenEntry; + let configureModeAssociations: IQuickPickItem; + let configureModeSettings: IQuickPickItem; let galleryAction: Action; if (hasLanguageSupport) { const ext = paths.extname(resource.fsPath) || paths.basename(resource.fsPath); @@ -915,7 +917,7 @@ export class ChangeModeAction extends Action { } // Offer to "Auto Detect" - const autoDetectMode: IPickOpenEntry = { + const autoDetectMode: IQuickPickItem = { label: nls.localize('autoDetect', "Auto Detect") }; @@ -923,7 +925,7 @@ export class ChangeModeAction extends Action { picks.unshift(autoDetectMode); } - return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }).then(pick => { + return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }).then(pick => { if (!pick) { return; } @@ -987,18 +989,18 @@ 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 <IPickOpenEntry>{ + return <IQuickPickItem>{ id, label: lang, description: (id === currentAssociation) ? nls.localize('currentAssociation', "Current Association") : void 0 }; }); - 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 => { + setTimeout(() => { + 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); @@ -1025,12 +1027,12 @@ export class ChangeModeAction extends Action { this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target); } - }); - }); + }, error => errors.onUnexpectedError(error)); + }, 50 /* quick open is sensitive to being opened so soon after another */); } } -export interface IChangeEOLEntry extends IPickOpenEntry { +export interface IChangeEOLEntry extends IQuickPickItem { eol: EndOfLineSequence; } @@ -1043,7 +1045,7 @@ class ChangeIndentationAction extends Action { actionId: string, actionLabel: string, @IEditorService private editorService: IEditorService, - @IQuickOpenService private quickOpenService: IQuickOpenService + @IQuickInputService private quickInputService: IQuickInputService ) { super(actionId, actionLabel); } @@ -1051,14 +1053,14 @@ class ChangeIndentationAction extends Action { run(): TPromise<any> { 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 = [ + const picks: QuickPickInput<IQuickPickItem & { run(): void }>[] = [ activeTextEditorWidget.getAction(IndentUsingSpaces.ID), activeTextEditorWidget.getAction(IndentUsingTabs.ID), activeTextEditorWidget.getAction(DetectIndentation.ID), @@ -1077,10 +1079,10 @@ class ChangeIndentationAction extends Action { }; }); - (<IPickOpenEntry>picks[0]).separator = { label: nls.localize('indentView', "change view") }; - (<IPickOpenEntry>picks[3]).separator = { label: nls.localize('indentConvert', "convert file"), border: true }; + picks.splice(3, 0, { type: 'separator', label: nls.localize('indentConvert', "convert file") }); + picks.unshift({ type: 'separator', label: nls.localize('indentView', "change view") }); - return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }).then(action => action && action.run()); + return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }).then(action => action && action.run()); } } @@ -1093,7 +1095,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 +1103,11 @@ export class ChangeEOLAction extends Action { run(): TPromise<any> { 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 +1119,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)) { @@ -1138,7 +1140,7 @@ export class ChangeEncodingAction extends Action { actionId: string, actionLabel: string, @IEditorService private editorService: IEditorService, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService, @IFileService private fileService: IFileService ) { @@ -1147,19 +1149,19 @@ export class ChangeEncodingAction extends Action { run(): TPromise<any> { 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<IPickOpenEntry>; + let pickActionPromise: TPromise<IQuickPickItem>; - 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 +1175,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 => { @@ -1200,7 +1202,7 @@ export class ChangeEncodingAction extends Action { let aliasMatchIndex: number; // All encodings are valid picks - const picks: IPickOpenEntry[] = Object.keys(SUPPORTED_ENCODINGS) + const picks: QuickPickInput[] = Object.keys(SUPPORTED_ENCODINGS) .sort((k1, k2) => { if (k1 === configuredEncoding) { return -1; @@ -1229,13 +1231,14 @@ export class ChangeEncodingAction extends Action { // If we have a guessed encoding, show it first unless it matches the configured encoding if (guessedEncoding && configuredEncoding !== guessedEncoding && SUPPORTED_ENCODINGS[guessedEncoding]) { - picks[0].separator = { border: true }; + picks.unshift({ type: 'separator' }); picks.unshift({ id: guessedEncoding, label: SUPPORTED_ENCODINGS[guessedEncoding].labelLong, description: nls.localize('guessedEncoding', "Guessed from content") }); } - return this.quickOpenService.pick(picks, { + const items = picks.filter(p => p.type !== 'separator') as IQuickPickItem[]; + return this.quickInputService.pick(picks, { placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"), - autoFocus: { autoFocusIndex: typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : void 0 } + activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1] }).then(encoding => { if (encoding) { activeControl = this.editorService.activeControl; diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css index 7b80fb89d06..cf1a913a8ef 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css @@ -7,12 +7,22 @@ display: none; } -.monaco-workbench>.part.editor>.content .editor-group-container:not(.active) .breadcrumbs-control { - 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/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 5c323a06753..efd03a02391 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -79,6 +79,7 @@ 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 74218a0d7a9..5486621c694 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -259,7 +259,7 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control { flex: 1 100%; - height: 25px; + height: 22px; cursor: default; } @@ -268,14 +268,13 @@ } .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { - max-width: 260px; + max-width: 80%; +} + +.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { + min-width: 16px; } .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 e4cd8fb91df..1cf163403ee 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -15,6 +15,7 @@ import { addDisposableListener, EventType, addClass, EventHelper, removeClass, t import { IEditorPartOptions, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor'; import { IAction } from 'vs/base/common/actions'; import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; +import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; export class NoTabsTitleControl extends TitleControl { private titleContainer: HTMLElement; @@ -40,7 +41,7 @@ export class NoTabsTitleControl extends TitleControl { this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e))); // Breadcrumbs - this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, extraClasses: ['no-tabs-breadcrumbs'] }); + this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, extraClasses: ['no-tabs-breadcrumbs'], breadcrumbsBackground: editorBackground }); // Right Actions Container const actionsContainer = document.createElement('div'); @@ -87,6 +88,8 @@ export class NoTabsTitleControl extends TitleControl { // Close editor on middle mouse click if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) { + EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */); + this.group.closeEditor(this.group.activeEditor); } } @@ -147,6 +150,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/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index 61c35f3f786..f61b6d341f5 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -107,7 +107,8 @@ export class ResourceViewer { private static isImageResource(descriptor: IResourceDescriptor) { const mime = getMime(descriptor); - return mime.indexOf('image/') >= 0; + // Chrome does not support tiffs + return mime.indexOf('image/') >= 0 && mime !== 'image/tiff'; } } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 605ac959776..e764810c07e 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -28,7 +28,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { getOrSet } from 'vs/base/common/map'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, WORKBENCH_BACKGROUND, TAB_ACTIVE_BORDER_TOP, TAB_UNFOCUSED_ACTIVE_BORDER_TOP } from 'vs/workbench/common/theme'; -import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, contrastBorder, editorBackground, breadcrumbsBackground } from 'vs/platform/theme/common/colorRegistry'; import { ResourcesDropHandler, fillResourceDataTransfers, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, DragAndDropObserver } from 'vs/workbench/browser/dnd'; import { Color } from 'vs/base/common/color'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -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, extraClasses: [] }); + this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, extraClasses: [], breadcrumbsBackground: breadcrumbsBackground }); } 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 @@ -485,6 +490,8 @@ export class TabsTitleControl extends TitleControl { tab.blur(); if (e.button === 1 /* Middle Button*/ && !this.originatesFromTabActionBar(e)) { + e.stopPropagation(); // for https://github.com/Microsoft/vscode/issues/56715 + this.blockRevealActiveTabOnce(); this.closeOneEditorAction.run({ groupId: this.group.id, editorIndex: index }); } diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index f59bae6f10b..de98bed575e 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -232,6 +232,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { const options: IDiffEditorOptions = super.getConfigurationOverrides(); options.readOnly = this.isReadOnly(); + options.lineDecorationsWidth = '2ch'; return options; } diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 3a123c9c218..33838407bc5 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -97,22 +97,24 @@ export abstract class TitleControl extends Themable { protected createBreadcrumbsControl(container: HTMLElement, options: IBreadcrumbsControlOptions): void { const config = this._register(BreadcrumbsConfig.IsEnabled.bindTo(this.configurationService)); - config.onDidChange(value => { + this._register(config.onDidChange(value => { 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) { + })); + if (config.getValue()) { this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group); } } + 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 9b4ab103998..00000000000 --- a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-workbench > .part.menubar { - display: flex; - position: absolute; - 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; - z-index: 2000; -} - -.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; -} - -.menubar-menu-items-holder.monaco-menu-container :focus { - outline: 0; -} \ 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 83accf773bb..00000000000 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ /dev/null @@ -1,351 +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'; - -goMenuRegistration(); - -if (isMacintosh) { - windowMenuRegistration(); -} - -helpMenuRegistration(); - -// Menu registration - -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 windowMenuRegistration() { - -} - -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 - }); -} 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/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index fd3ecd2e007..c1aae8802de 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -106,9 +106,9 @@ export class NotificationsCenter extends Themable { private updateTitle(): void { if (this.model.notifications.length === 0) { - this.notificationsCenterTitle.innerText = localize('notificationsEmpty', "No new notifications"); + this.notificationsCenterTitle.textContent = localize('notificationsEmpty', "No new notifications"); } else { - this.notificationsCenterTitle.innerText = localize('notifications', "Notifications"); + this.notificationsCenterTitle.textContent = localize('notifications', "Notifications"); } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 7e88bde2337..84a1f8fcfa2 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -426,10 +426,10 @@ export class NotificationTemplateRenderer { private renderSource(notification): void { if (notification.expanded && notification.source) { - this.template.source.innerText = localize('notificationSource', "Source: {0}", notification.source); + this.template.source.textContent = localize('notificationSource', "Source: {0}", notification.source); this.template.source.title = notification.source; } else { - this.template.source.innerText = ''; + this.template.source.textContent = ''; this.template.source.removeAttribute('title'); } } diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index d3933cb7b9c..54d4f61d040 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -14,7 +14,7 @@ import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/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 { diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 40c3fa928ea..1880db49212 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<Panel> 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<Panel> { @@ -282,7 +281,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService { } private removeComposite(compositeId: string): void { - this.compositeBar.removeComposite(compositeId); + this.compositeBar.hideComposite(compositeId); const compositeActions = this.compositeActions[compositeId]; if (compositeActions) { compositeActions.activityAction.dispose(); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.css b/src/vs/workbench/browser/parts/quickinput/quickInput.css index fbcdcbe684c..690286ea033 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; @@ -108,12 +113,22 @@ } .quick-input-list .quick-input-list-entry { + box-sizing: border-box; overflow: hidden; display: flex; height: 100%; padding: 0 6px; } +.quick-input-list .quick-input-list-entry.quick-input-list-separator-border { + border-top-width: 1px; + border-top-style: solid; +} + +.quick-input-list .monaco-list-row:first-child .quick-input-list-entry.quick-input-list-separator-border { + border-top-style: none; +} + .quick-input-list .quick-input-list-label { overflow: hidden; display: flex; @@ -163,4 +178,44 @@ .quick-input-list .monaco-highlighted-label .highlight { font-weight: bold; -} \ No newline at end of file +} + +.quick-input-list .quick-input-list-separator { + margin-right: 18px; +} + +.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-separator, +.quick-input-list .monaco-list-row.focused .quick-input-list-entry.has-actions .quick-input-list-separator { + margin-right: 0; +} + +.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 { + margin-top: 1px; +} + +.quick-input-list .quick-input-list-entry-action-bar ul:first-child .action-label.icon { + margin-left: 2px; +} + +.quick-input-list .quick-input-list-entry-action-bar ul:last-child .action-label.icon { + margin-right: 8px; +} + +.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 b0b274f92e1..d834bd59460 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, QuickPickInput, IQuickPickSeparator, IKeyMods } 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'; @@ -39,13 +39,15 @@ import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickop 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.$; +type Writeable<T> = { -readonly [P in keyof T]: T[P] }; + const backButton = { iconPath: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/dark/arrow-left.svg')), @@ -62,6 +64,7 @@ interface QuickInputUI { rightActionBar: ActionBar; checkAll: HTMLInputElement; inputBox: QuickInputBox; + visibleCount: CountBadge; count: CountBadge; message: HTMLElement; progressBar: ProgressBar; @@ -69,9 +72,11 @@ interface QuickInputUI { onDidAccept: Event<void>; onDidTriggerButton: Event<IQuickInputButton>; ignoreFocusOut: boolean; + keyMods: Writeable<IKeyMods>; show(controller: QuickInput): void; setVisibilities(visibilities: Visibilities): void; setEnabled(enabled: boolean): void; + setContextKey(contextKey?: string): void; hide(): void; } @@ -79,6 +84,7 @@ type Visibilities = { title?: boolean; checkAll?: boolean; inputBox?: boolean; + visibleCount?: boolean; count?: boolean; message?: boolean; list?: boolean; @@ -92,6 +98,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 +153,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 +196,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 +249,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() { @@ -284,7 +301,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi private _placeholder; private onDidChangeValueEmitter = new Emitter<string>(); private onDidAcceptEmitter = new Emitter<string>(); - private _items: T[] = []; + private _items: (T | IQuickPickSeparator)[] = []; private itemsUpdated = false; private _canSelectMany = false; private _matchOnDescription = false; @@ -297,7 +314,9 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi private selectedItemsUpdated = false; private selectedItemsToConfirm: T[] = []; private onDidChangeSelectionEmitter = new Emitter<T[]>(); - private quickNavigate = false; + private onDidTriggerItemButtonEmitter = new Emitter<IQuickPickItemButtonEvent<T>>(); + + quickNavigate: IQuickNavigateConfiguration; constructor(ui: QuickInputUI) { super(ui); @@ -306,6 +325,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi this.onDidAcceptEmitter, this.onDidChangeActiveEmitter, this.onDidChangeSelectionEmitter, + this.onDidTriggerItemButtonEmitter, ); } @@ -335,7 +355,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi return this._items; } - set items(items: T[]) { + set items(items: (T | IQuickPickSeparator)[]) { this._items = items; this.itemsUpdated = true; this.update(); @@ -390,8 +410,14 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi this.update(); } + get keyMods() { + return this.ui.keyMods; + } + onDidChangeSelection = this.onDidChangeSelectionEmitter.event; + onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; + show() { if (!this.visible) { this.visibleDisposables.push( @@ -415,7 +441,31 @@ class QuickPick<T extends IQuickPickItem> 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 +510,24 @@ class QuickPick<T extends IQuickPickItem> 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<T>)), + 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 +562,61 @@ class QuickPick<T extends IQuickPickItem> 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 +757,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 +765,7 @@ export class QuickInputService extends Component implements IQuickInputService { private enabled = true; private inQuickOpenWidgets: Record<string, boolean> = {}; private inQuickOpenContext: IContextKey<boolean>; + private contexts: { [id: string]: IContextKey<boolean>; } = Object.create(null); private onDidAcceptEmitter = this._register(new Emitter<void>()); private onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>()); @@ -725,7 +779,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 +805,43 @@ export class QuickInputService extends Component implements IQuickInputService { } } + private setContextKey(id?: string) { + let key: IContextKey<boolean>; + if (id) { + key = this.contexts[id]; + if (!key) { + key = new RawContextKey<boolean>(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 +873,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 +900,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 +910,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) { - (<HTMLElement>e.target).focus(); - return; - } - for (let element = <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); } @@ -870,6 +957,30 @@ export class QuickInputService extends Component implements IQuickInputService { break; } })); + this._register(dom.addDisposableListener(container, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + switch (event.keyCode) { + case KeyCode.Ctrl: + case KeyCode.Meta: + this.ui.keyMods.ctrlCmd = true; + break; + case KeyCode.Alt: + this.ui.keyMods.alt = true; + break; + } + })); + this._register(dom.addDisposableListener(container, dom.EventType.KEY_UP, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + switch (event.keyCode) { + case KeyCode.Ctrl: + case KeyCode.Meta: + this.ui.keyMods.ctrlCmd = false; + break; + case KeyCode.Alt: + this.ui.keyMods.alt = false; + break; + } + })); this._register(this.quickOpenService.onShow(() => this.hide(true))); @@ -880,6 +991,7 @@ export class QuickInputService extends Component implements IQuickInputService { rightActionBar, checkAll, inputBox, + visibleCount, count, message, progressBar, @@ -887,21 +999,31 @@ export class QuickInputService extends Component implements IQuickInputService { onDidAccept: this.onDidAcceptEmitter.event, onDidTriggerButton: this.onDidTriggerButtonEmitter.event, ignoreFocusOut: false, + keyMods: { ctrlCmd: false, alt: false }, show: controller => this.show(controller), hide: () => this.hide(), setVisibilities: visibilities => this.setVisibilities(visibilities), setEnabled: enabled => this.setEnabled(enabled), + setContextKey: contextKey => this.setContextKey(contextKey), }; this.updateStyles(); } - pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: TPromise<T[]>, options: O = <O>{}, token: CancellationToken = CancellationToken.None): TPromise<O extends { canPickMany: true } ? T[] : T> { - return new TPromise<O extends { canPickMany: true } ? T[] : T>((resolve, reject) => { + pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: TPromise<QuickPickInput<T>[]> | QuickPickInput<T>[], options: O = <O>{}, token: CancellationToken = CancellationToken.None): TPromise<O extends { canPickMany: true } ? T[] : T> { + return new TPromise<O extends { canPickMany: true } ? T[] : T>((doResolve, reject) => { + let resolve = (result: any) => { + resolve = doResolve; + if (options.onKeyMods) { + options.onKeyMods(input.keyMods); + } + doResolve(result); + }; if (token.isCancellationRequested) { resolve(undefined); return; } const input = this.createQuickPick<T>(); + let activeItem: T; const disposables = [ input, input.onDidAccept(() => { @@ -931,6 +1053,22 @@ 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; + } + } + })), + input.onDidChangeValue(value => { + if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) { + input.activeItems = [activeItem]; + } + }), token.onCancellationRequested(() => { input.hide(); }), @@ -944,16 +1082,23 @@ 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]) => { + activeItem = _activeItem; + input.busy = false; + input.items = items; + if (input.canSelectMany) { + input.selectedItems = items.filter(item => item.type !== 'separator' && item.picked) as T[]; + } + if (activeItem) { + input.activeItems = [activeItem]; + } + }); input.show(); - picks.then(null, err => { + TPromise.wrap(picks).then(null, err => { reject(err); input.hide(); }); @@ -1047,6 +1192,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(); @@ -1054,11 +1200,14 @@ export class QuickInputService extends Component implements IQuickInputService { this.ui.list.matchOnDescription = false; this.ui.list.matchOnDetail = false; this.ui.ignoreFocusOut = false; + this.ui.keyMods.ctrlCmd = false; + this.ui.keyMods.alt = false; const keybinding = this.keybindingService.lookupKeybinding(BackAction.ID); 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(); @@ -1069,6 +1218,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'; @@ -1098,6 +1248,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(); @@ -1122,7 +1273,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; } } } @@ -1185,25 +1336,6 @@ 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: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 6655db6f7bf..0b2ecb2d265 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, IQuickPickSeparator } 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'; @@ -24,8 +24,11 @@ import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlighte import { memoize } from 'vs/base/common/decorators'; import { range } from 'vs/base/common/arrays'; import * as platform from 'vs/base/common/platform'; -import { listFocusBackground } from 'vs/platform/theme/common/colorRegistry'; +import { listFocusBackground, pickerGroupBorder, pickerGroupForeground } 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,8 @@ interface IListElement { index: number; item: IQuickPickItem; checked: boolean; + separator: IQuickPickSeparator; + fireButtonTriggered: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void; } class ListElement implements IListElement { @@ -52,9 +57,11 @@ class ListElement implements IListElement { this._onChecked.fire(value); } } + separator: IQuickPickSeparator; labelHighlights?: IMatch[]; descriptionHighlights?: IMatch[]; detailHighlights?: IMatch[]; + fireButtonTriggered: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void; constructor(init: IListElement) { assign(this, init); @@ -62,9 +69,12 @@ class ListElement implements IListElement { } interface IListElementTemplateData { + entry: HTMLDivElement; checkbox: HTMLInputElement; label: IconLabel; detail: HighlightedLabel; + separator: HTMLDivElement; + actionBar: ActionBar; element: ListElement; toDisposeElement: IDisposable[]; toDisposeTemplate: IDisposable[]; @@ -83,10 +93,10 @@ class ListElementRenderer implements IRenderer<ListElement, IListElementTemplate data.toDisposeElement = []; data.toDisposeTemplate = []; - const entry = dom.append(container, $('.quick-input-list-entry')); + data.entry = dom.append(container, $('.quick-input-list-entry')); // Checkbox - const label = dom.append(entry, $('label.quick-input-list-label')); + const label = dom.append(data.entry, $('label.quick-input-list-label')); data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-list-checkbox')); data.checkbox.type = 'checkbox'; data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => { @@ -105,6 +115,14 @@ class ListElementRenderer implements IRenderer<ListElement, IListElementTemplate const detailContainer = dom.append(row2, $('.quick-input-list-label-meta')); data.detail = new HighlightedLabel(detailContainer); + // Separator + data.separator = dom.append(data.entry, $('.quick-input-list-separator')); + + // Actions + data.actionBar = new ActionBar(data.entry); + data.actionBar.domNode.classList.add('quick-input-list-entry-action-bar'); + data.toDisposeTemplate.push(data.actionBar); + return data; } @@ -121,14 +139,48 @@ class ListElementRenderer implements IRenderer<ListElement, IListElementTemplate options.matches = labelHighlights || []; options.descriptionTitle = element.item.description; options.descriptionMatches = descriptionHighlights || []; + options.extraClasses = element.item.iconClasses; data.label.setValue(element.item.label, element.item.description, options); // Meta data.detail.set(element.item.detail, detailHighlights); + + // Separator + if (element.separator && element.separator.label) { + data.separator.textContent = element.separator.label; + data.separator.style.display = null; + } else { + data.separator.style.display = 'none'; + } + if (element.separator) { + dom.addClass(data.entry, 'quick-input-list-separator-border'); + } else { + dom.removeClass(data.entry, 'quick-input-list-separator-border'); + } + + // Actions + data.actionBar.clear(); + const buttons = element.item.buttons; + if (buttons && buttons.length) { + data.actionBar.push(buttons.map((button, index) => { + 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 }); + dom.addClass(data.entry, 'has-actions'); + } else { + dom.removeClass(data.entry, 'has-actions'); + } } - disposeElement(): void { - // noop + disposeElement(element: ListElement, index: number, data: IListElementTemplateData): void { + data.toDisposeElement = dispose(data.toDisposeElement); } disposeTemplate(data: IListElementTemplateData): void { @@ -152,6 +204,7 @@ export class QuickInputList { private container: HTMLElement; private list: WorkbenchList<ListElement>; + private inputElements: (IQuickPickItem | IQuickPickSeparator)[]; private elements: ListElement[] = []; private elementsToIndexes = new Map<IQuickPickItem, number>(); matchOnDescription = false; @@ -160,8 +213,12 @@ export class QuickInputList { onChangedAllVisibleChecked: Event<boolean> = this._onChangedAllVisibleChecked.event; private _onChangedCheckedCount = new Emitter<number>(); onChangedCheckedCount: Event<number> = this._onChangedCheckedCount.event; + private _onChangedVisibleCount = new Emitter<number>(); + onChangedVisibleCount: Event<number> = this._onChangedVisibleCount.event; private _onChangedCheckedElements = new Emitter<IQuickPickItem[]>(); onChangedCheckedElements: Event<IQuickPickItem[]> = this._onChangedCheckedElements.event; + private _onButtonTriggered = new Emitter<IQuickPickItemButtonEvent<IQuickPickItem>>(); + onButtonTriggered = this._onButtonTriggered.event; private _onLeave = new Emitter<void>(); onLeave: Event<void> = this._onLeave.event; private _fireCheckedEvents = true; @@ -191,12 +248,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 +314,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; @@ -269,13 +339,23 @@ export class QuickInputList { } } - setElements(elements: IQuickPickItem[]): void { + setElements(inputElements: (IQuickPickItem | IQuickPickSeparator)[]): void { this.elementDisposables = dispose(this.elementDisposables); - this.elements = elements.map((item, index) => new ListElement({ - index, - item, - checked: false - })); + const fireButtonTriggered = (event: IQuickPickItemButtonEvent<IQuickPickItem>) => this.fireButtonTriggered(event); + this.inputElements = inputElements; + this.elements = inputElements.reduce((result, item, index) => { + if (item.type !== 'separator') { + const previous = index && inputElements[index - 1]; + result.push(new ListElement({ + index, + item, + checked: false, + separator: previous && previous.type === 'separator' ? previous : undefined, + fireButtonTriggered + })); + } + return result; + }, [] as ListElement[]); this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents()))); this.elementsToIndexes = this.elements.reduce((map, element, index) => { @@ -284,6 +364,7 @@ export class QuickInputList { }, new Map<IQuickPickItem, number>()); this.list.splice(0, this.list.length, this.elements); this.list.setFocus([]); + this._onChangedVisibleCount.fire(this.elements.length); } getFocusedElements() { @@ -338,10 +419,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'; } @@ -371,6 +452,8 @@ export class QuickInputList { element.descriptionHighlights = undefined; element.detailHighlights = undefined; element.hidden = false; + const previous = element.index && this.inputElements[element.index - 1]; + element.separator = previous && previous.type === 'separator' ? previous : undefined; }); } @@ -390,21 +473,21 @@ export class QuickInputList { element.labelHighlights = undefined; element.descriptionHighlights = undefined; element.detailHighlights = undefined; - element.hidden = true; + element.hidden = !element.item.alwaysShow; } + element.separator = undefined; }); } const shownElements = this.elements.filter(element => !element.hidden); // Sort by value - const normalizedSearchValue = query.toLowerCase(); - shownElements.sort((a, b) => { - if (!query) { - return a.index - b.index; // restore natural order - } - return compareEntries(a, b, normalizedSearchValue); - }); + if (query) { + const normalizedSearchValue = query.toLowerCase(); + shownElements.sort((a, b) => { + return compareEntries(a, b, normalizedSearchValue); + }); + } this.elementsToIndexes = shownElements.reduce((map, element, index) => { map.set(element.item, index); @@ -415,6 +498,7 @@ export class QuickInputList { this.list.layout(); this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked()); + this._onChangedVisibleCount.fire(shownElements.length); } toggleCheckbox() { @@ -451,6 +535,10 @@ export class QuickInputList { this._onChangedCheckedElements.fire(this.getCheckedElements()); } } + + private fireButtonTriggered(event: IQuickPickItemButtonEvent<IQuickPickItem>) { + this._onButtonTriggered.fire(event); + } } function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number { @@ -475,4 +563,12 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { background-color: ${listInactiveFocusBackground}; }`); collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused:hover { background-color: ${listInactiveFocusBackground}; }`); } + const pickerGroupBorderColor = theme.getColor(pickerGroupBorder); + if (pickerGroupBorderColor) { + collector.addRule(`.quick-input-list .quick-input-list-entry { border-top-color: ${pickerGroupBorderColor}; }`); + } + const pickerGroupForegroundColor = theme.getColor(pickerGroupForeground); + if (pickerGroupForegroundColor) { + collector.addRule(`.quick-input-list .quick-input-list-separator { color: ${pickerGroupForegroundColor}; }`); + } }); 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..279febbdff1 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -14,14 +14,12 @@ import URI from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import * as types from 'vs/base/common/types'; -import { Action, IAction } from 'vs/base/common/actions'; +import { Action } from 'vs/base/common/actions'; import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; -import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, compareEntries, QuickOpenItemAccessorClass } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, 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'; @@ -34,48 +32,29 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { QuickOpenHandler, QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, EditorQuickOpenEntry, CLOSE_ON_FOCUS_LOST_CONFIG } from 'vs/workbench/browser/quickopen'; import * as errors from 'vs/base/common/errors'; -import { IPickOpenEntry, IFilePickOpenEntry, IQuickOpenService, IShowOptions, IPickOpenItem, IStringPickOptions, ITypedPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; +import { IQuickOpenService, IShowOptions } 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'; import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme'; import { attachQuickOpenStyler } from 'vs/platform/theme/common/styler'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; -import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { FileKind, IFileService } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { scoreItem, ScorerCache, compareItemsByScore, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; -import { matchesFuzzyOcticonAware, parseOcticons, IParsedOcticons } from 'vs/base/common/octicon'; -import { IMatch } from 'vs/base/common/filters'; import { Schemas } from 'vs/base/common/network'; -import Severity from 'vs/base/common/severity'; import { INotificationService } from 'vs/platform/notification/common/notification'; 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 { ILabelService } from 'vs/platform/label/common/label'; +import { timeout } from 'vs/base/common/async'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; const HELP_PREFIX = '?'; -interface IInternalPickOptions { - contextKey?: string; - value?: string; - valueSelection?: [number, number]; - placeHolder?: string; - inputDecoration?: Severity; - password?: boolean; - autoFocus?: IAutoFocus; - matchOnDescription?: boolean; - matchOnDetail?: boolean; - ignoreFocusLost?: boolean; - quickNavigateConfiguration?: IQuickNavigateConfiguration; - onDidType?: (value: string) => any; - onDidFocus?: (item: any) => void; -} - export class QuickOpenController extends Component implements IQuickOpenService { private static readonly MAX_SHORT_RESPONSE_TIME = 500; @@ -90,13 +69,11 @@ export class QuickOpenController extends Component implements IQuickOpenService get onHide(): Event<void> { return this._onHide.event; } private quickOpenWidget: QuickOpenWidget; - private pickOpenWidget: QuickOpenWidget; private layoutDimensions: Dimension; private mapResolvedHandlersToPrefix: { [prefix: string]: TPromise<QuickOpenHandler>; } = Object.create(null); private mapContextKeyToContext: { [id: string]: IContextKey<boolean>; } = Object.create(null); private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null); private currentResultToken: string; - private currentPickerToken: string; private promisesToCompleteOnHide: ValueCallback[] = []; private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor; private actionProvider = new ContributableActionProvider(); @@ -123,7 +100,7 @@ export class QuickOpenController extends Component implements IQuickOpenService } private registerListeners(): void { - this._register(this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration())); + this._register(this.configurationService.onDidChangeConfiguration(() => this.updateConfiguration())); this._register(this.partService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget())); this._register(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget())); } @@ -140,295 +117,24 @@ export class QuickOpenController extends Component implements IQuickOpenService if (this.quickOpenWidget) { this.quickOpenWidget.navigate(next, quickNavigate); } - - if (this.pickOpenWidget) { - this.pickOpenWidget.navigate(next, quickNavigate); - } - } - - pick(picks: TPromise<string[]>, options?: IStringPickOptions, token?: CancellationToken): TPromise<string>; - pick<T extends IPickOpenEntry>(picks: TPromise<T[]>, options?: ITypedPickOptions<T>, token?: CancellationToken): TPromise<string>; - pick(picks: string[], options?: IStringPickOptions, token?: CancellationToken): TPromise<string>; - pick<T extends IPickOpenEntry>(picks: T[], options?: ITypedPickOptions<T>, token?: CancellationToken): TPromise<T>; - pick(arg1: string[] | TPromise<string[]> | IPickOpenEntry[] | TPromise<IPickOpenEntry[]>, options?: IStringPickOptions | ITypedPickOptions<IPickOpenEntry>, token?: CancellationToken): TPromise<string | IPickOpenEntry> { - if (!options) { - options = Object.create(null); - } - - let arrayPromise: TPromise<string[] | IPickOpenEntry[]>; - if (Array.isArray(arg1)) { - arrayPromise = TPromise.as(arg1); - } else if (TPromise.is(arg1)) { - arrayPromise = arg1; - } else { - throw new Error('illegal input'); - } - - let isAboutStrings = false; - const entryPromise = arrayPromise.then(elements => { - return (<Array<string | IPickOpenEntry>>elements).map(element => { - if (typeof element === 'string') { - isAboutStrings = true; - - return <IPickOpenEntry>{ label: element }; - } else { - return element; - } - }); - }); - - if (this.pickOpenWidget && this.pickOpenWidget.isVisible()) { - this.pickOpenWidget.hide(HideReason.CANCELED); - } - - return new TPromise<string | IPickOpenEntry>((resolve, reject) => { - - function onItem(item: IPickOpenEntry): string | IPickOpenEntry { - return item && isAboutStrings ? item.label : item; - } - - this.doPick(entryPromise, options, token).then(item => resolve(onItem(item)), err => reject(err)); - }); - } - - private doPick(picksPromise: TPromise<IPickOpenEntry[]>, options: IInternalPickOptions, token: CancellationToken = CancellationToken.None): TPromise<IPickOpenEntry> { - const autoFocus = options.autoFocus; - - // Use a generated token to avoid race conditions from long running promises - const currentPickerToken = defaultGenerator.nextId(); - this.currentPickerToken = currentPickerToken; - - // Update context - this.setQuickOpenContextKey(options.contextKey); - - // Create upon first open - if (!this.pickOpenWidget) { - this.pickOpenWidget = this._register(new QuickOpenWidget( - document.getElementById(this.partService.getWorkbenchElementId()), - { - onOk: () => { /* ignore, handle later */ }, - onCancel: () => { /* ignore, handle later */ }, - onType: (value: string) => { /* ignore, handle later */ }, - onShow: () => this.handleOnShow(true), - onHide: (reason) => this.handleOnHide(true, reason) - }, { - inputPlaceHolder: options.placeHolder || '', - keyboardSupport: false, - treeCreator: (container, config, opts) => this.instantiationService.createInstance(WorkbenchTree, container, config, opts) - } - )); - this._register(attachQuickOpenStyler(this.pickOpenWidget, this.themeService, { background: SIDE_BAR_BACKGROUND, foreground: SIDE_BAR_FOREGROUND })); - - const pickOpenContainer = this.pickOpenWidget.create(); - addClass(pickOpenContainer, 'show-file-icons'); - this.positionQuickOpenWidget(); - } - - // Update otherwise - else { - this.pickOpenWidget.setPlaceHolder(options.placeHolder || ''); - } - - // Respect input value - if (options.value) { - this.pickOpenWidget.setValue(options.value, options.valueSelection); - } - - // Respect password - this.pickOpenWidget.setPassword(options.password); - - // Input decoration - if (!types.isUndefinedOrNull(options.inputDecoration)) { - this.pickOpenWidget.showInputDecoration(options.inputDecoration); - } else { - this.pickOpenWidget.clearInputDecoration(); - } - - // Layout - if (this.layoutDimensions) { - this.pickOpenWidget.layout(this.layoutDimensions); - } - - return new TPromise<IPickOpenEntry>((complete, error) => { - - // Detect cancellation while pick promise is loading - this.pickOpenWidget.setCallbacks({ - onCancel: () => { complete(void 0); }, - onOk: () => { /* ignore, handle later */ }, - onType: (value: string) => { /* ignore, handle later */ }, - }); - - // hide widget when being cancelled - token.onCancellationRequested(e => { - if (this.currentPickerToken === currentPickerToken) { - this.pickOpenWidget.hide(HideReason.CANCELED); - } - }); - - let picksPromiseDone = false; - - // Resolve picks - picksPromise.then(picks => { - if (this.currentPickerToken !== currentPickerToken) { - return complete(void 0); // Return as canceled if another request came after or user canceled - } - - picksPromiseDone = true; - - // Reset Progress - this.pickOpenWidget.getProgressBar().stop().hide(); - - // Model - const model = new QuickOpenModel([], new PickOpenActionProvider()); - const entries = picks.map((e, index) => { - const onPreview = () => { - if (options.onDidFocus) { - options.onDidFocus(e); - } - }; - return this.instantiationService.createInstance(PickOpenEntry, e, index, onPreview, () => this.pickOpenWidget.refresh()); - }); - if (picks.length === 0) { - entries.push(this.instantiationService.createInstance(PickOpenEntry, { label: nls.localize('emptyPicks', "There are no entries to pick from") }, 0, null, null)); - } - - model.setEntries(entries); - - // Handlers - const callbacks = { - onOk: () => { - if (picks.length === 0) { - return complete(null); - } - - let index = -1; - let context: IEntryRunContext; - entries.forEach(entry => { - if (entry.shouldRunWithContext) { - index = entry.index; - context = entry.shouldRunWithContext; - } - }); - - const selectedPick = picks[index]; - - if (selectedPick && typeof selectedPick.run === 'function') { - selectedPick.run(context); - } - - complete(selectedPick || null); - }, - onCancel: () => complete(void 0), - onFocusLost: () => !this.closeOnFocusLost || options.ignoreFocusLost, - onType: (value: string) => { - - // the caller takes care of all input - if (options.onDidType) { - options.onDidType(value); - return; - } - - if (picks.length === 0) { - return; - } - - value = value ? strings.trim(value) : value; - - // Reset filtering - if (!value) { - entries.forEach(e => { - e.setHighlights(null); - e.setHidden(false); - }); - } - - // Filter by value (since we support octicons, use octicon aware fuzzy matching) - else { - entries.forEach(entry => { - const { labelHighlights, descriptionHighlights, detailHighlights } = entry.matchesFuzzy(value, options); - - if (entry.shouldAlwaysShow() || labelHighlights || descriptionHighlights || detailHighlights) { - entry.setHighlights(labelHighlights, descriptionHighlights, detailHighlights); - entry.setHidden(false); - } else { - entry.setHighlights(null, null, null); - entry.setHidden(true); - } - }); - } - - // Sort by value - const normalizedSearchValue = value ? strings.stripWildcards(value.toLowerCase()) : value; - model.entries.sort((pickA: PickOpenEntry, pickB: PickOpenEntry) => { - if (!value) { - return pickA.index - pickB.index; // restore natural order - } - - return compareEntries(pickA, pickB, normalizedSearchValue); - }); - - this.pickOpenWidget.refresh(model, value ? { autoFocusFirstEntry: true } : autoFocus); - }, - onShow: () => this.handleOnShow(true), - onHide: (reason: HideReason) => this.handleOnHide(true, reason) - }; - this.pickOpenWidget.setCallbacks(callbacks); - - // Set input - if (!this.pickOpenWidget.isVisible()) { - this.pickOpenWidget.show(model, { autoFocus, quickNavigateConfiguration: options.quickNavigateConfiguration }); - } else { - this.pickOpenWidget.setInput(model, autoFocus); - } - - // The user might have typed something (or options.value was set) so we need to play back - // the input box value through our callbacks to filter the result accordingly. - const inputValue = this.pickOpenWidget.getInputBox().value; - if (inputValue) { - callbacks.onType(inputValue); - } - }, (err) => { - this.pickOpenWidget.hide(); - - error(err); - }); - - // Progress if task takes a long time - TPromise.timeout(800).then(() => { - if (!picksPromiseDone && this.currentPickerToken === currentPickerToken) { - this.pickOpenWidget.getProgressBar().infinite().show(); - } - }); - - // Show picker empty if resolving takes a while - if (!picksPromiseDone) { - this.pickOpenWidget.show(new QuickOpenModel()); - } - }); } accept(): void { - [this.quickOpenWidget, this.pickOpenWidget].forEach(w => { - if (w && w.isVisible()) { - w.accept(); - } - }); + if (this.quickOpenWidget && this.quickOpenWidget.isVisible()) { + this.quickOpenWidget.accept(); + } } focus(): void { - [this.quickOpenWidget, this.pickOpenWidget].forEach(w => { - if (w && w.isVisible()) { - w.focus(); - } - }); + if (this.quickOpenWidget && this.quickOpenWidget.isVisible()) { + this.quickOpenWidget.focus(); + } } close(): void { - [this.quickOpenWidget, this.pickOpenWidget].forEach(w => { - if (w && w.isVisible()) { - w.hide(HideReason.CANCELED); - } - }); + if (this.quickOpenWidget && this.quickOpenWidget.isVisible()) { + this.quickOpenWidget.hide(HideReason.CANCELED); + } } private emitQuickOpenVisibilityChange(isVisible: boolean): void { @@ -463,8 +169,8 @@ export class QuickOpenController extends Component implements IQuickOpenService onOk: () => { /* ignore */ }, onCancel: () => { /* ignore */ }, onType: (value: string) => this.onType(value || ''), - onShow: () => this.handleOnShow(false), - onHide: (reason) => this.handleOnHide(false, reason), + onShow: () => this.handleOnShow(), + onHide: (reason) => this.handleOnHide(reason), onFocusLost: () => !this.closeOnFocusLost }, { inputPlaceHolder: this.hasHandler(HELP_PREFIX) ? nls.localize('quickOpenInput', "Type '?' to get help on the actions you can take from here") : '', @@ -526,42 +232,29 @@ export class QuickOpenController extends Component implements IQuickOpenService if (this.quickOpenWidget) { this.quickOpenWidget.getElement().style.top = `${titlebarOffset}px`; } - - if (this.pickOpenWidget) { - this.pickOpenWidget.getElement().style.top = `${titlebarOffset}px`; - } } - private handleOnShow(isPicker: boolean): void { - if (isPicker && this.quickOpenWidget) { - this.quickOpenWidget.hide(HideReason.FOCUS_LOST); - } else if (!isPicker && this.pickOpenWidget) { - this.pickOpenWidget.hide(HideReason.FOCUS_LOST); - } - + private handleOnShow(): void { this.emitQuickOpenVisibilityChange(true); } - private handleOnHide(isPicker: boolean, reason: HideReason): void { - if (!isPicker) { + private handleOnHide(reason: HideReason): void { + // Clear state + this.previousActiveHandlerDescriptor = null; - // Clear state - this.previousActiveHandlerDescriptor = null; + // Pass to handlers + for (let prefix in this.mapResolvedHandlersToPrefix) { + const promise = this.mapResolvedHandlersToPrefix[prefix]; + promise.then(handler => { + this.handlerOnOpenCalled[prefix] = false; - // Pass to handlers - for (let prefix in this.mapResolvedHandlersToPrefix) { - const promise = this.mapResolvedHandlersToPrefix[prefix]; - promise.then(handler => { - this.handlerOnOpenCalled[prefix] = false; + handler.onClose(reason === HideReason.CANCELED); // Don't check if onOpen was called to preserve old behaviour for now + }); + } - handler.onClose(reason === HideReason.CANCELED); // Don't check if onOpen was called to preserve old behaviour for now - }); - } - - // Complete promises that are waiting - while (this.promisesToCompleteOnHide.length) { - this.promisesToCompleteOnHide.pop()(true); - } + // Complete promises that are waiting + while (this.promisesToCompleteOnHide.length) { + this.promisesToCompleteOnHide.pop()(true); } if (reason !== HideReason.FOCUS_LOST) { @@ -670,11 +363,11 @@ export class QuickOpenController extends Component implements IQuickOpenService this.previousActiveHandlerDescriptor = handlerDescriptor; // Progress if task takes a long time - TPromise.timeout(instantProgress ? 0 : 800).then(() => { + setTimeout(() => { if (!resultPromiseDone && currentResultToken === this.currentResultToken) { this.quickOpenWidget.getProgressBar().infinite().show(); } - }); + }, instantProgress ? 0 : 800); // Promise done handling resultPromise.done(() => { @@ -709,7 +402,14 @@ export class QuickOpenController extends Component implements IQuickOpenService const previousInput = this.quickOpenWidget.getInput(); const wasShowingHistory = previousInput && previousInput.entries && previousInput.entries.some(e => e instanceof EditorHistoryEntry || e instanceof EditorHistoryEntryGroup); if (wasShowingHistory || matchingHistoryEntries.length > 0) { - (resolvedHandler.hasShortResponseTime() ? TPromise.timeout(QuickOpenController.MAX_SHORT_RESPONSE_TIME) : TPromise.as(undefined)).then(() => { + let responseDelay: Thenable<void>; + if (resolvedHandler.hasShortResponseTime()) { + responseDelay = timeout(QuickOpenController.MAX_SHORT_RESPONSE_TIME); + } else { + responseDelay = Promise.resolve(void 0); + } + + responseDelay.then(() => { if (this.currentResultToken === currentResultToken && !inputSet) { this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true }); inputSet = true; @@ -875,10 +575,6 @@ export class QuickOpenController extends Component implements IQuickOpenService if (this.quickOpenWidget) { this.quickOpenWidget.layout(this.layoutDimensions); } - - if (this.pickOpenWidget) { - this.pickOpenWidget.layout(this.layoutDimensions); - } } } @@ -896,169 +592,6 @@ class PlaceholderQuickOpenEntry extends QuickOpenEntryGroup { } } -class PickOpenEntry extends PlaceholderQuickOpenEntry implements IPickOpenItem { - private _shouldRunWithContext: IEntryRunContext; - private description: string; - private detail: string; - private tooltip: string; - private descriptionTooltip: string; - private hasSeparator: boolean; - private separatorLabel: string; - private alwaysShow: boolean; - private resource: URI; - private fileKind: FileKind; - private _action: IAction; - private removed: boolean; - private payload: any; - private labelOcticons: IParsedOcticons; - private descriptionOcticons: IParsedOcticons; - private detailOcticons: IParsedOcticons; - - constructor( - item: IPickOpenEntry, - private _index: number, - private onPreview: () => void, - private onRemove: () => void, - @IModeService private modeService: IModeService, - @IModelService private modelService: IModelService - ) { - super(item.label); - - this.description = item.description; - this.detail = item.detail; - this.tooltip = item.tooltip; - this.descriptionOcticons = item.description ? parseOcticons(item.description) : void 0; - this.descriptionTooltip = this.descriptionOcticons ? this.descriptionOcticons.text : void 0; - this.hasSeparator = item.separator && item.separator.border; - this.separatorLabel = item.separator && item.separator.label; - this.alwaysShow = item.alwaysShow; - this._action = item.action; - this.payload = item.payload; - - const fileItem = <IFilePickOpenEntry>item; - this.resource = fileItem.resource; - this.fileKind = fileItem.fileKind; - } - - matchesFuzzy(query: string, options: IInternalPickOptions): { labelHighlights: IMatch[], descriptionHighlights: IMatch[], detailHighlights: IMatch[] } { - if (!this.labelOcticons) { - this.labelOcticons = parseOcticons(this.getLabel()); // parse on demand - } - - const detail = this.getDetail(); - if (detail && options.matchOnDetail && !this.detailOcticons) { - this.detailOcticons = parseOcticons(detail); // parse on demand - } - - return { - labelHighlights: matchesFuzzyOcticonAware(query, this.labelOcticons), - descriptionHighlights: options.matchOnDescription && this.descriptionOcticons ? matchesFuzzyOcticonAware(query, this.descriptionOcticons) : void 0, - detailHighlights: options.matchOnDetail && this.detailOcticons ? matchesFuzzyOcticonAware(query, this.detailOcticons) : void 0 - }; - } - - getPayload(): any { - return this.payload; - } - - remove(): void { - super.setHidden(true); - this.removed = true; - - this.onRemove(); - } - - isHidden(): boolean { - return this.removed || super.isHidden(); - } - - get action(): IAction { - return this._action; - } - - get index(): number { - return this._index; - } - - getLabelOptions(): IIconLabelValueOptions { - return { - extraClasses: this.resource ? getIconClasses(this.modelService, this.modeService, this.resource, this.fileKind) : [] - }; - } - - get shouldRunWithContext(): IEntryRunContext { - return this._shouldRunWithContext; - } - - getDescription(): string { - return this.description; - } - - getDetail(): string { - return this.detail; - } - - getTooltip(): string { - return this.tooltip; - } - - getDescriptionTooltip(): string { - return this.descriptionTooltip; - } - - showBorder(): boolean { - return this.hasSeparator; - } - - getGroupLabel(): string { - return this.separatorLabel; - } - - shouldAlwaysShow(): boolean { - return this.alwaysShow; - } - - getResource(): URI { - return this.resource; - } - - run(mode: Mode, context: IEntryRunContext): boolean { - if (mode === Mode.OPEN) { - this._shouldRunWithContext = context; - - return true; - } - - if (mode === Mode.PREVIEW && this.onPreview) { - this.onPreview(); - } - - return false; - } -} - -class PickOpenActionProvider implements IActionProvider { - hasActions(tree: ITree, element: PickOpenEntry): boolean { - return !!element.action; - } - - getActions(tree: ITree, element: PickOpenEntry): TPromise<IAction[]> { - return TPromise.as(element.action ? [element.action] : []); - } - - hasSecondaryActions(tree: ITree, element: PickOpenEntry): boolean { - return false; - } - - getSecondaryActions(tree: ITree, element: PickOpenEntry): TPromise<IAction[]> { - return TPromise.as([]); - } - - getActionItem(tree: ITree, element: PickOpenEntry, action: Action): BaseActionItem { - return null; - } -} - class EditorHistoryHandler { private scorerCache: ScorerCache; @@ -1115,7 +648,7 @@ class EditorHistoryHandler { // Sort by score and provide a fallback sorter that keeps the // recency of items in case the score for items is the same - .sort((e1, e2) => compareItemsByScore(e1, e2, query, false, accessor, this.scorerCache, (e1, e2, query, accessor) => -1)); + .sort((e1, e2) => compareItemsByScore(e1, e2, query, false, accessor, this.scorerCache, () => -1)); } } @@ -1150,9 +683,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, + @ILabelService labelService: ILabelService, @IFileService fileService: IFileService ) { super(editorService); @@ -1167,8 +699,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 = labelService.getUriLabel(resources.dirname(this.resource), true); this.dirty = this.resource && this.textFileService.isDirty(this.resource); if (this.dirty && this.textFileService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { @@ -1245,7 +777,9 @@ export class RemoveFromEditorHistoryAction extends Action { constructor( id: string, label: string, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, + @IModelService private modelService: IModelService, + @IModeService private modeService: IModeService, @IInstantiationService private instantiationService: IInstantiationService, @IHistoryService private historyService: IHistoryService ) { @@ -1253,7 +787,7 @@ export class RemoveFromEditorHistoryAction extends Action { } run(): TPromise<any> { - interface IHistoryPickEntry extends IFilePickOpenEntry { + interface IHistoryPickEntry extends IQuickPickItem { input: IEditorInput | IResourceInput; } @@ -1263,13 +797,13 @@ export class RemoveFromEditorHistoryAction extends Action { return <IHistoryPickEntry>{ input: h, - resource: entry.getResource(), + iconClasses: getIconClasses(this.modelService, this.modeService, entry.getResource()), label: entry.getLabel(), description: entry.getDescription() }; }); - return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select an editor entry to remove from history"), autoFocus: { autoFocusFirstEntry: true }, matchOnDescription: true }).then(pick => { + return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select an editor entry to remove from history"), matchOnDescription: true }).then(pick => { if (pick) { this.historyService.remove(pick.input); } 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<Viewlet> { @@ -77,7 +76,10 @@ export class SidebarPart extends CompositePart<Viewlet> { 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<Viewlet> { 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<Viewlet> { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 175276033e2..baccf0bc4f9 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -10,7 +10,6 @@ import * as nls from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { TPromise } from 'vs/base/common/winjs.base'; 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'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -28,15 +27,15 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { isThemeColor } from 'vs/editor/common/editorCommon'; import { Color } from 'vs/base/common/color'; -import { addClass, EventHelper, createStyleSheet } from 'vs/base/browser/dom'; +import { addClass, EventHelper, createStyleSheet, addDisposableListener, removeNode } from 'vs/base/browser/dom'; import { INotificationService } from 'vs/platform/notification/common/notification'; export class StatusbarPart extends Part implements IStatusbarService { _serviceBrand: any; - private static readonly PRIORITY_PROP = 'priority'; - private static readonly ALIGNMENT_PROP = 'alignment'; + private static readonly PRIORITY_PROP = 'statusbar-entry-priority'; + private static readonly ALIGNMENT_PROP = 'statusbar-entry-alignment'; private statusItemsContainer: HTMLElement; private statusMsgDispose: IDisposable; @@ -71,7 +70,7 @@ export class StatusbarPart extends Part implements IStatusbarService { let inserted = false; for (let i = 0; i < neighbours.length; i++) { const neighbour = neighbours[i]; - const nPriority = $(neighbour).getProperty(StatusbarPart.PRIORITY_PROP); + const nPriority = Number(neighbour.getAttribute(StatusbarPart.PRIORITY_PROP)); if ( alignment === StatusbarAlignment.LEFT && nPriority < priority || alignment === StatusbarAlignment.RIGHT && nPriority > priority @@ -87,7 +86,7 @@ export class StatusbarPart extends Part implements IStatusbarService { } return toDisposable(() => { - $(el).destroy(); + removeNode(el); if (toDispose) { toDispose.dispose(); @@ -102,7 +101,7 @@ export class StatusbarPart extends Part implements IStatusbarService { const children = container.children; for (let i = 0; i < children.length; i++) { const childElement = <HTMLElement>children.item(i); - if ($(childElement).getProperty(StatusbarPart.ALIGNMENT_PROP) === alignment) { + if (Number(childElement.getAttribute(StatusbarPart.ALIGNMENT_PROP)) === alignment) { entries.push(childElement); } } @@ -134,22 +133,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}; }`; @@ -168,8 +167,8 @@ export class StatusbarPart extends Part implements IStatusbarService { addClass(el, 'left'); } - $(el).setProperty(StatusbarPart.PRIORITY_PROP, priority); - $(el).setProperty(StatusbarPart.ALIGNMENT_PROP, alignment); + el.setAttribute(StatusbarPart.PRIORITY_PROP, String(priority)); + el.setAttribute(StatusbarPart.ALIGNMENT_PROP, String(alignment)); return el; } @@ -242,7 +241,7 @@ class StatusBarEntryItem implements IStatusbarItem { if (this.entry.command) { textContainer = document.createElement('a'); - $(textContainer).on('click', () => this.executeCommand(this.entry.command, this.entry.arguments), toDispose); + toDispose.push(addDisposableListener(textContainer, 'click', () => this.executeCommand(this.entry.command, this.entry.arguments))); } else { textContainer = document.createElement('span'); } @@ -252,7 +251,7 @@ class StatusBarEntryItem implements IStatusbarItem { // Tooltip if (this.entry.tooltip) { - $(textContainer).title(this.entry.tooltip); + textContainer.title = this.entry.tooltip; } // Color @@ -263,15 +262,15 @@ class StatusBarEntryItem implements IStatusbarItem { color = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString(); toDispose.push(this.themeService.onThemeChange(theme => { let colorValue = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString(); - $(textContainer).color(colorValue); + textContainer.style.color = colorValue; })); } - $(textContainer).color(color); + textContainer.style.color = color; } // Context Menu if (this.entry.extensionId) { - $(textContainer).on('contextmenu', e => { + toDispose.push(addDisposableListener(textContainer, 'contextmenu', e => { EventHelper.stop(e, true); this.contextMenuService.showContextMenu({ @@ -279,7 +278,7 @@ class StatusBarEntryItem implements IStatusbarItem { getActionsContext: () => this.entry.extensionId, getActions: () => TPromise.as([manageExtensionAction]) }); - }, toDispose); + })); } el.appendChild(textContainer); diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 28af45e5b91..42965aae65c 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -6,7 +6,6 @@ .monaco-workbench > .part.titlebar { box-sizing: border-box; width: 100%; - font-size: 12px; padding: 0 70px; overflow: hidden; flex-shrink: 0; @@ -26,14 +25,18 @@ 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; + margin-left: auto; + margin-right: auto; zoom: 1; /* prevent zooming */ } @@ -44,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, @@ -56,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 { @@ -84,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 59% rename from src/vs/workbench/browser/parts/menubar/menubarPart.ts rename to src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 7515a99f803..ea32ff76863 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -5,41 +5,41 @@ '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 * as strings from 'vs/base/common/strings'; +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'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ActionRunner, IActionRunner, IAction, Action } from 'vs/base/common/actions'; -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 * as DOM from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { isWindows, 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 { isMacintosh } from 'vs/base/common/platform'; +import { Menu, IMenuOptions, SubmenuAction, MENU_MNEMONIC_REGEX, cleanMnemonic, MENU_ESCAPED_MNEMONIC_REGEX } from 'vs/base/browser/ui/menu/menu'; +import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; 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, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { getPathLabel } from 'vs/base/common/labels'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; 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 { ILabelService } from 'vs/platform/label/common/label'; +import { foreground } from 'vs/platform/theme/common/colorRegistry'; +import { IUpdateService, StateType } from 'vs/platform/update/common/update'; + +const $ = DOM.$; interface CustomMenu { title: string; - buttonElement: Builder; - titleElement: Builder; + buttonElement: HTMLElement; + titleElement: HTMLElement; actions?: IAction[]; } @@ -50,7 +50,7 @@ enum MenubarState { OPEN } -export class MenubarPart extends Part { +export class MenubarControl extends Disposable { private keys = [ 'files.autoSave', @@ -60,7 +60,7 @@ export class MenubarPart extends Part { 'workbench.statusBar.visible', 'workbench.activityBar.visible', 'window.enableMenuBarMnemonics', - // 'window.nativeTabs' + 'window.nativeTabs' ]; private topLevelMenus: { @@ -69,9 +69,8 @@ export class MenubarPart extends Part { 'Selection': IMenu; 'View': IMenu; 'Go': IMenu; - 'Terminal': IMenu; 'Debug': IMenu; - 'Tasks': IMenu; + 'Terminal': IMenu; 'Window'?: IMenu; 'Help': IMenu; [index: string]: IMenu; @@ -83,15 +82,14 @@ export class MenubarPart extends Part { 'Selection': nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection"), 'View': nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View"), 'Go': nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go"), - 'Terminal': nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "Ter&&minal"), 'Debug': nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug"), - 'Tasks': nls.localize({ key: 'mTasks', comment: ['&& denotes a mnemonic'] }, "&&Tasks"), + 'Terminal': nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "Ter&&minal"), 'Help': nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help") }; private focusedMenu: { index: number; - holder?: Builder; + holder?: HTMLElement; widget?: Menu; }; @@ -99,38 +97,36 @@ export class MenubarPart extends Part { private menuUpdater: RunOnceScheduler; private actionRunner: IActionRunner; - private focusToReturn: Builder; - private container: Builder; + private focusToReturn: HTMLElement; + private container: HTMLElement; private recentlyOpened: IRecentlyOpened; private updatePending: boolean; - private _modifierKeyStatus: IModifierKeyStatus; private _focusState: MenubarState; - private _onVisibilityChange: Emitter<Dimension>; + // Input-related + private _mnemonicsInUse: boolean; + private openedViaKeyboard: boolean; + private awaitingAltRelease: boolean; + private mnemonics: Map<KeyCode, number>; - private initialSizing: { - menuButtonPaddingLeftRight?: number; - menubarHeight?: number; - menubarPaddingLeft?: number; - menubarPaddingRight?: number; - menubarFontSize?: number; - } = {}; + private _onVisibilityChange: Emitter<boolean>; - 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 + @ILabelService private labelService: ILabelService, + @IUpdateService private updateService: IUpdateService ) { - super(id, { hasTitle: false }, themeService); + + super(); this.topLevelMenus = { 'File': this._register(this.menuService.createMenu(MenuId.MenubarFileMenu, this.contextKeyService)), @@ -138,24 +134,23 @@ export class MenubarPart extends Part { 'Selection': this._register(this.menuService.createMenu(MenuId.MenubarSelectionMenu, this.contextKeyService)), 'View': this._register(this.menuService.createMenu(MenuId.MenubarViewMenu, this.contextKeyService)), 'Go': this._register(this.menuService.createMenu(MenuId.MenubarGoMenu, this.contextKeyService)), - 'Terminal': this._register(this.menuService.createMenu(MenuId.MenubarTerminalMenu, this.contextKeyService)), 'Debug': this._register(this.menuService.createMenu(MenuId.MenubarDebugMenu, this.contextKeyService)), - 'Tasks': this._register(this.menuService.createMenu(MenuId.MenubarTasksMenu, this.contextKeyService)), + 'Terminal': this._register(this.menuService.createMenu(MenuId.MenubarTerminalMenu, this.contextKeyService)), 'Help': this._register(this.menuService.createMenu(MenuId.MenubarHelpMenu, this.contextKeyService)) }; 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(), 200)); this.actionRunner = this._register(new ActionRunner()); this._register(this.actionRunner.onDidBeforeRun(() => { this.setUnfocusedState(); })); - this._onVisibilityChange = this._register(new Emitter<Dimension>()); + this._onVisibilityChange = this._register(new Emitter<boolean>()); if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') { for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { @@ -182,10 +177,6 @@ export class MenubarPart extends Part { return enableMenuBarMnemonics; } - private get currentMultiCursorSetting(): string { - return this.configurationService.getValue<string>('editor.multiCursorModifier'); - } - private get currentAutoSaveSetting(): string { return this.configurationService.getValue<string>('files.autoSave'); } @@ -238,21 +229,27 @@ export class MenubarPart extends Part { return; } + const isVisible = this.isVisible; + const isOpen = this.isOpen; + const isFocused = this.isFocused; + + this._focusState = value; + switch (value) { case MenubarState.HIDDEN: - if (this.isVisible) { + if (isVisible) { this.hideMenubar(); } - if (this.isOpen) { + if (isOpen) { this.cleanupCustomMenu(); } - if (this.isFocused) { + if (isFocused) { this.focusedMenu = null; if (this.focusToReturn) { - this.focusToReturn.domFocus(); + this.focusToReturn.focus(); this.focusToReturn = null; } } @@ -260,48 +257,48 @@ export class MenubarPart extends Part { break; case MenubarState.VISIBLE: - if (!this.isVisible) { + if (!isVisible) { this.showMenubar(); } - if (this.isOpen) { + if (isOpen) { this.cleanupCustomMenu(); } - if (this.isFocused) { + if (isFocused) { if (this.focusedMenu) { - this.customMenus[this.focusedMenu.index].buttonElement.domBlur(); + this.customMenus[this.focusedMenu.index].buttonElement.blur(); } this.focusedMenu = null; if (this.focusToReturn) { - this.focusToReturn.domFocus(); + this.focusToReturn.focus(); this.focusToReturn = null; } } break; case MenubarState.FOCUSED: - if (!this.isVisible) { + if (!isVisible) { this.showMenubar(); } - if (this.isOpen) { + if (isOpen) { this.cleanupCustomMenu(); } if (this.focusedMenu) { - this.customMenus[this.focusedMenu.index].buttonElement.domFocus(); + this.customMenus[this.focusedMenu.index].buttonElement.focus(); } break; case MenubarState.OPEN: - if (!this.isVisible) { + if (!isVisible) { this.showMenubar(); } if (this.focusedMenu) { - this.showCustomMenu(this.focusedMenu.index); + this.showCustomMenu(this.focusedMenu.index, this.openedViaKeyboard); } break; } @@ -309,6 +306,14 @@ export class MenubarPart extends Part { this._focusState = value; } + private get mnemonicsInUse(): boolean { + return this._mnemonicsInUse; + } + + private set mnemonicsInUse(value: boolean) { + this._mnemonicsInUse = value; + } + private get isVisible(): boolean { return this.focusState >= MenubarState.VISIBLE; } @@ -322,16 +327,17 @@ export class MenubarPart extends Part { } private onDidChangeFullscreen(): void { - this.updateStyles(); + this.setUnfocusedState(); } private onDidChangeWindowFocus(hasFocus: boolean): void { if (this.container) { if (hasFocus) { - this.container.removeClass('inactive'); + DOM.removeClass(this.container, 'inactive'); } else { - this.container.addClass('inactive'); + DOM.addClass(this.container, 'inactive'); this.setUnfocusedState(); + this.awaitingAltRelease = false; } } } @@ -340,54 +346,81 @@ 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; + if (this.currentMenubarVisibility === 'toggle' || this.currentMenubarVisibility === 'hidden') { + this.focusState = MenubarState.HIDDEN; + } else if (this.currentMenubarVisibility === 'default' && browser.isFullscreen()) { + this.focusState = MenubarState.HIDDEN; + } else { + this.focusState = MenubarState.VISIBLE; + } + + this.mnemonicsInUse = false; + this.updateMnemonicVisibility(false); } 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; } + // Alt key pressed while menu is focused. This should return focus away from the menubar + if (this.isFocused && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.altKey) { + this.setUnfocusedState(); + this.mnemonicsInUse = false; + this.awaitingAltRelease = true; + } + + // Clean alt key press and release if (allModifiersReleased && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.lastKeyReleased === 'alt') { - if (!this.isFocused) { - this.focusedMenu = { index: 0 }; - this.focusState = MenubarState.FOCUSED; - } else if (!this.isOpen) { - this.setUnfocusedState(); + if (!this.awaitingAltRelease) { + if (!this.isFocused) { + this.mnemonicsInUse = true; + this.focusedMenu = { index: 0 }; + this.focusState = MenubarState.FOCUSED; + } else if (!this.isOpen) { + this.setUnfocusedState(); + } } } - if (this.currentEnableMenuBarMnemonics && this.customMenus) { - this.customMenus.forEach(customMenu => { - let child = customMenu.titleElement.child(); - if (child) { - child.style('text-decoration', modifierKeyStatus.altKey ? 'underline' : null); - } - }); + // Alt key released + if (!modifierKeyStatus.altKey && modifierKeyStatus.lastKeyReleased === 'alt') { + this.awaitingAltRelease = false; } + + if (this.currentEnableMenuBarMnemonics && this.customMenus && !this.isOpen) { + this.updateMnemonicVisibility((!this.awaitingAltRelease && modifierKeyStatus.altKey) || this.mnemonicsInUse); + } + } + + private updateMnemonicVisibility(visible: boolean): void { + this.customMenus.forEach(customMenu => { + if (customMenu.titleElement.children.length) { + let child = customMenu.titleElement.children.item(0) as HTMLElement; + if (child) { + child.style.textDecoration = visible ? 'underline' : null; + } + } + }); } private onRecentlyOpenedChange(): void { @@ -402,7 +435,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())); @@ -429,29 +462,24 @@ 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; - } - - private registerMnemonic(topLevelElement: HTMLElement, mnemonic: string): void { - topLevelElement.accessKey = mnemonic.toLocaleLowerCase(); + private registerMnemonic(menuIndex: number, mnemonic: string): void { + this.mnemonics.set(KeyCodeUtils.fromString(mnemonic), menuIndex); } private setCheckedStatus(action: IAction | IMenubarMenuItemAction) { @@ -468,16 +496,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"); @@ -506,23 +524,23 @@ export class MenubarPart extends Part { break; } - return this.currentEnableMenuBarMnemonics ? label : label.replace(/&&(.)/g, '$1'); + return label; } - private createOpenRecentMenuAction(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): IAction { + private createOpenRecentMenuAction(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, commandId: string, isFile: boolean): IAction { let label: string; let uri: URI; - if (isSingleFolderWorkspaceIdentifier(workspace)) { - label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); + if (isSingleFolderWorkspaceIdentifier(workspace) && !isFile) { + label = this.labelService.getWorkspaceLabel(workspace, { verbose: true }); uri = workspace; } else if (isWorkspaceIdentifier(workspace)) { - label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); + label = this.labelService.getWorkspaceLabel(workspace, { verbose: true }); uri = URI.file(workspace.configPath); } else { - label = getPathLabel(workspace, this.environmentService); - uri = URI.file(workspace); + uri = workspace; + label = this.labelService.getUriLabel(uri); } return new Action(commandId, label, undefined, undefined, (event) => { @@ -545,7 +563,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)); } @@ -553,7 +571,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)); } @@ -563,12 +581,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; } @@ -581,11 +645,12 @@ export class MenubarPart extends Part { return; } - this.container.attr('role', 'menubar'); + this.container.attributes['role'] = 'menubar'; const firstTimeSetup = this.customMenus === undefined; if (firstTimeSetup) { this.customMenus = []; + this.mnemonics = new Map<KeyCode, number>(); } let idx = 0; @@ -593,13 +658,16 @@ export class MenubarPart extends Part { for (let menuTitle of Object.keys(this.topLevelMenus)) { const menu: IMenu = this.topLevelMenus[menuTitle]; let menuIndex = idx++; + const cleanMenuLabel = cleanMnemonic(this.topLevelTitles[menuTitle]); // Create the top level menu button element if (firstTimeSetup) { - const buttonElement = $(this.container).div({ class: 'menubar-menu-button' }).attr({ 'role': 'menu', 'tabindex': 0 }); - buttonElement.attr('aria-label', this.topLevelTitles[menuTitle].replace(/&&(.)/g, '$1')); - const titleElement = $(buttonElement).div({ class: 'menubar-menu-title', 'aria-hidden': true }); + const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': 0, 'aria-label': cleanMenuLabel, 'aria-haspopup': true }); + const titleElement = $('div.menubar-menu-title', { 'role': 'none', 'aria-hidden': true }); + + buttonElement.appendChild(titleElement); + this.container.appendChild(buttonElement); this.customMenus.push({ title: menuTitle, @@ -609,15 +677,24 @@ export class MenubarPart extends Part { } // Update the button label to reflect mnemonics - let displayTitle = this.topLevelTitles[menuTitle].replace(/&&(.)/g, this.currentEnableMenuBarMnemonics ? '<mnemonic>$1</mnemonic>' : '$1'); - $(this.customMenus[menuIndex].titleElement).innerHtml(displayTitle); + this.customMenus[menuIndex].titleElement.innerHTML = this.currentEnableMenuBarMnemonics ? + strings.escape(this.topLevelTitles[menuTitle]).replace(MENU_ESCAPED_MNEMONIC_REGEX, '<mnemonic aria-hidden="true">$1</mnemonic>') : + cleanMenuLabel; - // Clear and register mnemonics due to updated settings - this.clearMnemonic(this.customMenus[menuIndex].buttonElement.getHTMLElement()); - if (this.currentEnableMenuBarMnemonics) { - let mnemonic = (/&&(.)/g).exec(this.topLevelTitles[menuTitle]); - if (mnemonic && mnemonic[1]) { - this.registerMnemonic(this.customMenus[menuIndex].buttonElement.getHTMLElement(), mnemonic[1]); + let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(this.topLevelTitles[menuTitle]); + + // Register mnemonics + if (mnemonicMatches) { + let mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[2]; + + if (firstTimeSetup) { + this.registerMnemonic(menuIndex, mnemonic); + } + + if (this.currentEnableMenuBarMnemonics) { + this.customMenus[menuIndex].buttonElement.setAttribute('aria-keyshortcuts', 'Alt+' + mnemonic.toLocaleLowerCase()); + } else { + this.customMenus[menuIndex].buttonElement.removeAttribute('aria-keyshortcuts'); } } @@ -656,12 +733,13 @@ export class MenubarPart extends Part { updateActions(menu, this.customMenus[menuIndex].actions); if (firstTimeSetup) { - this.customMenus[menuIndex].buttonElement.on(EventType.KEY_UP, (e) => { + this._register(DOM.addDisposableListener(this.customMenus[menuIndex].buttonElement, DOM.EventType.KEY_UP, (e) => { let event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; 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; @@ -671,47 +749,33 @@ export class MenubarPart extends Part { event.preventDefault(); event.stopPropagation(); } - }); + })); - this.customMenus[menuIndex].buttonElement.on(EventType.CLICK, (e) => { - if (this._modifierKeyStatus && (this._modifierKeyStatus.shiftKey || this._modifierKeyStatus.ctrlKey)) { - return; // supress keyboard shortcuts that shouldn't conflict - } - - if (this.isOpen) { - if (this.isCurrentMenu(menuIndex)) { - this.setUnfocusedState(); - } else { - this.cleanupCustomMenu(); - this.showCustomMenu(menuIndex); - } - } else { - this.focusedMenu = { index: menuIndex }; - this.focusState = MenubarState.OPEN; - } + this._register(DOM.addDisposableListener(this.customMenus[menuIndex].buttonElement, DOM.EventType.CLICK, (e) => { + this.onMenuTriggered(menuIndex, true); e.preventDefault(); e.stopPropagation(); - }); + })); - this.customMenus[menuIndex].buttonElement.on(EventType.MOUSE_ENTER, () => { + this._register(DOM.addDisposableListener(this.customMenus[menuIndex].buttonElement, DOM.EventType.MOUSE_ENTER, () => { if (this.isOpen && !this.isCurrentMenu(menuIndex)) { - this.customMenus[menuIndex].buttonElement.domFocus(); + this.customMenus[menuIndex].buttonElement.focus(); this.cleanupCustomMenu(); - this.showCustomMenu(menuIndex); + this.showCustomMenu(menuIndex, false); } else if (this.isFocused && !this.isOpen) { this.focusedMenu = { index: menuIndex }; - this.customMenus[menuIndex].buttonElement.domFocus(); + this.customMenus[menuIndex].buttonElement.focus(); } - }); - + })); } } if (firstTimeSetup) { - this.container.on(EventType.KEY_DOWN, (e) => { + this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_DOWN, (e) => { let event = new StandardKeyboardEvent(e as KeyboardEvent); let eventHandled = true; + const key = !!e.key ? KeyCodeUtils.fromString(e.key) : KeyCode.Unknown; if (event.equals(KeyCode.LeftArrow) || (event.shiftKey && event.keyCode === KeyCode.Tab)) { this.focusPrevious(); @@ -719,6 +783,9 @@ export class MenubarPart extends Part { this.focusNext(); } else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) { this.setUnfocusedState(); + } else if (!event.ctrlKey && this.currentEnableMenuBarMnemonics && this.mnemonicsInUse && this.mnemonics.has(key)) { + const menuIndex = this.mnemonics.get(key); + this.onMenuTriggered(menuIndex, false); } else { eventHandled = false; } @@ -727,36 +794,74 @@ export class MenubarPart extends Part { event.preventDefault(); event.stopPropagation(); } - }); + })); - this._register($(window).on(EventType.CLICK, () => { + this._register(DOM.addDisposableListener(window, DOM.EventType.CLICK, () => { // This click is outside the menubar so it counts as a focus out if (this.isFocused) { this.setUnfocusedState(); } })); + + this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, (e) => { + let event = e as FocusEvent; + + if (event.relatedTarget) { + if (!this.container.contains(event.relatedTarget as HTMLElement)) { + this.focusToReturn = event.relatedTarget as HTMLElement; + } + } + })); + + this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_OUT, (e) => { + let event = e as FocusEvent; + + if (event.relatedTarget) { + if (!this.container.contains(event.relatedTarget as HTMLElement)) { + this.focusToReturn = null; + this.setUnfocusedState(); + } + } + })); + + this._register(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e) => { + if (!this.currentEnableMenuBarMnemonics || !e.altKey || e.ctrlKey) { + return; + } + + const key = KeyCodeUtils.fromString(e.key); + if (!this.mnemonics.has(key)) { + return; + } + + // Prevent conflicts with keybindings + const standardKeyboardEvent = new StandardKeyboardEvent(e); + const resolvedResult = this.keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); + if (resolvedResult) { + return; + } + + this.mnemonicsInUse = true; + + const menuIndex = this.mnemonics.get(key); + this.onMenuTriggered(menuIndex, false); + })); } + } - this.container.on(EventType.FOCUS_IN, (e) => { - let event = e as FocusEvent; - - if (event.relatedTarget) { - if (!this.container.getHTMLElement().contains(event.relatedTarget as HTMLElement)) { - this.focusToReturn = $(event.relatedTarget as HTMLElement); - } + private onMenuTriggered(menuIndex: number, clicked: boolean) { + if (this.isOpen) { + if (this.isCurrentMenu(menuIndex)) { + this.setUnfocusedState(); + } else { + this.cleanupCustomMenu(); + this.showCustomMenu(menuIndex, this.openedViaKeyboard); } - }); - - this.container.on(EventType.FOCUS_OUT, (e) => { - let event = e as FocusEvent; - - if (event.relatedTarget) { - if (!this.container.getHTMLElement().contains(event.relatedTarget as HTMLElement)) { - this.focusToReturn = null; - this.setUnfocusedState(); - } - } - }); + } else { + this.focusedMenu = { index: menuIndex }; + this.openedViaKeyboard = !clicked; + this.focusState = MenubarState.OPEN; + } } private focusPrevious(): void { @@ -776,7 +881,7 @@ export class MenubarPart extends Part { this.showCustomMenu(newFocusedIndex); } else if (this.isFocused) { this.focusedMenu.index = newFocusedIndex; - this.customMenus[newFocusedIndex].buttonElement.domFocus(); + this.customMenus[newFocusedIndex].buttonElement.focus(); } } @@ -796,47 +901,101 @@ export class MenubarPart extends Part { this.showCustomMenu(newFocusedIndex); } else if (this.isFocused) { this.focusedMenu.index = newFocusedIndex; - this.customMenus[newFocusedIndex].buttonElement.domFocus(); + this.customMenus[newFocusedIndex].buttonElement.focus(); } } - 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<IMenubarKeybinding> { + // 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; @@ -847,10 +1006,12 @@ export class MenubarPart extends Part { private cleanupCustomMenu(): void { if (this.focusedMenu) { + // Remove focus from the menus first + this.customMenus[this.focusedMenu.index].buttonElement.focus(); if (this.focusedMenu.holder) { - $(this.focusedMenu.holder.getHTMLElement().parentElement).removeClass('open'); - this.focusedMenu.holder.dispose(); + DOM.removeClass(this.focusedMenu.holder.parentElement, 'open'); + this.focusedMenu.holder.remove(); } if (this.focusedMenu.widget) { @@ -861,25 +1022,24 @@ export class MenubarPart extends Part { } } - private showCustomMenu(menuIndex: number): void { + private showCustomMenu(menuIndex: number, selectFirst = true): void { const customMenu = this.customMenus[menuIndex]; + const menuHolder = $('div.menubar-menu-items-holder'); - let menuHolder = $(customMenu.buttonElement).div({ class: 'menubar-menu-items-holder' }); + DOM.addClass(customMenu.buttonElement, 'open'); + menuHolder.style.top = `${this.container.clientHeight}px`; + menuHolder.style.left = `${customMenu.buttonElement.getBoundingClientRect().left}px`; - $(menuHolder.getHTMLElement().parentElement).addClass('open'); - menuHolder.style({ - 'zoom': `${1 / browser.getZoomFactor()}`, - 'top': `${this.container.getClientArea().height * browser.getZoomFactor()}px` - }); + customMenu.buttonElement.appendChild(menuHolder); let menuOptions: IMenuOptions = { getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id), actionRunner: this.actionRunner, - // ariaLabel: 'File' - // actionItemProvider: (action) => { return this._getActionItem(action); } + enableMnemonics: this.mnemonicsInUse && this.currentEnableMenuBarMnemonics, + ariaLabel: customMenu.buttonElement.attributes['aria-label'].value }; - let menuWidget = this._register(new Menu(menuHolder.getHTMLElement(), customMenu.actions, menuOptions)); + let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions)); this._register(menuWidget.onDidCancel(() => { this.focusState = MenubarState.FOCUSED; @@ -891,7 +1051,7 @@ export class MenubarPart extends Part { }, 100); })); - menuWidget.focus(); + menuWidget.focus(selectFirst); this.focusedMenu = { index: menuIndex, @@ -900,79 +1060,45 @@ export class MenubarPart extends Part { }; } - public get onVisibilityChange(): Event<Dimension> { + public get onVisibilityChange(): Event<boolean> { 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: DOM.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 { + public getMenubarItemsDimensions(): DOM.Dimension { if (this.customMenus) { - const left = this.customMenus[0].buttonElement.getHTMLElement().getBoundingClientRect().left; - const right = this.customMenus[this.customMenus.length - 1].buttonElement.getHTMLElement().getBoundingClientRect().right; - return new Dimension(right - left, this.container.getClientArea().height); + const left = this.customMenus[0].buttonElement.getBoundingClientRect().left; + const right = this.customMenus[this.customMenus.length - 1].buttonElement.getBoundingClientRect().right; + return new DOM.Dimension(right - left, this.container.clientHeight); } - return new Dimension(0, 0); + return new DOM.Dimension(0, 0); } - public createContentArea(parent: HTMLElement): HTMLElement { - this.container = $(parent); - - if (!isWindows) { - return this.container.getHTMLElement(); - } + public create(parent: HTMLElement): HTMLElement { + this.container = parent; // Build the menubar if (this.container) { this.doSetupMenubar(); + + if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') { + this.setUnfocusedState(); + } } - return this.container.getHTMLElement(); + return this.container; } } @@ -980,7 +1106,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}; } `); @@ -989,7 +1115,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}; } `); @@ -999,9 +1125,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}; } `); @@ -1010,9 +1136,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}; } `); @@ -1021,18 +1147,18 @@ 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}; } @@ -1058,13 +1184,21 @@ 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, .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item { color: ${menuFgColor}; } + + .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item .action-menu-item .menu-item-check { + background-color: ${menuFgColor}; + } `); } @@ -1083,6 +1217,10 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused { color: ${selectedMenuItemFgColor}; } + + .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused .action-menu-item .menu-item-check { + background-color: ${selectedMenuItemFgColor}; + } `); } @@ -1191,4 +1329,4 @@ class ModifierKeyEmitter extends Emitter<IModifierKeyStatus> { 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 6188f621f55..a81ca96efd8 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -21,18 +21,21 @@ 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 { ILabelService } from 'vs/platform/label/common/label'; +import { Event } from 'vs/base/common/event'; export class TitlebarPart extends Part implements ITitleService { @@ -50,10 +53,12 @@ export class TitlebarPart extends Part implements ITitleService { 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; @@ -77,8 +82,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, + @ILabelService private labelService: ILabelService ) { super(id, { hasTitle: false }, themeService); @@ -96,7 +102,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 { @@ -115,10 +120,23 @@ 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<MenuBarVisibility>('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); + } + } + } + + onMenubarVisibilityChange(): Event<boolean> { + return this.menubarPart.onVisibilityChange; } private onActiveEditorChange(): void { @@ -225,16 +243,16 @@ export class TitlebarPart extends Part implements ITitleService { const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : ''; 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 rootName = this.labelService.getWorkspaceLabel(workspace); + const rootPath = root ? this.labelService.getUriLabel(root) : ''; const folderName = folder ? folder.name : ''; - const folderPath = folder ? labels.getPathLabel(folder.uri, this.environmentService) : ''; + const folderPath = folder ? this.labelService.getUriLabel(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<string>('window.title'); - return labels.template(titleTemplate, { + return template(titleTemplate, { activeEditorShort, activeEditorLong, activeEditorMedium, @@ -257,14 +275,20 @@ export class TitlebarPart extends Part implements ITitleService { // 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 @@ -276,11 +300,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) => { @@ -296,12 +322,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(); @@ -312,16 +338,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 @@ -339,16 +365,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(); + } } } @@ -357,6 +389,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()) { @@ -411,9 +449,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)); @@ -436,88 +474,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<MenuBarVisibility>('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<MenuBarVisibility>('window.menuBarVisibility') === 'toggle'; - if (menubarToggled && this.menubarWidth) { - this.title.style('visibility', 'hidden'); - - // 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); - } + 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); } @@ -533,3 +567,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 bf04ef7a0c0..257a760877c 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -502,9 +502,9 @@ class TreeRenderer implements IRenderer { templateData.resourceLabel.clear(); templateData.actionBar.clear(); - if ((resource || node.themeIcon) && !icon) { + if (resource || node.themeIcon) { const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - templateData.resourceLabel.setLabel({ name: label, resource: resource ? resource : URI.parse('_icon_resource') }, { fileKind: this.getFileKind(node), title, fileDecorations: fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'] }); + templateData.resourceLabel.setLabel({ name: label, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!icon, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'] }); } else { templateData.resourceLabel.setLabel({ name: label }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'] }); } 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 597a1fcc597..2bffac61f30 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -88,13 +88,16 @@ -webkit-font-smoothing: antialiased; } +.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 .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; - flex-grow: 100; } .tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row:hover .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel > .actions, diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 612b59f257f..2a49c79c496 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -496,7 +496,7 @@ export class ViewsService extends Disposable implements IViewsService { const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry); viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer)); this._register(viewContainersRegistry.onDidRegister(viewContainer => this.onDidRegisterViewContainer(viewContainer))); - this._register(Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).onDidRegister(viewlet => this.viewletService.setViewletEnablement(viewlet.id, this.storageService.getBoolean(`viewservice.${viewlet.id}.enablement`, StorageScope.GLOBAL, viewlet.id !== TEST_VIEW_CONTAINER_ID)))); + this._register(Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).onDidRegister(viewlet => this.viewletService.setViewletEnablement(viewlet.id, this.storageService.getBoolean(`viewservice.${viewlet.id}.enablement`, StorageScope.WORKSPACE, viewlet.id !== TEST_VIEW_CONTAINER_ID)))); } openView(id: string, focus: boolean): TPromise<IView> { diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 183b36edfb6..7fbaeee1186 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -377,7 +377,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView private computeInitialSizes(): { [id: string]: number } { let sizes = {}; if (this.dimension) { - let totalWeight = 0; + const totalWeight = this.viewsModel.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0); for (const viewDescriptor of this.viewsModel.visibleViewDescriptors) { sizes[viewDescriptor.id] = this.dimension.height * (viewDescriptor.weight || 20) / totalWeight; } diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index 0b86ba6a484..beaec28f4c4 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -7,6 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { mark } from 'vs/base/common/performance'; // --- Workbench Contribution Registry @@ -89,13 +90,16 @@ export class WorkbenchContributionsRegistry implements IWorkbenchContributionsRe } private doInstantiateByPhase(instantiationService: IInstantiationService, phase: LifecyclePhase): void { + mark(`LifecyclePhase/${LifecyclePhase[phase]}/createContrib:start`); const toBeInstantiated = this.toBeInstantiated.get(phase); if (toBeInstantiated) { while (toBeInstantiated.length > 0) { - instantiationService.createInstance(toBeInstantiated.shift()); + const ctor = toBeInstantiated.shift(); + instantiationService.createInstance(ctor); } } + mark(`LifecyclePhase/${LifecyclePhase[phase]}/createContrib:end`); } } -Registry.add(Extensions.Workbench, new WorkbenchContributionsRegistry()); \ No newline at end of file +Registry.add(Extensions.Workbench, new WorkbenchContributionsRegistry()); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index e49e2d26fc5..0d5e2e30903 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -25,6 +25,7 @@ export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', export const EditorGroupActiveEditorDirtyContext = new RawContextKey<boolean>('groupActiveEditorDirty', false); export const NoEditorsVisibleContext: ContextKeyExpr = EditorsVisibleContext.toNegated(); export const TextCompareEditorVisibleContext = new RawContextKey<boolean>('textCompareEditorVisible', false); +export const TextCompareEditorActiveContext = new RawContextKey<boolean>('textCompareEditorActive', false); export const ActiveEditorGroupEmptyContext = new RawContextKey<boolean>('activeEditorGroupEmpty', false); export const MultipleEditorGroupsContext = new RawContextKey<boolean>('multipleEditorGroups', false); export const SingleEditorGroupsContext = MultipleEditorGroupsContext.toNegated(); @@ -278,6 +279,11 @@ export interface IEditorInput extends IDisposable { */ getResource(): URI; + /** + * Unique type identifier for this inpput. + */ + getTypeId(): string; + /** * Returns the display name of this input. */ diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index ee49dbe7211..cdd5ab7c14d 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 { ILabelService } from 'vs/platform/label/common/label'; /** * 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, + @ILabelService private labelService: ILabelService ) { 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.labelService.getUriLabel(resources.dirname(this.resource))); } @memoize private get mediumDescription(): string { - return labels.getPathLabel(resources.dirname(this.resource), this.environmentService, this.contextService); + return this.labelService.getUriLabel(resources.dirname(this.resource), true); } @memoize private get longDescription(): string { - return labels.getPathLabel(resources.dirname(this.resource), this.environmentService); + return this.labelService.getUriLabel(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.labelService.getUriLabel(this.resource, true); } @memoize private get longTitle(): string { - return labels.getPathLabel(this.resource, this.environmentService); + return this.labelService.getUriLabel(this.resource); } getTitle(verbosity: Verbosity): string { diff --git a/src/vs/workbench/common/extensionHostProtocol.ts b/src/vs/workbench/common/extensionHostProtocol.ts new file mode 100644 index 00000000000..cd40f3f0ec5 --- /dev/null +++ b/src/vs/workbench/common/extensionHostProtocol.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'; + +export enum MessageType { + Initialized, + Ready, + Terminate +} + +export function createMessageOfType(type: MessageType): Buffer { + const result = Buffer.allocUnsafe(1); + + switch (type) { + case MessageType.Initialized: result.writeUInt8(1, 0); break; + case MessageType.Ready: result.writeUInt8(2, 0); break; + case MessageType.Terminate: result.writeUInt8(3, 0); break; + } + + return result; +} + +export function isMessageOfType(message: Buffer, type: MessageType): boolean { + if (message.length !== 1) { + return false; + } + + switch (message.readUInt8(0)) { + case 1: return type === MessageType.Initialized; + case 2: return type === MessageType.Ready; + case 3: return type === MessageType.Terminate; + default: return false; + } +} \ No newline at end of file diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index 1801c5ad38c..a044403632b 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -10,8 +10,10 @@ 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'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; -export class ResourceContextKey implements IContextKey<URI> { +export class ResourceContextKey extends Disposable implements IContextKey<URI> { static Scheme = new RawContextKey<string>('resourceScheme', undefined); static Filename = new RawContextKey<string>('resourceFilename', undefined); @@ -19,7 +21,8 @@ export class ResourceContextKey implements IContextKey<URI> { static Resource = new RawContextKey<URI>('resource', undefined); static Extension = new RawContextKey<string>('resourceExtname', undefined); static HasResource = new RawContextKey<boolean>('resourceSet', false); - static IsFile = new RawContextKey<boolean>('resourceIsFile', false); + static IsFileSystemResource = new RawContextKey<boolean>('isFileSystemResource', false); + static IsFileSystemResourceOrUntitled = new RawContextKey<boolean>('isFileSystemResourceOrUntitled', false); private _resourceKey: IContextKey<URI>; private _schemeKey: IContextKey<string>; @@ -27,20 +30,30 @@ export class ResourceContextKey implements IContextKey<URI> { private _langIdKey: IContextKey<string>; private _extensionKey: IContextKey<string>; private _hasResource: IContextKey<boolean>; - private _isFile: IContextKey<boolean>; + private _isfileSystemResource: IContextKey<boolean>; + private _isFileSystemResourceOrUntitled: IContextKey<boolean>; constructor( @IContextKeyService contextKeyService: IContextKeyService, - @IModeService private readonly _modeService: IModeService, - @IFileService private readonly _fileService: IFileService + @IFileService private readonly _fileService: IFileService, + @IModeService private readonly _modeService: IModeService ) { + super(); + this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService); this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService); this._langIdKey = ResourceContextKey.LangId.bindTo(contextKeyService); this._resourceKey = ResourceContextKey.Resource.bindTo(contextKeyService); this._extensionKey = ResourceContextKey.Extension.bindTo(contextKeyService); this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService); - this._isFile = ResourceContextKey.IsFile.bindTo(contextKeyService); + this._isfileSystemResource = ResourceContextKey.IsFileSystemResource.bindTo(contextKeyService); + this._isFileSystemResourceOrUntitled = ResourceContextKey.IsFileSystemResourceOrUntitled.bindTo(contextKeyService); + + this._register(_fileService.onDidChangeFileSystemProviderRegistrations(() => { + const resource = this._resourceKey.get(); + this._isfileSystemResource.set(resource && _fileService.canHandleResource(resource)); + this._isFileSystemResourceOrUntitled.set(this._isfileSystemResource.get() || this._schemeKey.get() === Schemas.untitled); + })); } set(value: URI) { @@ -50,7 +63,8 @@ export class ResourceContextKey implements IContextKey<URI> { 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)); + this._isfileSystemResource.set(value && this._fileService.canHandleResource(value)); + this._isFileSystemResourceOrUntitled.set(this._isfileSystemResource.get() || this._schemeKey.get() === Schemas.untitled); } reset(): void { @@ -60,7 +74,6 @@ export class ResourceContextKey implements IContextKey<URI> { 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 09dda42e441..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.")); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index b1049fab080..b0723b21310 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -42,7 +42,7 @@ export interface IViewContainersRegistry { * * @returns the registered ViewContainer. */ - registerViewContainer(id: string): ViewContainer; + registerViewContainer(id: string, extensionId?: string): ViewContainer; /** * Returns the view container with given id. @@ -54,7 +54,7 @@ export interface IViewContainersRegistry { } export class ViewContainer { - protected constructor(readonly id: string) { } + protected constructor(readonly id: string, readonly extensionId: string) { } } class ViewContainersRegistryImpl implements IViewContainersRegistry { @@ -68,11 +68,11 @@ class ViewContainersRegistryImpl implements IViewContainersRegistry { return values(this.viewContainers); } - registerViewContainer(id: string): ViewContainer { + registerViewContainer(id: string, extensionId: string): ViewContainer { if (!this.viewContainers.has(id)) { const viewContainer = new class extends ViewContainer { constructor() { - super(id); + super(id, extensionId); } }; this.viewContainers.set(id, viewContainer); diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 2a4764e9943..64f774f9f35 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -13,34 +13,24 @@ import { Action } 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'; -import pkg from 'vs/platform/node/package'; import * as errors from 'vs/base/common/errors'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/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'; -import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; -import { IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; -import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/common/timerService'; import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/group/common/editorGroupsService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts, Position as PartPosition } from 'vs/workbench/services/part/common/partService'; 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, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, 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'; -import { getEntries } from 'vs/base/common/performance'; import { IssueType } from 'vs/platform/issue/common/issue'; import { domEvent } from 'vs/base/browser/event'; import { once } from 'vs/base/common/event'; @@ -50,6 +40,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 { ILabelService } from 'vs/platform/label/common/label'; +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, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; +import { getIconClasses } from 'vs/workbench/browser/labels'; // --- actions @@ -262,280 +258,6 @@ export class ZoomResetAction extends BaseZoomAction { } } -/* Copied from loader.ts */ -enum LoaderEventType { - LoaderAvailable = 1, - - BeginLoadingScript = 10, - EndLoadingScriptOK = 11, - EndLoadingScriptError = 12, - - BeginInvokeFactory = 21, - EndInvokeFactory = 22, - - NodeBeginEvaluatingScript = 31, - NodeEndEvaluatingScript = 32, - - NodeBeginNativeRequire = 33, - NodeEndNativeRequire = 34 -} - -interface ILoaderEvent { - type: LoaderEventType; - timestamp: number; - detail: string; -} - -export class ShowStartupPerformance extends Action { - - static readonly ID = 'workbench.action.appPerf'; - static readonly LABEL = nls.localize('appPerf', "Startup Performance"); - - constructor( - id: string, - label: string, - @IWindowService private windowService: IWindowService, - @ITimerService private timerService: ITimerService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IExtensionService private extensionService: IExtensionService - ) { - super(id, label); - } - - run(): TPromise<boolean> { - - // Show dev tools - this.windowService.openDevTools(); - - // Print to console - setTimeout(() => { - (<any>console).group('Startup Performance Measurement'); - const metrics: IStartupMetrics = this.timerService.startupMetrics; - console.log(`OS: ${metrics.platform} (${metrics.release})`); - console.log(`CPUs: ${metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed})`); - console.log(`Memory (System): ${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)}GB (${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)`); - console.log(`Memory (Process): ${(metrics.meminfo.workingSetSize / 1024).toFixed(2)}MB working set (${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)`); - console.log(`VM (likelyhood): ${metrics.isVMLikelyhood}%`); - console.log(`Initial Startup: ${metrics.initialStartup}`); - console.log(`Screen Reader Active: ${metrics.hasAccessibilitySupport}`); - console.log(`Empty Workspace: ${metrics.emptyWorkbench}`); - - let nodeModuleLoadTime: number; - if (this.environmentService.performance) { - const nodeModuleTimes = this.analyzeNodeModulesLoadTimes(); - nodeModuleLoadTime = nodeModuleTimes.duration; - } - - (<any>console).table(this.getStartupMetricsTable(nodeModuleLoadTime)); - - if (this.environmentService.performance) { - const data = this.analyzeLoaderStats(); - for (let type in data) { - (<any>console).groupCollapsed(`Loader: ${type}`); - (<any>console).table(data[type]); - (<any>console).groupEnd(); - } - } - - (<any>console).groupEnd(); - - (<any>console).group('Extension Activation Stats'); - let extensionsActivationTimes: { [id: string]: ActivationTimes; } = {}; - let extensionsStatus = this.extensionService.getExtensionsStatus(); - for (let id in extensionsStatus) { - const status = extensionsStatus[id]; - if (status.activationTimes) { - extensionsActivationTimes[id] = status.activationTimes; - } - } - (<any>console).table(extensionsActivationTimes); - (<any>console).groupEnd(); - - (<any>console).group('Raw Startup Timers (CSV)'); - let value = `Name\tStart\n`; - let entries = getEntries('mark').slice(0).sort((a, b) => a.startTime - b.startTime); - for (const entry of entries) { - value += `${entry.name}\t${entry.startTime}\n`; - } - console.log(value); - (<any>console).groupEnd(); - }, 1000); - - return TPromise.as(true); - } - - private getStartupMetricsTable(nodeModuleLoadTime?: number): any[] { - const table: any[] = []; - const metrics: IStartupMetrics = this.timerService.startupMetrics; - - if (metrics.initialStartup) { - table.push({ Topic: '[main] start => app.isReady', 'Took (ms)': metrics.timers.ellapsedAppReady }); - table.push({ Topic: '[main] nls:start => nls:end', 'Took (ms)': metrics.timers.ellapsedNlsGeneration }); - table.push({ Topic: '[main] app.isReady => window.loadUrl()', 'Took (ms)': metrics.timers.ellapsedWindowLoad }); - } - - table.push({ Topic: '[renderer] window.loadUrl() => begin to require(workbench.main.js)', 'Took (ms)': metrics.timers.ellapsedWindowLoadToRequire }); - table.push({ Topic: '[renderer] require(workbench.main.js)', 'Took (ms)': metrics.timers.ellapsedRequire }); - - if (nodeModuleLoadTime) { - table.push({ Topic: '[renderer] -> of which require() node_modules', 'Took (ms)': nodeModuleLoadTime }); - } - - table.push({ Topic: '[renderer] create extension host => extensions onReady()', 'Took (ms)': metrics.timers.ellapsedExtensions }); - table.push({ Topic: '[renderer] restore viewlet', 'Took (ms)': metrics.timers.ellapsedViewletRestore }); - table.push({ Topic: '[renderer] restore editor view state', 'Took (ms)': metrics.timers.ellapsedEditorRestore }); - table.push({ Topic: '[renderer] overall workbench load', 'Took (ms)': metrics.timers.ellapsedWorkbench }); - table.push({ Topic: '------------------------------------------------------' }); - table.push({ Topic: '[main, renderer] start => extensions ready', 'Took (ms)': metrics.timers.ellapsedExtensionsReady }); - table.push({ Topic: '[main, renderer] start => workbench ready', 'Took (ms)': metrics.ellapsed }); - - return table; - } - - private analyzeNodeModulesLoadTimes(): { table: any[], duration: number } { - const stats = <ILoaderEvent[]>(<any>require).getStats(); - const result = []; - - let total = 0; - - for (let i = 0, len = stats.length; i < len; i++) { - if (stats[i].type === LoaderEventType.NodeEndNativeRequire) { - if (stats[i - 1].type === LoaderEventType.NodeBeginNativeRequire && stats[i - 1].detail === stats[i].detail) { - const entry: any = {}; - const dur = (stats[i].timestamp - stats[i - 1].timestamp); - entry['Event'] = 'nodeRequire ' + stats[i].detail; - entry['Took (ms)'] = dur.toFixed(2); - total += dur; - entry['Start (ms)'] = '**' + stats[i - 1].timestamp.toFixed(2); - entry['End (ms)'] = '**' + stats[i - 1].timestamp.toFixed(2); - result.push(entry); - } - } - } - - if (total > 0) { - result.push({ Event: '------------------------------------------------------' }); - - const entry: any = {}; - entry['Event'] = '[renderer] total require() node_modules'; - entry['Took (ms)'] = total.toFixed(2); - entry['Start (ms)'] = '**'; - entry['End (ms)'] = '**'; - result.push(entry); - } - - return { table: result, duration: Math.round(total) }; - } - - private analyzeLoaderStats(): { [type: string]: any[] } { - const stats = <ILoaderEvent[]>(<any>require).getStats().slice(0).sort((a: ILoaderEvent, b: ILoaderEvent) => { - if (a.detail < b.detail) { - return -1; - } else if (a.detail > b.detail) { - return 1; - } else if (a.type < b.type) { - return -1; - } else if (a.type > b.type) { - return 1; - } else { - return 0; - } - }); - - class Tick { - - readonly duration: number; - readonly detail: string; - - constructor(private readonly start: ILoaderEvent, private readonly end: ILoaderEvent) { - console.assert(start.detail === end.detail); - - this.duration = this.end.timestamp - this.start.timestamp; - this.detail = start.detail; - } - - toTableObject() { - return { - ['Path']: this.start.detail, - ['Took (ms)']: this.duration.toFixed(2), - // ['Start (ms)']: this.start.timestamp, - // ['End (ms)']: this.end.timestamp - }; - } - - static compareUsingStartTimestamp(a: Tick, b: Tick): number { - if (a.start.timestamp < b.start.timestamp) { - return -1; - } else if (a.start.timestamp > b.start.timestamp) { - return 1; - } else { - return 0; - } - } - } - - const ticks: { [type: number]: Tick[] } = { - [LoaderEventType.BeginLoadingScript]: [], - [LoaderEventType.BeginInvokeFactory]: [], - [LoaderEventType.NodeBeginEvaluatingScript]: [], - [LoaderEventType.NodeBeginNativeRequire]: [], - }; - - for (let i = 1; i < stats.length - 1; i++) { - const stat = stats[i]; - const nextStat = stats[i + 1]; - - if (nextStat.type - stat.type > 2) { - //bad?! - break; - } - - i += 1; - ticks[stat.type].push(new Tick(stat, nextStat)); - } - - ticks[LoaderEventType.BeginInvokeFactory].sort(Tick.compareUsingStartTimestamp); - ticks[LoaderEventType.BeginInvokeFactory].sort(Tick.compareUsingStartTimestamp); - ticks[LoaderEventType.NodeBeginEvaluatingScript].sort(Tick.compareUsingStartTimestamp); - ticks[LoaderEventType.NodeBeginNativeRequire].sort(Tick.compareUsingStartTimestamp); - - const ret = { - 'Load Script': ticks[LoaderEventType.BeginLoadingScript].map(t => t.toTableObject()), - '(Node) Load Script': ticks[LoaderEventType.NodeBeginNativeRequire].map(t => t.toTableObject()), - 'Eval Script': ticks[LoaderEventType.BeginInvokeFactory].map(t => t.toTableObject()), - '(Node) Eval Script': ticks[LoaderEventType.NodeBeginEvaluatingScript].map(t => t.toTableObject()), - }; - - function total(ticks: Tick[]): number { - let sum = 0; - for (const tick of ticks) { - sum += tick.duration; - } - return sum; - } - - // totals - ret['Load Script'].push({ - ['Path']: 'TOTAL TIME', - ['Took (ms)']: total(ticks[LoaderEventType.BeginLoadingScript]).toFixed(2) - }); - ret['Eval Script'].push({ - ['Path']: 'TOTAL TIME', - ['Took (ms)']: total(ticks[LoaderEventType.BeginInvokeFactory]).toFixed(2) - }); - ret['(Node) Load Script'].push({ - ['Path']: 'TOTAL TIME', - ['Took (ms)']: total(ticks[LoaderEventType.NodeBeginNativeRequire]).toFixed(2) - }); - ret['(Node) Eval Script'].push({ - ['Path']: 'TOTAL TIME', - ['Took (ms)']: total(ticks[LoaderEventType.NodeBeginEvaluatingScript]).toFixed(2) - }); - - return ret; - } -} - export class ReloadWindowAction extends Action { static readonly ID = 'workbench.action.reloadWindow'; @@ -573,20 +295,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 +322,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.folderUri ? win.folderUri : win.workspace ? URI.file(win.workspace.configPath) : void 0, - fileKind: win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? 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<boolean> { - 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 +365,12 @@ export class SwitchWindow extends BaseSwitchWindow { label: string, @IWindowsService windowsService: IWindowsService, @IWindowService windowService: IWindowService, - @IQuickOpenService quickOpenService: IQuickOpenService, + @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, ) { - super(id, label, windowsService, windowService, quickOpenService, keybindingService, instantiationService); + super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); } protected isQuickNavigate(): boolean { @@ -682,11 +388,12 @@ export class QuickSwitchWindow extends BaseSwitchWindow { label: string, @IWindowsService windowsService: IWindowsService, @IWindowService windowService: IWindowService, - @IQuickOpenService quickOpenService: IQuickOpenService, + @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, ) { - super(id, label, windowsService, windowService, quickOpenService, keybindingService, instantiationService); + super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); } protected isQuickNavigate(): boolean { @@ -697,21 +404,26 @@ export class QuickSwitchWindow extends BaseSwitchWindow { export const inRecentFilesPickerContextKey = 'inRecentFilesPicker'; export abstract class BaseOpenRecentAction extends Action { - private removeAction: RemoveFromRecentlyOpened; + + private removeFromRecentlyOpened: IQuickInputButton = { + iconClass: 'action-remove-from-recently-opened', + tooltip: nls.localize('remove', "Remove from Recently Opened") + }; constructor( id: string, label: string, private windowService: IWindowService, - private quickOpenService: IQuickOpenService, + private windowsService: IWindowsService, + private quickInputService: IQuickInputService, private contextService: IWorkspaceContextService, private environmentService: IEnvironmentService, + private labelService: ILabelService, private keybindingService: IKeybindingService, - instantiationService: IInstantiationService + private modelService: IModelService, + private modeService: IModeService, ) { super(id, label); - - this.removeAction = instantiationService.createInstance(RemoveFromRecentlyOpened); } protected abstract isQuickNavigate(): boolean; @@ -721,89 +433,72 @@ export abstract class BaseOpenRecentAction extends Action { .then(({ workspaces, files }) => this.openRecent(workspaces, files)); } - private openRecent(recentWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], recentFiles: string[]): void { + private openRecent(recentWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], recentFiles: URI[]): void { - function toPick(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, separator: ISeparator, fileKind: FileKind, environmentService: IEnvironmentService, removeAction?: RemoveFromRecentlyOpened): IFilePickOpenEntry { + const toPick = (workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, fileKind: FileKind, environmentService: IEnvironmentService, labelService: ILabelService, buttons: IQuickInputButton[]) => { let resource: URI; let label: string; let description: string; - if (isSingleFolderWorkspaceIdentifier(workspace)) { + if (isSingleFolderWorkspaceIdentifier(workspace) && fileKind !== FileKind.FILE) { resource = workspace; - label = getWorkspaceLabel(workspace, environmentService); - description = getPathLabel(resource.with({ path: paths.dirname(resource.path) }), environmentService); + label = labelService.getWorkspaceLabel(workspace); + description = labelService.getUriLabel(dirname(resource)); } else if (isWorkspaceIdentifier(workspace)) { resource = URI.file(workspace.configPath); - label = getWorkspaceLabel(workspace, environmentService); - description = getPathLabel(paths.dirname(workspace.configPath), environmentService); + label = labelService.getWorkspaceLabel(workspace); + description = labelService.getUriLabel(dirname(resource)); } else { - resource = URI.file(workspace); + resource = workspace; label = getBaseLabel(workspace); - description = getPathLabel(paths.dirname(workspace), environmentService); + description = labelService.getUriLabel(dirname(resource)); } return { - resource, - fileKind, + iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), label, description, - separator, - run: context => { - 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(resource, fileKind === FileKind.FILE, context); - }); - }, - action: removeAction + buttons, + workspace, + resource, + fileKind, }; - } - - const runPick = (resource: URI, isFile: boolean, context: IEntryRunContext) => { - const forceNewWindow = context.keymods.ctrlCmd; - 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 runPick = (resource: URI, isFile: boolean, keyMods: IKeyMods) => { + const forceNewWindow = keyMods.ctrlCmd; + return this.windowService.openWindow([resource], { forceNewWindow, forceOpenWorkspaceAsFile: isFile }); + }; + + const workspacePicks = recentWorkspaces.map(workspace => toPick(workspace, isSingleFolderWorkspaceIdentifier(workspace) ? FileKind.FOLDER : FileKind.ROOT_FOLDER, this.environmentService, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : void 0)); + const filePicks = recentFiles.map(p => toPick(p, FileKind.FILE, this.environmentService, this.labelService, !this.isQuickNavigate() ? [this.removeFromRecentlyOpened] : void 0)); // focus second entry if the first recent workspace is the current workspace let autoFocusSecondEntry: boolean = recentWorkspaces[0] && this.contextService.isCurrentWorkspace(recentWorkspaces[0]); - this.quickOpenService.pick([...workspacePicks, ...filePicks], { + let keyMods: IKeyMods; + const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('workspaces', "workspaces") }; + const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") }; + const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks]; + this.quickInputService.pick(picks, { contextKey: inRecentFilesPickerContextKey, - autoFocus: { autoFocusFirstEntry: !autoFocusSecondEntry, autoFocusSecondEntry: autoFocusSecondEntry }, + activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0], placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to open in new window)"), matchOnDescription: true, - 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 { - - static readonly ID = 'workbench.action.removeFromRecentlyOpened'; - static readonly LABEL = nls.localize('remove', "Remove from Recently Opened"); - - constructor( - @IWindowsService private windowsService: IWindowsService - ) { - super(RemoveFromRecentlyOpened.ID, RemoveFromRecentlyOpened.LABEL); - - this.class = 'action-remove-from-recently-opened'; - } - - run(item: IPickOpenItem): TPromise<boolean> { - return this.windowsService.removeFromRecentlyOpened([item.getResource().fsPath]).then(() => { - item.remove(); - - return true; - }); + onKeyMods: mods => keyMods = mods, + quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : void 0, + onDidTriggerItemButton: context => { + this.windowsService.removeFromRecentlyOpened([context.item.workspace]).then(() => { + context.removeItem(); + }).then(null, errors.onUnexpectedError); + } + }) + .then(pick => { + if (pick) { + return runPick(pick.resource, pick.fileKind === FileKind.FILE, keyMods); + } + return null; + }) + .done(null, errors.onUnexpectedError); } } @@ -816,13 +511,16 @@ export class OpenRecentAction extends BaseOpenRecentAction { id: string, label: string, @IWindowService windowService: IWindowService, - @IQuickOpenService quickOpenService: IQuickOpenService, + @IWindowsService windowsService: IWindowsService, + @IQuickInputService quickInputService: IQuickInputService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IEnvironmentService environmentService: IEnvironmentService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, + @ILabelService labelService: ILabelService ) { - super(id, label, windowService, quickOpenService, contextService, environmentService, keybindingService, instantiationService); + super(id, label, windowService, windowsService, quickInputService, contextService, environmentService, labelService, keybindingService, modelService, modeService); } protected isQuickNavigate(): boolean { @@ -839,13 +537,16 @@ export class QuickOpenRecentAction extends BaseOpenRecentAction { id: string, label: string, @IWindowService windowService: IWindowService, - @IQuickOpenService quickOpenService: IQuickOpenService, + @IWindowsService windowsService: IWindowsService, + @IQuickInputService quickInputService: IQuickInputService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IEnvironmentService environmentService: IEnvironmentService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, + @ILabelService labelService: ILabelService ) { - super(id, label, windowService, quickOpenService, contextService, environmentService, keybindingService, instantiationService); + super(id, label, windowService, windowsService, quickInputService, contextService, environmentService, labelService, keybindingService, modelService, modeService); } protected isQuickNavigate(): boolean { @@ -908,126 +609,6 @@ export class ReportPerformanceIssueUsingReporterAction extends Action { } } -// NOTE: This is still used when running --prof-startup, which already opens a dialog, so the reporter is not used. -export class ReportPerformanceIssueAction extends Action { - - static readonly ID = 'workbench.action.reportPerformanceIssue'; - static readonly LABEL = nls.localize('reportPerformanceIssue', "Report Performance Issue"); - - constructor( - id: string, - label: string, - @IIntegrityService private integrityService: IIntegrityService, - @IEnvironmentService private environmentService: IEnvironmentService, - @ITimerService private timerService: ITimerService - ) { - super(id, label); - } - - run(appendix?: string): TPromise<boolean> { - this.integrityService.isPure().then(res => { - const issueUrl = this.generatePerformanceIssueUrl(product.reportIssueUrl, pkg.name, pkg.version, product.commit, product.date, res.isPure, appendix); - - window.open(issueUrl); - }); - - return TPromise.wrap(true); - } - - private generatePerformanceIssueUrl(baseUrl: string, name: string, version: string, commit: string, date: string, isPure: boolean, appendix?: string): string { - - if (!appendix) { - appendix = `Additional Steps to Reproduce (if any): - -1. -2.`; - } - - let nodeModuleLoadTime: number; - if (this.environmentService.performance) { - nodeModuleLoadTime = this.computeNodeModulesLoadTime(); - } - - const metrics: IStartupMetrics = this.timerService.startupMetrics; - - const osVersion = `${os.type()} ${os.arch()} ${os.release()}`; - const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; - const body = encodeURIComponent( - `- VSCode Version: <code>${name} ${version}${isPure ? '' : ' **[Unsupported]**'} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})</code> -- OS Version: <code>${osVersion}</code> -- CPUs: <code>${metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed})</code> -- Memory (System): <code>${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)}GB (${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)</code> -- Memory (Process): <code>${(metrics.meminfo.workingSetSize / 1024).toFixed(2)}MB working set (${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)</code> -- Load (avg): <code>${metrics.loadavg.map(l => Math.round(l)).join(', ')}</code> -- VM: <code>${metrics.isVMLikelyhood}%</code> -- Initial Startup: <code>${metrics.initialStartup ? 'yes' : 'no'}</code> -- Screen Reader: <code>${metrics.hasAccessibilitySupport ? 'yes' : 'no'}</code> -- Empty Workspace: <code>${metrics.emptyWorkbench ? 'yes' : 'no'}</code> -- Timings: - -${this.generatePerformanceTable(nodeModuleLoadTime)} - ---- - -${appendix}` - ); - - return `${baseUrl}${queryStringPrefix}body=${body}`; - } - - private computeNodeModulesLoadTime(): number { - const stats = <ILoaderEvent[]>(<any>require).getStats(); - let total = 0; - - for (let i = 0, len = stats.length; i < len; i++) { - if (stats[i].type === LoaderEventType.NodeEndNativeRequire) { - if (stats[i - 1].type === LoaderEventType.NodeBeginNativeRequire && stats[i - 1].detail === stats[i].detail) { - const dur = (stats[i].timestamp - stats[i - 1].timestamp); - total += dur; - } - } - } - - return Math.round(total); - } - - private generatePerformanceTable(nodeModuleLoadTime?: number): string { - let tableHeader = `|Component|Task|Time (ms)| -|---|---|---|`; - - const table = this.getStartupMetricsTable(nodeModuleLoadTime).map(e => { - return `|${e.component}|${e.task}|${e.time}|`; - }).join('\n'); - - return `${tableHeader}\n${table}`; - } - - private getStartupMetricsTable(nodeModuleLoadTime?: number): { component: string, task: string; time: number; }[] { - const table: any[] = []; - const metrics: IStartupMetrics = this.timerService.startupMetrics; - - if (metrics.initialStartup) { - table.push({ component: 'main', task: 'start => app.isReady', time: metrics.timers.ellapsedAppReady }); - table.push({ component: 'main', task: 'app.isReady => window.loadUrl()', time: metrics.timers.ellapsedWindowLoad }); - } - - table.push({ component: 'renderer', task: 'window.loadUrl() => begin to require(workbench.main.js)', time: metrics.timers.ellapsedWindowLoadToRequire }); - table.push({ component: 'renderer', task: 'require(workbench.main.js)', time: metrics.timers.ellapsedRequire }); - - if (nodeModuleLoadTime) { - table.push({ component: 'renderer', task: '-> of which require() node_modules', time: nodeModuleLoadTime }); - } - - table.push({ component: 'renderer', task: 'create extension host => extensions onReady()', time: metrics.timers.ellapsedExtensions }); - table.push({ component: 'renderer', task: 'restore viewlet', time: metrics.timers.ellapsedViewletRestore }); - table.push({ component: 'renderer', task: 'restore editor view state', time: metrics.timers.ellapsedEditorRestore }); - table.push({ component: 'renderer', task: 'overall workbench load', time: metrics.timers.ellapsedWorkbench }); - table.push({ component: 'main + renderer', task: 'start => extensions ready', time: metrics.timers.ellapsedExtensionsReady }); - table.push({ component: 'main + renderer', task: 'start => workbench ready', time: metrics.ellapsed }); - - return table; - } -} export class KeybindingsReferenceAction extends Action { @@ -1458,6 +1039,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<boolean> { + return this.windowsService.newWindowTab().then(() => true); + } +} + export class ShowPreviousWindowTab extends Action { static readonly ID = 'workbench.action.showPreviousWindowTab'; @@ -1644,25 +1243,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<void> { - 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.html b/src/vs/workbench/electron-browser/bootstrap/index.html index c14ebbe8653..31e970da6c4 100644 --- a/src/vs/workbench/electron-browser/bootstrap/index.html +++ b/src/vs/workbench/electron-browser/bootstrap/index.html @@ -6,7 +6,6 @@ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;"> </head> <body class="monaco-shell vs-dark" aria-label=""> - <script src="preload.js"></script> </body> <!-- Startup via index.js --> diff --git a/src/vs/workbench/electron-browser/bootstrap/index.js b/src/vs/workbench/electron-browser/bootstrap/index.js index 51700d6f0b3..4d16cd36a71 100644 --- a/src/vs/workbench/electron-browser/bootstrap/index.js +++ b/src/vs/workbench/electron-browser/bootstrap/index.js @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// Warning: Do not use the `let` declarator in this file, it breaks our minification - 'use strict'; /*global window,document,define*/ @@ -15,27 +13,28 @@ perf.mark('renderer/started'); const path = require('path'); const fs = require('fs'); const electron = require('electron'); -const remote = electron.remote; const ipc = electron.ipcRenderer; +Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) + process.lazyEnv = new Promise(function (resolve) { const handle = setTimeout(function () { resolve(); console.warn('renderer did not receive lazyEnv in time'); }, 10000); + ipc.once('vscode:acceptShellEnv', function (event, shellEnv) { clearTimeout(handle); assign(process.env, shellEnv); resolve(process.env); }); + ipc.send('vscode:fetchShellEnv'); }); -Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) - function onError(error, enableDeveloperTools) { if (enableDeveloperTools) { - remote.getCurrentWebContents().openDevTools(); + ipc.send('vscode:openDevTools'); } console.error('[uncaught exception]: ' + error); @@ -46,8 +45,7 @@ function onError(error, enableDeveloperTools) { } function assign(destination, source) { - return Object.keys(source) - .reduce(function (r, key) { r[key] = source[key]; return r; }, destination); + return Object.keys(source).reduce(function (r, key) { r[key] = source[key]; return r; }, destination); } function parseURLQueryArgs() { @@ -66,7 +64,7 @@ function uriFromPath(_path) { pathName = '/' + pathName; } - return encodeURI('file://' + pathName); + return encodeURI('file://' + pathName).replace(/#/g, '%23'); } function readFile(file) { @@ -81,38 +79,72 @@ function readFile(file) { }); } -function showPartsSplash(configuration) { +function writeFile(file, content) { + return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); +} - let key; - let keep = false; - // this is the logic of StorageService#getWorkspaceKey and StorageService#toStorageKey - if (configuration.folderUri) { - let workspaceKey = require('vscode-uri').default.revive(configuration.folderUri).toString().replace('file:///', '').replace(/^\//, ''); - key = `storage://workspace/${workspaceKey}/parts-splash`; - } else if (configuration.workspace) { - key = `storage://workspace/root:${configuration.workspace.id}/parts-splash`; - } else { - key = `storage://global/parts-splash`; - keep = true; - } +function showPartsSplash(configuration) { + perf.mark('willShowPartsSplash'); // TODO@Ben remove me after a while perf.mark('willAccessLocalStorage'); let storage = window.localStorage; perf.mark('didAccessLocalStorage'); - let structure = storage.getItem(key); - if (structure) { - let splash = document.createElement('div'); - splash.innerHTML = structure; - document.body.appendChild(splash); + let data; + try { + let raw = storage.getItem('storage://global/parts-splash-data'); + data = JSON.parse(raw); + } catch (e) { + // ignore } - if (!keep) { - storage.removeItem(key); - } -} -const writeFile = (file, content) => new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); + // high contrast mode has been turned on, ignore stored colors and layouts + if (data && configuration.highContrast && data.baseTheme !== 'hc-black') { + data = void 0; + } + + const style = document.createElement('style'); + document.head.appendChild(style); + + if (data) { + const { layoutInfo, colorInfo, baseTheme } = data; + + // set the theme base id used by images and some styles + document.body.className = `monaco-shell ${baseTheme}`; + // stylesheet that defines foreground and background color + style.innerHTML = `.monaco-shell { background-color: ${colorInfo.editorBackground}; color: ${colorInfo.foreground}; }`; + + const splash = document.createElement('div'); + splash.id = data.id; + + // 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 = ` + <div style="position: absolute; width: 100%; left: 0; top: 0; height: ${layoutInfo.titleBarHeight}px; background-color: ${colorInfo.titleBarBackground};"></div> + <div style="position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: 0; width: ${layoutInfo.activityBarWidth}px; background-color: ${colorInfo.activityBarBackground};"></div> + <div style="position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: ${layoutInfo.activityBarWidth}px; width: ${layoutInfo.sideBarWidth}px; background-color: ${colorInfo.sideBarBackground};"></div> + <div style="position: absolute; width: 100%; bottom: 0; left: 0; height: ${layoutInfo.statusBarHeight}px; background-color: ${colorInfo.statusBarBackground};"></div> + `; + } else { + // empty -> speical status bar color, no sidebar + splash.innerHTML = ` + <div style="position: absolute; width: 100%; left: 0; top: 0; height: ${layoutInfo.titleBarHeight}px; background-color: ${colorInfo.titleBarBackground};"></div> + <div style="position: absolute; height: calc(100% - ${layoutInfo.titleBarHeight}px); top: ${layoutInfo.titleBarHeight}px; ${layoutInfo.sideBarSide}: 0; width: ${layoutInfo.activityBarWidth}px; background-color: ${colorInfo.activityBarBackground};"></div> + <div style="position: absolute; width: 100%; bottom: 0; left: 0; height: ${layoutInfo.statusBarHeight}px; background-color: ${colorInfo.statusBarNoFolderBackground};"></div> + `; + } + document.body.appendChild(splash); + } else { + document.body.className = `monaco-shell ${configuration.highContrast ? 'hc-black' : 'vs-dark'}`; + style.innerHTML = `.monaco-shell { background-color: ${configuration.highContrast ? '#000000' : '#1E1E1E'}; color: ${configuration.highContrast ? '#FFFFFF' : '#CCCCCC'}; }`; + } + + perf.mark('didShowPartsSplash'); +} function registerListeners(enableDeveloperTools) { @@ -135,9 +167,9 @@ function registerListeners(enableDeveloperTools) { listener = function (e) { const key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB) { - remote.getCurrentWebContents().toggleDevTools(); + ipc.send('vscode:toggleDevTools'); } else if (key === RELOAD_KB) { - remote.getCurrentWindow().reload(); + ipc.send('vscode:reloadWindow'); } }; window.addEventListener('keydown', listener); @@ -188,8 +220,15 @@ function main() { // Correctly inherit the parent's environment assign(process.env, configuration.userEnv); - perf.importEntries(configuration.perfEntries); + // disable pinch zoom & apply zoom level early to avoid glitches + const zoomLevel = configuration.zoomLevel; + webFrame.setVisualZoomLevelLimits(1, 1); + if (typeof zoomLevel === 'number' && zoomLevel !== 0) { + webFrame.setZoomLevel(zoomLevel); + } + + // Parts splash showPartsSplash(configuration); // Get the nls configuration into the process.env as early as possible. @@ -238,13 +277,6 @@ function main() { const enableDeveloperTools = (process.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; const unbind = registerListeners(enableDeveloperTools); - // disable pinch zoom & apply zoom level early to avoid glitches - const zoomLevel = configuration.zoomLevel; - webFrame.setVisualZoomLevelLimits(1, 1); - if (typeof zoomLevel === 'number' && zoomLevel !== 0) { - webFrame.setZoomLevel(zoomLevel); - } - // Load the loader and start loading the workbench const loaderFilename = configuration.appRoot + '/out/vs/loader.js'; const loaderSource = require('fs').readFileSync(loaderFilename); @@ -274,14 +306,6 @@ function main() { }); } - // Perf Counters - window.MonacoEnvironment.timers = { - isInitialStartup: !!configuration.isInitialStartup, - hasAccessibilitySupport: !!configuration.accessibilitySupport, - start: configuration.perfStartTime, - windowLoad: configuration.perfWindowLoadTime - }; - perf.mark('willLoadWorkbenchMain'); require([ 'vs/workbench/workbench.main', @@ -301,7 +325,6 @@ function main() { }); }); }); - } main(); diff --git a/src/vs/workbench/electron-browser/bootstrap/preload.js b/src/vs/workbench/electron-browser/bootstrap/preload.js deleted file mode 100644 index d451c2d5fb7..00000000000 --- a/src/vs/workbench/electron-browser/bootstrap/preload.js +++ /dev/null @@ -1,39 +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'; - -(function() { - function getConfig() { - const queryParams = window.location.search.substring(1).split('&'); - for (var i = 0; i < queryParams.length; i++) { - var kv = queryParams[i].split('='); - if (kv[0] === 'config' && kv[1]) { - return JSON.parse(decodeURIComponent(kv[1])); - } - } - return {}; - } - try { - const config = getConfig(); - const document = window.document; - - // sets the base theme class ('vs', 'vs-dark', 'hc-black') - const baseTheme = config.baseTheme || 'vs'; - document.body.className = 'monaco-shell ' + baseTheme; - - // adds a stylesheet with the backgrdound color - var backgroundColor = config.backgroundColor; - if (!backgroundColor) { - backgroundColor = baseTheme === 'hc-black' ? '#000000' : (baseTheme === 'vs' ? '#FFFFFF' : '#1E1E1E'); - } - const foregroundColor = baseTheme === 'hc-black' ? '#FFFFFF' : (baseTheme === 'vs' ? '#6C6C6C' : '#CCCCCC'); - const style = document.createElement('style'); - style.innerHTML = '.monaco-shell { background-color:' + backgroundColor + '; color:' + foregroundColor + '; }'; - document.head.appendChild(style); - - } catch (error) { - console.error(error); - } -})(); \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 3853ebdfab4..e7f8c93d838 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -19,7 +19,8 @@ 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'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import URI from 'vs/base/common/uri'; // --- List Commands @@ -550,7 +551,7 @@ export function registerCommands(): void { win: { primary: void 0 } }); - CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: string | ISingleFolderWorkspaceIdentifier) { + CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string) { 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 e408207987d..14cc4706bbd 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -14,7 +14,7 @@ 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, OpenRecentAction } from 'vs/workbench/electron-browser/actions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, 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'; @@ -22,6 +22,8 @@ import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browse 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'; +import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; +import { FileDialogContext, IsMacContext } from 'vs/platform/workbench/common/contextkeys'; // Contribute Commands registerCommands(); @@ -70,7 +72,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( @@ -125,7 +126,6 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { // Developer related actions const developerCategory = nls.localize('developer', "Developer"); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowStartupPerformance, ShowStartupPerformance.ID, ShowStartupPerformance.LABEL), 'Developer: Startup Performance', developerCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenProcessExplorer, OpenProcessExplorer.ID, OpenProcessExplorer.LABEL), 'Developer: Open Process Explorer', developerCategory); @@ -169,7 +169,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { id: OpenFileAction.ID, title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") }, - order: 1 + order: 1, + when: ContextKeyExpr.and(IsMacContext.toNegated(), FileDialogContext.isEqualTo('local')) }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -178,7 +179,18 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { id: OpenFolderAction.ID, title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") }, - order: 2 + order: 2, + when: ContextKeyExpr.and(IsMacContext.toNegated(), FileDialogContext.isEqualTo('local')) +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '2_open', + command: { + id: OpenFileFolderAction.ID, + title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...") + }, + order: 1, + when: ContextKeyExpr.and(IsMacContext, FileDialogContext.isEqualTo('local')) }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -187,7 +199,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { id: OpenWorkspaceAction.ID, title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") }, - order: 3 + order: 3, + when: FileDialogContext.isEqualTo('local') }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -211,10 +224,11 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '3_workspace', command: { - id: AddRootFolderAction.ID, + id: ADD_ROOT_FOLDER_COMMAND_ID, title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") }, - order: 1 + order: 1, + when: FileDialogContext.isEqualTo('local') }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -223,23 +237,37 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { id: SaveWorkspaceAsAction.ID, title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") }, - order: 2 + order: 2, + when: FileDialogContext.isEqualTo('local') }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), submenu: MenuId.MenubarPreferencesMenu, group: '5_autosave', - order: 2 + order: 2, + when: IsMacContext.toNegated() }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', command: { id: CloseWorkspaceAction.ID, - title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder") + title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), + precondition: new RawContextKey<number>('workspaceFolderCount', 0).notEqualsTo('0') }, - order: 3 + order: 3, + when: new RawContextKey<string>('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<string>('workbenchState', '').isEqualTo('workspace') }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -317,6 +345,130 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { 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 +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: 'z_about', + command: { + id: 'workbench.action.showAboutDialog', + title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About") + }, + order: 1, + when: IsMacContext.toNegated() +}); // Configuration: Workbench const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration); @@ -329,7 +481,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': { @@ -342,52 +494,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', @@ -396,7 +554,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': { @@ -412,22 +570,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 if opening keybinding settings also opens an editor showing all default keybindings."), + 'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."), 'default': true }, 'workbench.sideBar.location': { @@ -462,7 +620,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."), @@ -473,22 +631,38 @@ 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'], + 'enum': ['hide', 'filter'], + 'enumDescriptions': [ + nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching."), + nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category."), + ], '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': { - 'type': 'boolean', - 'description': nls.localize('settingsTocVisible', "Controls whether the settings editor Table of Contents is visible."), - 'default': true, + 'workbench.settings.editor': { + 'type': 'string', + 'enum': ['ui', 'json'], + 'enumDescriptions': [ + nls.localize('settings.editor.json', "Use the JSON file editor."), + nls.localize('settings.editor.ui', "Use the settings UI editor."), + ], + 'description': nls.localize('settings.editor.desc', "Determines which settings editor to use by default."), + 'default': 'ui', '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'] } } }); @@ -505,41 +679,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', @@ -552,13 +726,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', @@ -568,8 +742,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', @@ -582,12 +756,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', @@ -634,7 +808,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': { @@ -657,32 +831,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 05c7a8a054e..08edc11b168 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -24,30 +24,28 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { realpath } from 'vs/base/node/pfs'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import * as gracefulFs from 'graceful-fs'; -import { IInitData } from 'vs/workbench/services/timer/common/timerService'; -import { TimerService } from 'vs/workbench/services/timer/node/timerService'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows'; -import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; +import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { StorageService, inMemoryLocalStorageInstance, IStorage } from 'vs/platform/storage/common/storageService'; import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser'; import { webFrame } from 'electron'; -import { UpdateChannelClient } from 'vs/platform/update/common/updateIpc'; +import { UpdateChannelClient } from 'vs/platform/update/node/updateIpc'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/common/urlIpc'; +import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/node/urlIpc'; import { IURLService } from 'vs/platform/url/common/url'; -import { WorkspacesChannelClient } from 'vs/platform/workspaces/common/workspacesIpc'; +import { WorkspacesChannelClient } from 'vs/platform/workspaces/node/workspacesIpc'; 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'; -import { IssueChannelClient } from 'vs/platform/issue/common/issueIpc'; +import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc'; import { IIssueService } from 'vs/platform/issue/common/issue'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; +import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; import { RelayURLService } from 'vs/platform/url/common/urlService'; -import { MenubarChannelClient } from 'vs/platform/menubar/common/menubarIpc'; +import { MenubarChannelClient } from 'vs/platform/menubar/node/menubarIpc'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { Schemas } from 'vs/base/common/network'; @@ -55,6 +53,10 @@ gracefulFs.gracefulify(fs); // enable gracefulFs export function startup(configuration: IWindowConfiguration): TPromise<void> { + revive(configuration); + + perf.importEntries(configuration.perfEntries); + // Ensure others can listen to zoom level changes browser.setZoomFactor(webFrame.getZoomFactor()); @@ -75,6 +77,22 @@ export function startup(configuration: IWindowConfiguration): TPromise<void> { return openWorkbench(configuration); } +function revive(workbench: IWindowConfiguration) { + if (workbench.folderUri) { + workbench.folderUri = uri.revive(workbench.folderUri); + } + const filesToWaitPaths = workbench.filesToWait && workbench.filesToWait.paths; + [filesToWaitPaths, workbench.filesToOpen, workbench.filesToCreate, workbench.filesToDiff].forEach(paths => { + if (Array.isArray(paths)) { + paths.forEach(path => { + if (path.fileUri) { + path.fileUri = uri.revive(path.fileUri); + } + }); + } + }); +} + function openWorkbench(configuration: IWindowConfiguration): TPromise<void> { const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`); const mainServices = createMainProcessServices(mainProcessClient, configuration); @@ -86,7 +104,6 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise<void> { // Since the configuration service is one of the core services that is used in so many places, we initialize it // right before startup of the workbench shell to have its data ready for consumers return createAndInitializeWorkspaceService(configuration, environmentService).then(workspaceService => { - const timerService = new TimerService((<any>window).MonacoEnvironment.timers as IInitData, workspaceService.getWorkbenchState() === WorkbenchState.EMPTY); const storageService = createStorageService(workspaceService, environmentService); return domContentLoaded().then(() => { @@ -98,7 +115,6 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise<void> { configurationService: workspaceService, environmentService, logService, - timerService, storageService }, mainServices, mainProcessClient, configuration); shell.open(); @@ -116,8 +132,7 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise<void> { } function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): TPromise<WorkspaceService> { - const folderUri = configuration.folderUri ? uri.revive(configuration.folderUri) : null; - return validateFolderUri(folderUri, configuration.verbose).then(validatedFolderUri => { + return validateFolderUri(configuration.folderUri, configuration.verbose).then(validatedFolderUri => { const workspaceService = new WorkspaceService(environmentService); diff --git a/src/vs/workbench/electron-browser/media/shell.css b/src/vs/workbench/electron-browser/media/shell.css index 4911010c77c..306a4b4e6db 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: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; } -.monaco-shell:lang(zh-Hans) { 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) { 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) { 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) { 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; } +.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; } } @@ -75,16 +80,24 @@ .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), .monaco-shell .monaco-menu .monaco-action-bar.vertical .keybinding { - padding: 0 1.5em; + font-size: inherit; + padding: 0 2em; +} + +.monaco-shell .monaco-menu .monaco-action-bar.vertical .menu-item-check { + font-size: inherit; + width: 2em; } .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label.separator { + font-size: inherit; padding: 0.2em 0 0 0; margin-bottom: 0.2em; } .monaco-shell .monaco-menu .monaco-action-bar.vertical .submenu-indicator { - padding: 0 1em; + font-size: 60%; + padding: 0 1.8em; } .monaco-shell .monaco-menu .action-item { diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 24e8b1f7cc7..b6db73f5201 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -19,7 +19,7 @@ import pkg from 'vs/platform/node/package'; import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, configurationTelemetry, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { ElectronWindow } from 'vs/workbench/electron-browser/window'; @@ -41,7 +41,7 @@ import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -57,13 +57,11 @@ import { WorkbenchModeServiceImpl } from 'vs/workbench/services/mode/common/work import { IModeService } from 'vs/editor/common/services/modeService'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ICrashReporterService, NullCrashReporterService, CrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; -import { getDelayedChannel, IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { getDelayedChannel, IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { DefaultURITransformer } from 'vs/base/common/uriIpc'; -import { IExtensionManagementChannel, ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IExtensionManagementService, IExtensionEnablementService, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementChannel, ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; +import { IExtensionManagementService, IExtensionEnablementService, IExtensionManagementServerService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; -import { ITimerService } from 'vs/workbench/services/timer/common/timerService'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration'; import * as browser from 'vs/base/browser/browser'; @@ -82,7 +80,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { stat } from 'fs'; import { join } from 'path'; -import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/common/localizationsIpc'; +import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { WorkbenchIssueService } from 'vs/workbench/services/issue/electron-browser/workbenchIssueService'; @@ -90,14 +88,17 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { NotificationService } from 'vs/workbench/services/notification/common/notificationService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { DialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; -import { DialogChannel } from 'vs/platform/dialogs/common/dialogIpc'; -import { EventType, addDisposableListener, addClass, getTotalHeight, getTotalWidth } from 'vs/base/browser/dom'; +import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; +import { EventType, addDisposableListener, addClass } from 'vs/base/browser/dom'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHistoryService'; import { MulitExtensionManagementService } from 'vs/platform/extensionManagement/common/multiExtensionManagement'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; -import { Parts, Position } from 'vs/workbench/services/part/common/partService'; +import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; +import { DefaultURITransformer } from 'vs/base/common/uriIpc'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { ILabelService } from 'vs/platform/label/common/label'; /** * Services that we require for the Shell @@ -107,7 +108,6 @@ export interface ICoreServices { configurationService: IConfigurationService; environmentService: IEnvironmentService; logService: ILogService; - timerService: ITimerService; storageService: IStorageService; } @@ -118,13 +118,13 @@ export interface ICoreServices { export class WorkbenchShell extends Disposable { private storageService: IStorageService; private environmentService: IEnvironmentService; + private labelService: ILabelService; private logService: ILogService; private configurationService: IConfigurationService; private contextService: IWorkspaceContextService; private telemetryService: ITelemetryService; private extensionService: ExtensionService; private broadcastService: IBroadcastService; - private timerService: ITimerService; private themeService: WorkbenchThemeService; private lifecycleService: LifecycleService; private mainProcessServices: ServiceCollection; @@ -148,7 +148,6 @@ export class WorkbenchShell extends Disposable { this.configurationService = coreServices.configurationService; this.environmentService = coreServices.environmentService; this.logService = coreServices.logService; - this.timerService = coreServices.timerService; this.storageService = coreServices.storageService; this.mainProcessServices = mainProcessServices; @@ -189,9 +188,6 @@ export class WorkbenchShell extends Disposable { // Startup Workbench workbench.startup().done(startupInfos => { - // Remove splash screen - this._removePartsSplash(); - // Set lifecycle phase to `Runnning` so that other contributions can now do something this.lifecycleService.phase = LifecyclePhase.Running; @@ -322,9 +318,9 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IWorkspaceContextService, this.contextService); serviceCollection.set(IConfigurationService, this.configurationService); serviceCollection.set(IEnvironmentService, this.environmentService); + serviceCollection.set(ILabelService, this.labelService); serviceCollection.set(ILogService, this._register(this.logService)); - serviceCollection.set(ITimerService, this.timerService); serviceCollection.set(IStorageService, this.storageService); this.mainProcessServices.forEach((serviceIdentifier, serviceInstance) => { serviceCollection.set(serviceIdentifier, serviceInstance); @@ -344,7 +340,10 @@ export class WorkbenchShell extends Disposable { .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`)); sharedProcess - .done(client => client.registerChannel('dialog', instantiationService.createInstance(DialogChannel))); + .done(client => { + client.registerChannel('download', new DownloadServiceChannel()); + client.registerChannel('dialog', instantiationService.createInstance(DialogChannel)); + }); // Warm up font cache information before building up too many dom elements restoreFontInfo(this.storageService); @@ -385,6 +384,9 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(ILifecycleService, lifecycleService); this.lifecycleService = lifecycleService; + serviceCollection.set(IRequestService, new SyncDescriptor(RequestService)); + serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + const extensionManagementChannel = getDelayedChannel<IExtensionManagementChannel>(sharedProcess.then(c => c.getChannel('extensions'))); const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel, DefaultURITransformer); serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, extensionManagementChannelClient)); @@ -393,7 +395,6 @@ export class WorkbenchShell extends Disposable { const extensionEnablementService = this._register(instantiationService.createInstance(ExtensionEnablementService)); serviceCollection.set(IExtensionEnablementService, extensionEnablementService); - serviceCollection.set(IRequestService, new SyncDescriptor(RequestService)); this.extensionService = instantiationService.createInstance(ExtensionService); serviceCollection.set(IExtensionService, this.extensionService); @@ -512,71 +513,11 @@ export class WorkbenchShell extends Disposable { // Keep font info for next startup around saveFontInfo(this.storageService); - this._savePartsSplash(); - // Dispose Workbench if (this.workbench) { this.workbench.dispose(reason); } } - - private static readonly PARTS_SPLASH_ID = 'monaco-parts-splash'; - - private _savePartsSplash() { - - // capture html-structure - let state = this.contextService.getWorkbenchState(); - let html = `<div id="${WorkbenchShell.PARTS_SPLASH_ID}">`; - - // title part - let titleHeight: number; - { - let part = this.workbench.getContainer(Parts.TITLEBAR_PART); - let height = getTotalHeight(part); - let bg = part.style.backgroundColor || 'inhert'; - html += `<div style="position: absolute; width: 100%; left: 0; top: 0; height: ${height}px; background-color: ${bg};"></div>`; - titleHeight = height; - } - - // activitybar-part - let left = this.workbench.getSideBarPosition() === Position.LEFT; - let activityPartWidth: number; - { - let part = this.workbench.getContainer(Parts.ACTIVITYBAR_PART); - let width = getTotalWidth(part); - let bg = part.style.backgroundColor || 'inhert'; - html += `<div style="position: absolute; height: calc(100% - ${titleHeight}px); top: ${titleHeight}px; ${left ? 'left' : 'right'}: 0; width: ${width}px; background-color: ${bg};"></div>`; - activityPartWidth = width; - } - - // sidebar-part (only for folder/workspace cases) - if (state !== WorkbenchState.EMPTY) { - let part = this.workbench.getContainer(Parts.SIDEBAR_PART); - let width = getTotalWidth(part); - let bg = part.style.backgroundColor || 'inhert'; - html += `<div style="position: absolute; height: calc(100% - ${titleHeight}px); top: ${titleHeight}px; ${left ? 'left' : 'right'}: ${activityPartWidth}px; width: ${width}px; background-color: ${bg};"></div>`; - } - - // statusbar-part - { - let part = this.workbench.getContainer(Parts.STATUSBAR_PART); - let height = getTotalHeight(part); - let bg = part.style.backgroundColor || 'inhert'; - html += `<div style="position: absolute; width: 100%; bottom: 0; left: 0; height: ${height}px; background-color: ${bg};"></div>`; - } - - html += '\n</div>'; - - // store per workspace or globally - this.storageService.store('parts-splash', html, state === WorkbenchState.EMPTY ? StorageScope.GLOBAL : StorageScope.WORKSPACE); - } - - private _removePartsSplash(): void { - let element = document.getElementById(WorkbenchShell.PARTS_SPLASH_ID); - if (element) { - element.remove(); - } - } } diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index d01be333d42..b19e6c63fa8 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -19,15 +19,13 @@ import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; 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 { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IPathData } 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 { IWorkbenchThemeService, VS_HC_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'; @@ -44,6 +42,8 @@ import { AccessibilitySupport, isRootUser, isWindows, isMacintosh } from 'vs/bas import product from 'vs/platform/node/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)), @@ -149,7 +149,6 @@ export class ElectronWindow extends Themable { } 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) { @@ -204,7 +203,7 @@ export class ElectronWindow extends Themable { const windowConfig = this.configurationService.getValue<IWindowSettings>('window'); if (windowConfig && windowConfig.autoDetectHighContrast) { this.lifecycleService.when(LifecyclePhase.Running).then(() => { - this.themeService.setColorTheme(VS_DARK_THEME, null); + this.themeService.restoreColorTheme(); }); } }); @@ -383,19 +382,16 @@ export class ElectronWindow extends Themable { 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; })); }); @@ -448,7 +444,7 @@ export class ElectronWindow extends Themable { // In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. - const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.file(p.filePath)); + const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.revive(p.fileUri)); const waitMarkerFile = URI.file(request.filesToWait.waitMarkerFilePath); const unbind = this.editorService.onDidCloseEditor(() => { if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { @@ -477,9 +473,9 @@ export class ElectronWindow extends Themable { }); } - private toInputs(paths: IPath[], isNew: boolean): IResourceEditor[] { + private toInputs(paths: IPathData[], isNew: boolean): IResourceEditor[] { return paths.map(p => { - const resource = URI.file(p.filePath); + const resource = URI.revive(p.fileUri); let input: IResourceInput | IUntitledResourceInput; if (isNew) { input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 00aae6933f9..c4c00dc70ca 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'; @@ -95,7 +93,7 @@ import { IDecorationsService } from 'vs/workbench/services/decorations/browser/d import { ActivityService } from 'vs/workbench/services/activity/browser/activityService'; import URI from 'vs/base/common/uri'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; -import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys'; +import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, FileDialogContext } from 'vs/platform/workbench/common/contextkeys'; import { IViewsService } from 'vs/workbench/common/views'; import { ViewsService } from 'vs/workbench/browser/parts/views/views'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -105,7 +103,7 @@ import { NotificationsAlerts } from 'vs/workbench/browser/parts/notifications/no import { NotificationsStatus } from 'vs/workbench/browser/parts/notifications/notificationsStatus'; import { registerNotificationCommands } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts'; -import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { PreferencesService } from 'vs/workbench/services/preferences/browser/preferencesService'; @@ -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 { LabelService, ILabelService } from 'vs/platform/label/common/label'; 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; @@ -222,6 +219,7 @@ export class Workbench extends Disposable implements IPartService { private sideBarHidden: boolean; private statusBarHidden: boolean; private activityBarHidden: boolean; + private menubarToggled: boolean; private sideBarPosition: Position; private panelPosition: Position; private panelHidden: boolean; @@ -298,10 +296,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 +318,7 @@ export class Workbench extends Disposable implements IPartService { // Actions for macOS native tabs management (only when enabled) const windowConfig = this.configurationService.getValue<IWindowConfiguration>(); 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 +336,10 @@ export class Workbench extends Disposable implements IPartService { // Clipboard serviceCollection.set(IClipboardService, new ClipboardService()); + // Uri Display + const labelService = new LabelService(this.environmentService, this.contextService); + serviceCollection.set(ILabelService, labelService); + // 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); @@ -490,7 +493,7 @@ export class Workbench extends Disposable implements IPartService { // Listen to editor closing (if we run with --wait) const filesToWait = this.workbenchParams.configuration.filesToWait; if (filesToWait) { - const resourcesToWaitFor = filesToWait.paths.map(p => URI.file(p.filePath)); + const resourcesToWaitFor = filesToWait.paths.map(p => p.fileUri); const waitMarkerFile = URI.file(filesToWait.waitMarkerFilePath); const listenerDispose = this.editorService.onDidCloseEditor(() => this.onEditorClosed(listenerDispose, resourcesToWaitFor, waitMarkerFile)); @@ -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(); } @@ -528,6 +532,20 @@ export class Workbench extends Disposable implements IPartService { } } + private onMenubarToggled(visible: boolean) { + if (visible !== this.menubarToggled) { + this.menubarToggled = visible; + + if (this.menubarVisibility === 'toggle' || (browser.isFullscreen() && this.menubarVisibility === 'default')) { + if (browser.isFullscreen() && this.menubarVisibility === 'default') { + this._onTitleBarVisibilityChange.fire(); + } + + this.layout(); + } + } + } + private onEditorClosed(listenerDispose: IDisposable, resourcesToWaitFor: URI[], waitMarkerFile: URI): void { // In wait mode, listen to changes to the editors and wait until the files @@ -595,17 +613,25 @@ export class Workbench extends Disposable implements IPartService { private handleContextKeys(): void { this.inZenMode = InEditorZenModeContext.bindTo(this.contextKeyService); + IsMacContext.bindTo(this.contextKeyService); + IsLinuxContext.bindTo(this.contextKeyService); + IsWindowsContext.bindTo(this.contextKeyService); + FileDialogContext.bindTo(this.contextKeyService); + const sidebarVisibleContextRaw = new RawContextKey<boolean>('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) { @@ -708,12 +734,10 @@ export class Workbench extends Disposable implements IPartService { const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels); const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId()); if (!this.panelHidden && !!panelId) { + perf.mark('willRestorePanel'); const isPanelToRestoreEnabled = !!this.panelPart.getPanels().filter(p => p.id === panelId).length; - if (isPanelToRestoreEnabled) { - restorePromises.push(this.panelPart.openPanel(panelId, false)); - } else { - restorePromises.push(this.panelPart.openPanel(panelRegistry.getDefaultPanelId(), false)); - } + const panelIdToRestore = isPanelToRestoreEnabled ? panelId : panelRegistry.getDefaultPanelId(); + restorePromises.push(this.panelPart.openPanel(panelIdToRestore, false).then(() => perf.mark('didRestorePanel'))); } // Restore Zen Mode if active @@ -805,7 +829,7 @@ export class Workbench extends Disposable implements IPartService { } return paths.map(p => { - const resource = URI.file(p.filePath); + const resource = p.fileUri; let input: IResourceInput | IUntitledResourceInput; if (isNew) { input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput; @@ -916,9 +940,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 +967,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 +987,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 +1003,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(); @@ -993,95 +1017,70 @@ export class Workbench extends Disposable implements IPartService { // Notification Handlers this.createNotificationsHandlers(); + + // Menubar visibility changes + if ((isWindows || isLinux) && this.getCustomTitleBarStyle() === 'custom') { + this.titlebarPart.onMenubarVisibilityChange()(e => this.onMenubarToggled(e)); + } + // 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 +1134,10 @@ export class Workbench extends Disposable implements IPartService { private _onTitleBarVisibilityChange: Emitter<void> = this._register(new Emitter<void>()); get onTitleBarVisibilityChange(): Event<void> { return this._onTitleBarVisibilityChange.event; } - private _onMenubarVisibilityChange: Emitter<DOM.Dimension> = this._register(new Emitter<DOM.Dimension>()); - get onMenubarVisibilityChange(): Event<DOM.Dimension> { return this._onMenubarVisibilityChange.event; } - get onEditorLayout(): Event<IDimension> { return this.editorPart.onDidLayout; } isCreated(): boolean { - return this.workbenchCreated && this.workbenchStarted; + return !!(this.workbenchCreated && this.workbenchStarted); } hasFocus(part: Parts): boolean { @@ -1160,9 +1156,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 +1179,19 @@ 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())); + if (this.getCustomTitleBarStyle() !== 'custom') { + return false; + } else if (!browser.isFullscreen()) { + return true; + } else if (isMacintosh) { + return false; + } else if (this.menubarVisibility === 'visible') { + return true; + } else if (this.menubarVisibility === 'toggle' || this.menubarVisibility === 'default') { + return this.menubarToggled; + } + + return false; case Parts.SIDEBAR_PART: return !this.sideBarHidden; case Parts.PANEL_PART: @@ -1206,6 +1209,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 +1342,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 +1393,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 @@ -1460,13 +1466,19 @@ export class Workbench extends Disposable implements IPartService { } setMenubarVisibility(visibility: MenuBarVisibility, skipLayout: boolean): void { - this.menubarVisibility = visibility; + if (this.menubarVisibility !== visibility) { + this.menubarVisibility = visibility; - if (!skipLayout) { - this.workbenchLayout.layout(); + if (!skipLayout) { + this.workbenchLayout.layout(); + } } } + getMenubarVisibility(): MenuBarVisibility { + return this.menubarVisibility; + } + getPanelPosition(): Position { return this.panelPosition; } diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 6bf7a628602..c71e0dd2f54 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -15,15 +15,17 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { QueryType, ISearchQuery } from 'vs/platform/search/common/search'; import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; -import { IInitData, IEnvironment, IWorkspaceData, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { IInitData, IEnvironment, IWorkspaceData, MainContext, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol'; import * as errors from 'vs/base/common/errors'; import * as glob from 'vs/base/common/glob'; import { ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; import URI from 'vs/base/common/uri'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; +import { timeout } from 'vs/base/common/async'; +import { Counter } from 'vs/base/common/numbers'; const nativeExit = process.exit.bind(process); function patchProcess(allowExit: boolean) { @@ -84,6 +86,9 @@ export class ExtensionHostMain { private _extHostLogService: ExtHostLogService; private disposables: IDisposable[] = []; + private _searchRequestIdProvider: Counter; + private _mainThreadWorkspace: MainThreadWorkspaceShape; + constructor(protocol: IMessagePassingProtocol, initData: IInitData) { this._environment = initData.environment; @@ -98,7 +103,9 @@ export class ExtensionHostMain { this._extHostLogService = new ExtHostLogService(initData.windowId, initData.logLevel, initData.logsPath); this.disposables.push(this._extHostLogService); - const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, initData.workspace, this._extHostLogService); + + this._searchRequestIdProvider = new Counter(); + const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, initData.workspace, this._extHostLogService, this._searchRequestIdProvider); this._extHostLogService.info('extension host started'); this._extHostLogService.trace('initData', initData); @@ -138,6 +145,8 @@ export class ExtensionHostMain { mainThreadErrors.$onUnexpectedError(data); } }); + + this._mainThreadWorkspace = rpcProtocol.getProxy(MainContext.MainThreadWorkspace); } start(): TPromise<void> { @@ -179,7 +188,7 @@ export class ExtensionHostMain { // Give extensions 1 second to wrap up any async dispose, then exit setTimeout(() => { - TPromise.any<void>([TPromise.timeout(4000), extensionsDeactivated]).then(() => exit(), () => exit()); + TPromise.any<void>([timeout(4000), extensionsDeactivated]).then(() => exit(), () => exit()); }, 1000); } @@ -281,8 +290,8 @@ export class ExtensionHostMain { ignoreSymlinks: !followSymlinks }; - const result = await this._diskSearch.search(query); - if (result.limitHit) { + const exists = await this._mainThreadWorkspace.$checkExists(query, this._searchRequestIdProvider.getNext()); + if (exists) { // a file was found matching one of the glob patterns return ( this._extensionService.activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`)) @@ -294,7 +303,7 @@ export class ExtensionHostMain { } private handleExtensionTests(): TPromise<void> { - if (!this._environment.extensionTestsPath || !this._environment.extensionDevelopmentPath) { + if (!this._environment.extensionTestsPath || !this._environment.extensionDevelopmentLocationURI) { return TPromise.as(null); } diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts index 9035333b54f..c2ea02afd05 100644 --- a/src/vs/workbench/node/extensionHostProcess.ts +++ b/src/vs/workbench/node/extensionHostProcess.ts @@ -8,10 +8,11 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ExtensionHostMain, exit } from 'vs/workbench/node/extensionHostMain'; import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; import { createConnection } from 'net'; import { Event, filterEvent } from 'vs/base/common/event'; +import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; // With Electron 2.x and node.js 8.x the "natives" module // can cause a native crash (see https://github.com/nodejs/node/issues/19891 and @@ -61,7 +62,7 @@ function createExtHostProtocol(): Promise<IMessagePassingProtocol> { private _terminating = false; readonly onMessage: Event<any> = filterEvent(protocol.onMessage, msg => { - if (msg.type !== '__$terminate') { + if (!isMessageOfType(msg, MessageType.Terminate)) { return true; } this._terminating = true; @@ -85,7 +86,7 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer const first = protocol.onMessage(raw => { first.dispose(); - const initData = <IInitData>JSON.parse(raw); + const initData = <IInitData>JSON.parse(raw.toString()); // Print a console message when rejection isn't handled within N seconds. For details: // see https://nodejs.org/api/process.html#process_event_unhandledrejection @@ -114,6 +115,10 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer onUnexpectedError(err); }); + process.on('SIGPIPE', () => { + onUnexpectedError(new Error('Unexpected SIGPIPE')); + }); + // Kill oneself if one's parent dies. Much drama. setInterval(function () { try { @@ -124,13 +129,13 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer }, 5000); // Tell the outside that we are initialized - protocol.send('initialized'); + protocol.send(createMessageOfType(MessageType.Initialized)); c({ protocol, initData }); }); // Tell the outside that we are ready to receive messages - protocol.send('ready'); + protocol.send(createMessageOfType(MessageType.Ready)); }); } 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<T>(code: string, value: T = null): (err: any) => TPromise<T> { return err => err.code === code ? TPromise.as<T>(value) : TPromise.wrapError<T>(err); @@ -28,7 +28,7 @@ function ignore<T>(code: string, value: T = null): (err: any) => TPromise<T> { 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/browser/media/suggestEnabledInput.css b/src/vs/workbench/parts/codeEditor/browser/media/suggestEnabledInput.css new file mode 100644 index 00000000000..65fd4a3c8ba --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/browser/media/suggestEnabledInput.css @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.suggest-input-container { + padding: 3px 4px 5px; +} + +.suggest-input-container .monaco-editor-background, +.suggest-input-container .monaco-editor, +.suggest-input-container .mtk1 { + /* allow the embedded monaco to be styled from the outer context */ + background-color: transparent; + color: inherit; +} + +.suggest-input-container .suggest-input-placeholder { + position: absolute; + z-index: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + pointer-events: none; + margin-top: 2px; + margin-left: 1px; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/menuPreventer.ts b/src/vs/workbench/parts/codeEditor/browser/menuPreventer.ts similarity index 100% rename from src/vs/workbench/parts/codeEditor/electron-browser/menuPreventer.ts rename to src/vs/workbench/parts/codeEditor/browser/menuPreventer.ts diff --git a/src/vs/workbench/parts/codeEditor/browser/simpleEditorOptions.ts b/src/vs/workbench/parts/codeEditor/browser/simpleEditorOptions.ts new file mode 100644 index 00000000000..a18c1f0479d --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/browser/simpleEditorOptions.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; + +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/browser/suggestEnabledInput.ts b/src/vs/workbench/parts/codeEditor/browser/suggestEnabledInput.ts new file mode 100644 index 00000000000..091350f5973 --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/browser/suggestEnabledInput.ts @@ -0,0 +1,232 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/css!./media/suggestEnabledInput'; +import { $, addClass, append, removeClass, Dimension } from 'vs/base/browser/dom'; +import { chain, Emitter, Event } from 'vs/base/common/event'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import uri from 'vs/base/common/uri'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import * as modes from 'vs/editor/common/modes'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; +import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { inputBackground, inputBorder, inputForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Component } from 'vs/workbench/common/component'; +import { MenuPreventer } from 'vs/workbench/parts/codeEditor/browser/menuPreventer'; +import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/browser/simpleEditorOptions'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; + +interface SuggestResultsProvider { + /** + * Provider function for suggestion results. + * + * @param query the full text of the input. + */ + provideResults: (query: string) => string[]; + + /** + * Trigger characters for this input. Suggestions will appear when one of these is typed, + * or upon `ctrl+space` triggering at a word boundary. + * + * Defaults to the empty array. + */ + triggerCharacters?: string[]; + + /** + * Defines the sorting function used when showing results. + * + * Defaults to the identity function. + */ + sortKey?: (result: string) => string; +} + +interface SuggestEnabledInputOptions { + /** + * The text to show when no input is present. + * + * Defaults to the empty string. + */ + placeholderText?: string; + + /** + * Context key tracking the focus state of this element + */ + focusContextKey?: IContextKey<boolean>; +} + +export class SuggestEnabledInput extends Component { + + private _onShouldFocusResults = new Emitter<void>(); + readonly onShouldFocusResults: Event<void> = this._onShouldFocusResults.event; + + private _onEnter = new Emitter<void>(); + readonly onEnter: Event<void> = this._onEnter.event; + + private _onInputDidChange = new Emitter<string>(); + readonly onInputDidChange: Event<string> = this._onInputDidChange.event; + + private disposables: IDisposable[] = []; + private inputWidget: CodeEditorWidget; + private stylingContainer: HTMLDivElement; + private placeholderText: HTMLDivElement; + + constructor( + id: string, + parent: HTMLElement, + suggestionProvider: SuggestResultsProvider, + ariaLabel: string, + resourceHandle: string, + options: SuggestEnabledInputOptions, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService, + @IModelService modelService: IModelService, + ) { + super(id, themeService); + + this.stylingContainer = append(parent, $('.suggest-input-container')); + this.placeholderText = append(this.stylingContainer, $('.suggest-input-placeholder', null, options.placeholderText || '')); + + this.inputWidget = instantiationService.createInstance(CodeEditorWidget, this.stylingContainer, + mixinHTMLInputStyleOptions(getSimpleEditorOptions(), ariaLabel), + { + contributions: [SuggestController, SnippetController2, ContextMenuController, MenuPreventer], + isSimpleWidget: true, + }); + + let scopeHandle = uri.parse(resourceHandle); + this.inputWidget.setModel(modelService.createModel('', null, scopeHandle, true)); + + this.disposables.push(this.inputWidget.onDidPaste(() => this.setValue(this.getValue()))); // setter cleanses + + this.disposables.push((this.inputWidget.onDidFocusEditorText(() => { + if (options.focusContextKey) { options.focusContextKey.set(true); } + addClass(this.stylingContainer, 'synthetic-focus'); + }))); + this.disposables.push((this.inputWidget.onDidBlurEditorText(() => { + if (options.focusContextKey) { options.focusContextKey.set(false); } + removeClass(this.stylingContainer, 'synthetic-focus'); + }))); + + const onKeyDownMonaco = chain(this.inputWidget.onKeyDown); + onKeyDownMonaco.filter(e => e.keyCode === KeyCode.Enter).on(e => { e.preventDefault(); this._onEnter.fire(); }, this, this.disposables); + onKeyDownMonaco.filter(e => e.keyCode === KeyCode.DownArrow && (isMacintosh ? e.metaKey : e.ctrlKey)).on(() => this._onShouldFocusResults.fire(), this, this.disposables); + + let preexistingContent = this.getValue(); + this.disposables.push(this.inputWidget.getModel().onDidChangeContent(() => { + let content = this.getValue(); + this.placeholderText.style.visibility = content ? 'hidden' : 'visible'; + if (preexistingContent.trim() === content.trim()) { return; } + this._onInputDidChange.fire(); + preexistingContent = content; + })); + + let validatedSuggestProvider = { + provideResults: suggestionProvider.provideResults, + sortKey: suggestionProvider.sortKey || (a => a), + triggerCharacters: suggestionProvider.triggerCharacters || [] + }; + + this.disposables.push(modes.SuggestRegistry.register({ scheme: scopeHandle.scheme, pattern: '**/' + scopeHandle.path, hasAccessToAllModels: true }, { + triggerCharacters: validatedSuggestProvider.triggerCharacters, + provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext) => { + let query = model.getValue(); + + let wordStart = query.lastIndexOf(' ', position.column - 1) + 1; + let alreadyTypedCount = position.column - wordStart - 1; + + // dont show suggestions if the user has typed something, but hasn't used the trigger character + if (alreadyTypedCount > 0 && (validatedSuggestProvider.triggerCharacters).indexOf(query[wordStart]) === -1) { return { suggestions: [] }; } + + return { + suggestions: suggestionProvider.provideResults(query).map(result => { + return { + label: result, + insertText: result, + overwriteBefore: alreadyTypedCount, + sortText: validatedSuggestProvider.sortKey(result), + type: <modes.SuggestionType>'keyword' + }; + }) + }; + } + })); + } + + public setValue(val: string) { + val = val.replace(/\s/g, ' '); + this.inputWidget.setValue(val); + this.inputWidget.setScrollTop(0); + this.inputWidget.setPosition(new Position(1, val.length + 1)); + } + + public getValue(): string { + return this.inputWidget.getValue(); + } + + + public updateStyles(): void { + super.updateStyles(); + + this.stylingContainer.style.backgroundColor = this.getColor(inputBackground); + this.stylingContainer.style.color = this.getColor(inputForeground); + this.placeholderText.style.color = this.getColor(inputPlaceholderForeground); + + const inputBorderColor = this.getColor(inputBorder); + this.stylingContainer.style.borderWidth = '1px'; + this.stylingContainer.style.borderStyle = 'solid'; + this.stylingContainer.style.borderColor = inputBorderColor || 'transparent'; + + let cursor = this.stylingContainer.getElementsByClassName('cursor')[0] as HTMLDivElement; + if (cursor) { + cursor.style.backgroundColor = this.getColor(inputForeground); + } + } + + public focus(): void { + this.inputWidget.focus(); + } + + public layout(dimension: Dimension): void { + this.inputWidget.layout(dimension); + this.placeholderText.style.width = `${dimension.width}px`; + } + + public selectAll(): void { + this.inputWidget.setSelection(new Range(1, 1, 1, this.getValue().length + 1)); + } + + dispose(): void { + this.disposables = dispose(this.disposables); + super.dispose(); + } +} + + +function mixinHTMLInputStyleOptions(config: IEditorOptions, ariaLabel?: string): IEditorOptions { + config.fontSize = 13; + config.lineHeight = 22; + config.wordWrap = 'off'; + config.scrollbar.vertical = 'hidden'; + config.roundedSelection = false; + 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/codeEditor/codeEditor.contribution.ts b/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts index a4085223ade..d66877173a1 100644 --- a/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts +++ b/src/vs/workbench/parts/codeEditor/codeEditor.contribution.ts @@ -6,8 +6,7 @@ import './electron-browser/accessibility'; import './electron-browser/inspectKeybindings'; import './electron-browser/largeFileOptimizations'; -import './electron-browser/menubarRegistrations'; -import './electron-browser/menuPreventer'; +import './browser/menuPreventer'; import './electron-browser/selectionClipboard'; import './electron-browser/textMate/inspectTMScopes'; import './electron-browser/toggleMinimap'; 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..c739f208f75 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts @@ -38,6 +38,7 @@ interface ILanguageConfiguration { wordPattern?: string | IRegExp; indentationRules?: IIndentationRules; folding?: FoldingRules; + autoCloseBefore?: string; } function isStringArr(something: string[]): boolean { @@ -89,7 +90,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 = <ILanguageConfiguration>parse(contents.value.toString(), errors); if (errors.length) { @@ -274,6 +275,11 @@ export class LanguageConfigurationFileHandler { richEditConfig.surroundingPairs = surroundingPairs; } + const autoCloseBefore = configuration.autoCloseBefore; + if (typeof autoCloseBefore === 'string') { + richEditConfig.autoCloseBefore = autoCloseBefore; + } + if (configuration.wordPattern) { try { let wordPattern = this._parseRegex(configuration.wordPattern); @@ -433,6 +439,11 @@ const schema: IJSONSchema = { }] } }, + autoCloseBefore: { + default: ';:.,=}])> \n\t', + description: nls.localize('schema.autoCloseBefore', 'Defines what characters must be after the cursor in order for bracket or quote autoclosing to occur when using the \'languageDefined\' autoclosing setting. This is typically the set of characters which can not start an expression.'), + type: 'string', + }, surroundingPairs: { default: [['(', ')'], ['[', ']'], ['{', '}']], description: nls.localize('schema.surroundingPairs', 'Defines the bracket pairs that can be used to surround a selected string.'), diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/menubarRegistrations.ts b/src/vs/workbench/parts/codeEditor/electron-browser/menubarRegistrations.ts deleted file mode 100644 index 02d2ee2f366..00000000000 --- a/src/vs/workbench/parts/codeEditor/electron-browser/menubarRegistrations.ts +++ /dev/null @@ -1,138 +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 nls from 'vs/nls'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; - -selectionMenuRegistration(); - -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 - }); -} diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts b/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts new file mode 100644 index 00000000000..4e3a0f1a14c --- /dev/null +++ b/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { MenuPreventer } from 'vs/workbench/parts/codeEditor/browser/menuPreventer'; +import { SelectionClipboard } from 'vs/workbench/parts/codeEditor/electron-browser/selectionClipboard'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; +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 function getSimpleCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { + return { + isSimpleWidget: true, + contributions: [ + MenuPreventer, + SelectionClipboard, + ContextMenuController, + SuggestController, + SnippetController2, + TabCompletionController, + ] + }; +} \ No newline at end of file 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<string>('multiCursorModifier', 'altKey'); + +class MultiCursorModifierContextKeyController implements IWorkbenchContribution { + + private readonly _multiCursorModifier: IContextKey<string>; + + 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<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(MultiCursorModifierContextKeyController, LifecyclePhase.Running); + + const registry = Registry.as<IWorkbenchActionRegistry>(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/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/common/reviewModel.ts b/src/vs/workbench/parts/comments/common/reviewModel.ts deleted file mode 100644 index 2c3b962122f..00000000000 --- a/src/vs/workbench/parts/comments/common/reviewModel.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { Emitter, Event } from 'vs/base/common/event'; - -export enum ReviewStyle { - Complete, - Inline, - Gutter -} - -export class ReviewModel { - private _style: ReviewStyle; - public get style(): ReviewStyle { return this._style; } - private _onDidChangeStyle = new Emitter<ReviewStyle>(); - public get onDidChangeStyle(): Event<ReviewStyle> { return this._onDidChangeStyle.event; } - - constructor() { - this._style = ReviewStyle.Inline; - } - - setStyle(style: ReviewStyle) { - this._style = style; - this._onDidChangeStyle.fire(this._style); - } -} \ No newline at end of file diff --git a/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts index 44d7a10810e..b05c049300e 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts @@ -4,37 +4,38 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { ICodeEditor, IContentWidget, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -export class CommentGlyphWidget implements IContentWidget { - private _id: string; +export class CommentGlyphWidget { private _lineNumber: number; - private _domNode: HTMLDivElement; private _editor: ICodeEditor; + private commentsDecorations: string[] = []; + private _commentsOptions: ModelDecorationOptions; - constructor(id: string, editor: ICodeEditor, lineNumber: number, disabled: boolean, onClick: () => void) { - this._id = id; - this._domNode = document.createElement('div'); - this._domNode.className = disabled ? 'comment-hint commenting-disabled' : 'comment-hint'; - this._domNode.addEventListener('click', onClick); + constructor(editor: ICodeEditor, lineNumber: number, commentsOptions: ModelDecorationOptions, onClick: () => void) { + this._commentsOptions = commentsOptions; this._lineNumber = lineNumber; - this._editor = editor; - this._editor.addContentWidget(this); - + this.update(); } - getDomNode(): HTMLElement { - return this._domNode; - } + update() { + let commentsDecorations = [{ + range: { + startLineNumber: this._lineNumber, startColumn: 1, + endLineNumber: this._lineNumber, endColumn: 1 + }, + options: this._commentsOptions + }]; - getId(): string { - return this._id; + this.commentsDecorations = this._editor.getModel().deltaDecorations(this.commentsDecorations, commentsDecorations); } setLineNumber(lineNumber: number): void { this._lineNumber = lineNumber; + this.update(); } getPosition(): IContentWidgetPosition { @@ -46,4 +47,10 @@ export class CommentGlyphWidget implements IContentWidget { preference: [ContentWidgetPositionPreference.EXACT] }; } + + dispose() { + if (this.commentsDecorations) { + this._editor.deltaDecorations(this.commentsDecorations, []); + } + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index 41fd94bf268..7dce0085037 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, textBlockQuoteBackground, textBlockQuoteBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { transparent, editorForeground, textLinkActiveForeground, textLinkForeground, focusBorder, textBlockQuoteBackground, textBlockQuoteBorder, contrastBorder, inputValidationErrorBorder, inputValidationErrorBackground } 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'; @@ -34,6 +34,8 @@ import { Range, IRange } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; +import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; const EXPAND_ACTION_CLASS = 'expand-review-action octicon octicon-chevron-down'; @@ -45,9 +47,11 @@ export class CommentNode { private _body: HTMLElement; private _md: HTMLElement; private _clearTimeout: any; + public get domNode(): HTMLElement { return this._domNode; } + constructor( public comment: modes.Comment, private markdownRenderer: MarkdownRenderer, @@ -93,11 +97,10 @@ export class CommentNode { } let INMEM_MODEL_ID = 0; + export class ReviewZoneWidget extends ZoneWidget { private _headElement: HTMLElement; - protected _primaryHeading: HTMLElement; - protected _secondaryHeading: HTMLElement; - protected _metaHeading: HTMLElement; + protected _headingLabel: HTMLElement; protected _actionbarWidget: ActionBar; private _bodyElement: HTMLElement; private _commentEditor: ICodeEditor; @@ -107,14 +110,17 @@ export class ReviewZoneWidget extends ZoneWidget { private _reviewThreadReplyButton: HTMLElement; private _resizeObserver: any; private _onDidClose = new Emitter<ReviewZoneWidget>(); + private _onDidCreateThread = new Emitter<ReviewZoneWidget>(); private _isCollapsed; private _toggleAction: Action; private _commentThread: modes.CommentThread; private _commentGlyph: CommentGlyphWidget; private _owner: number; private _localToDispose: IDisposable[]; + private _globalToDispose: IDisposable[]; private _markdownRenderer: MarkdownRenderer; private _styleElement: HTMLStyleElement; + private _error: HTMLElement; public get owner(): number { return this._owner; @@ -140,11 +146,17 @@ export class ReviewZoneWidget extends ZoneWidget { this._owner = owner; this._commentThread = commentThread; this._isCollapsed = commentThread.collapsibleState !== modes.CommentThreadCollapsibleState.Expanded; + this._globalToDispose = []; this._localToDispose = []; this.create(); this._styleElement = dom.createStyleSheet(this.domNode); - this.themeService.onThemeChange(this._applyTheme, this); + this._globalToDispose.push(this.themeService.onThemeChange(this._applyTheme, this)); + this._globalToDispose.push(this.editor.onDidChangeConfiguration(e => { + if (e.fontInfo) { + this._applyTheme(this.themeService.getTheme()); + } + })); this._applyTheme(this.themeService.getTheme()); this._markdownRenderer = new MarkdownRenderer(editor, this.modeService, this.openerService); @@ -154,6 +166,10 @@ export class ReviewZoneWidget extends ZoneWidget { return this._onDidClose.event; } + public get onDidCreateThread(): Event<ReviewZoneWidget> { + return this._onDidCreateThread.event; + } + protected revealLine(lineNumber: number) { // we don't do anything here as we always do the reveal ourselves. } @@ -163,8 +179,6 @@ export class ReviewZoneWidget extends ZoneWidget { this.show({ lineNumber: this._commentThread.range.startLineNumber, column: 1 }, 2); } - this._bodyElement.focus(); - if (commentId) { let height = this.editor.getLayoutInfo().height; let matchedNode = this._commentElements.filter(commentNode => commentNode.comment.commentId === commentId); @@ -173,7 +187,6 @@ export class ReviewZoneWidget extends ZoneWidget { const commentCoords = dom.getDomNodePagePosition(matchedNode[0].domNode); this.editor.setScrollTop(this.editor.getTopForLineNumber(this._commentThread.range.startLineNumber) - height / 2 + commentCoords.top - commentThreadCoords.top); - matchedNode[0].focus(); return; } } @@ -196,13 +209,8 @@ export class ReviewZoneWidget extends ZoneWidget { appendTo(this._headElement). getHTMLElement(); - this._primaryHeading = $('span.filename').appendTo(titleElement).getHTMLElement(); - this._secondaryHeading = $('span.dirname').appendTo(titleElement).getHTMLElement(); - this._metaHeading = $('span.meta').appendTo(titleElement).getHTMLElement(); - - if (this._commentThread.comments.length) { - this.createParticipantsLabel(); - } + this._headingLabel = $('span.filename').appendTo(titleElement).getHTMLElement(); + this.createThreadLabel(); const actionsContainer = $('.review-actions').appendTo(this._headElement); this._actionbarWidget = new ActionBar(actionsContainer.getHTMLElement(), {}); @@ -279,42 +287,20 @@ export class ReviewZoneWidget extends ZoneWidget { this._commentThread = commentThread; this._commentElements = newCommentNodeList; - let secondaryHeading = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', '); - $(this._secondaryHeading).safeInnerHtml(secondaryHeading); + this.createThreadLabel(); } protected _doLayout(heightInPixel: number, widthInPixel: number): void { - this._commentEditor.layout({ height: (this._commentEditor.hasWidgetFocus() ? 5 : 1) * 18, width: widthInPixel - 40 /* margin */ }); + this._commentEditor.layout({ height: (this._commentEditor.hasWidgetFocus() ? 5 : 1) * 18, width: widthInPixel - 42 /* margin */ }); } - display(lineNumber: number) { - this._commentGlyph = new CommentGlyphWidget(`review_${lineNumber}`, this.editor, lineNumber, false, () => { + display(lineNumber: number, commentsOptions: ModelDecorationOptions) { + this._commentGlyph = new CommentGlyphWidget(this.editor, lineNumber, commentsOptions, () => { this.toggleExpand(); }); - this.editor.layoutContentWidget(this._commentGlyph); this._localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); this._localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); - this._localToDispose.push(this.editor.onDidChangeModelContent((e) => { - // If the widget has been opened, the position is set and can be relied on for updating the glyph position - if (this.position) { - if (this.position.lineNumber !== this._commentGlyph.getPosition().position.lineNumber) { - this._commentGlyph.setLineNumber(this.position.lineNumber); - this.editor.layoutContentWidget(this._commentGlyph); - } - } else { - // Otherwise manually calculate position change :( - const positionChange = e.changes.map(change => { - if (change.range.startLineNumber < change.range.endLineNumber) { - return change.range.startLineNumber - change.range.endLineNumber; - } else { - return change.text.split(e.eol).length - 1; - } - }).reduce((prev, curr) => prev + curr, 0); - this._commentGlyph.setLineNumber(this._commentGlyph.getPosition().position.lineNumber + positionChange); - this.editor.layoutContentWidget(this._commentGlyph); - } - })); var headHeight = Math.ceil(this.editor.getConfiguration().lineHeight * 1.2); this._headElement.style.height = `${headHeight}px`; this._headElement.style.lineHeight = this._headElement.style.height; @@ -354,65 +340,43 @@ 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(); } } + + if (this._commentEditor.getModel().getValueLength() !== 0 && ev.keyCode === KeyCode.Enter && (ev.ctrlKey || ev.metaKey)) { + let lineNumber = this._commentGlyph.getPosition().position.lineNumber; + this.createComment(lineNumber); + } })); + this._error = $('.validation-error.hidden').appendTo(this._commentForm).getHTMLElement(); + const formActions = $('.form-actions').appendTo(this._commentForm).getHTMLElement(); 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; - } - - let newCommentThread; - if (this._commentThread.threadId) { - // reply - newCommentThread = await this.commentService.replyToCommentThread( - this._owner, - this.editor.getModel().uri, - new Range(lineNumber, 1, lineNumber, 1), - this._commentThread, - this._commentEditor.getValue() - ); + button.enabled = false; + this._localToDispose.push(this._commentEditor.onDidChangeModelContent(_ => { + if (this._commentEditor.getValue()) { + button.enabled = true; } else { - newCommentThread = await this.commentService.createNewCommentThread( - this._owner, - this.editor.getModel().uri, - new Range(lineNumber, 1, lineNumber, 1), - this._commentEditor.getValue() - ); - - this.createReplyButton(); - this.createParticipantsLabel(); + button.enabled = false; } + })); - this._commentEditor.setValue(''); - if (dom.hasClass(this._commentForm, 'expand')) { - dom.removeClass(this._commentForm, 'expand'); - } - - if (newCommentThread) { - this.update(newCommentThread); - } + button.onDidClick(async () => { + let lineNumber = this._commentGlyph.getPosition().position.lineNumber; + this.createComment(lineNumber); }); this._resizeObserver = new MutationObserver(this._refresh.bind(this)); @@ -434,20 +398,72 @@ export class ReviewZoneWidget extends ZoneWidget { } } - createParticipantsLabel() { - const primaryHeading = 'Participants:'; - $(this._primaryHeading).safeInnerHtml(primaryHeading); - this._primaryHeading.setAttribute('aria-label', primaryHeading); + private async createComment(lineNumber: number): Promise<void> { + try { + let newCommentThread; + const isReply = this._commentThread.threadId !== null; - const secondaryHeading = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', '); - $(this._secondaryHeading).safeInnerHtml(secondaryHeading); - this._secondaryHeading.setAttribute('aria-label', secondaryHeading); + if (isReply) { + newCommentThread = await this.commentService.replyToCommentThread( + this._owner, + this.editor.getModel().uri, + new Range(lineNumber, 1, lineNumber, 1), + this._commentThread, + this._commentEditor.getValue() + ); + } else { + newCommentThread = await this.commentService.createNewCommentThread( + this._owner, + this.editor.getModel().uri, + new Range(lineNumber, 1, lineNumber, 1), + this._commentEditor.getValue() + ); + + if (newCommentThread) { + this.createReplyButton(); + } + } + + if (newCommentThread) { + this._commentEditor.setValue(''); + if (dom.hasClass(this._commentForm, 'expand')) { + dom.removeClass(this._commentForm, 'expand'); + } + this._commentEditor.getDomNode().style.outline = ''; + this._error.textContent = ''; + dom.addClass(this._error, 'hidden'); + this.update(newCommentThread); + + if (!isReply) { + this._onDidCreateThread.fire(this); + } + } + } catch (e) { + this._error.textContent = e.message + ? nls.localize('commentCreationError', "Adding a comment failed: {0}.", e.message) + : nls.localize('commentCreationDefaultError', "Adding a comment failed. Please try again or report an issue with the extension if the problem persists."); + this._commentEditor.getDomNode().style.outline = `1px solid ${this.themeService.getTheme().getColor(inputValidationErrorBorder)}`; + dom.removeClass(this._error, 'hidden'); + } } - createReplyButton() { + private createThreadLabel() { + let label: string; + if (this._commentThread.comments.length) { + const participantsList = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', '); + label = nls.localize('commentThreadParticipants', "Participants: {0}", participantsList); + } else { + label = nls.localize('startThread', "Start discussion"); + } + + $(this._headingLabel).safeInnerHtml(label); + this._headingLabel.setAttribute('aria-label', label); + } + + private createReplyButton() { this._reviewThreadReplyButton = <HTMLButtonElement>$('button.review-thread-reply-button').appendTo(this._commentForm).getHTMLElement(); - this._reviewThreadReplyButton.title = 'Reply...'; - this._reviewThreadReplyButton.textContent = 'Reply...'; + this._reviewThreadReplyButton.title = nls.localize('reply', "Reply..."); + this._reviewThreadReplyButton.textContent = nls.localize('reply', "Reply..."); // bind click/escape actions for reviewThreadReplyButton and textArea this._reviewThreadReplyButton.onclick = () => { if (!dom.hasClass(this._commentForm, 'expand')) { @@ -481,7 +497,11 @@ export class ReviewZoneWidget extends ZoneWidget { if (model) { let valueLength = model.getValueLength(); const hasExistingComments = this._commentThread.comments.length > 0; - let placeholder = valueLength > 0 ? '' : (hasExistingComments ? 'Reply...' : 'Type a new comment'); + let placeholder = valueLength > 0 + ? '' + : (hasExistingComments + ? nls.localize('replytoCommentThread', "Reply... (press Ctrl+Enter to submit)") + : nls.localize('createCommentThread', "Type a new comment (press Ctrl+Enter to submit)")); const decorations = [{ range: { startLineNumber: 0, @@ -501,61 +521,72 @@ export class ReviewZoneWidget extends ZoneWidget { } } - private mouseDownInfo: { lineNumber: number, iconClicked: boolean }; + private mouseDownInfo: { lineNumber: number }; private onEditorMouseDown(e: IEditorMouseEvent): void { - if (!e.event.leftButton) { - return; - } + this.mouseDownInfo = null; + + const range = e.target.range; - let range = e.target.range; if (!range) { return; } - let iconClicked = false; - switch (e.target.type) { - case MouseTargetType.GUTTER_GLYPH_MARGIN: - iconClicked = true; - break; - default: - return; + if (!e.event.leftButton) { + return; } - this.mouseDownInfo = { lineNumber: range.startLineNumber, iconClicked }; + if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { + return; + } + + const data = e.target.detail as IMarginData; + const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft; + + // don't collide with folding and git decorations + if (gutterOffsetX > 14) { + return; + } + + this.mouseDownInfo = { lineNumber: range.startLineNumber }; } private onEditorMouseUp(e: IEditorMouseEvent): void { if (!this.mouseDownInfo) { return; } - let lineNumber = this.mouseDownInfo.lineNumber; - let iconClicked = this.mouseDownInfo.iconClicked; - let range = e.target.range; + const { lineNumber } = this.mouseDownInfo; + this.mouseDownInfo = null; + + const range = e.target.range; + if (!range || range.startLineNumber !== lineNumber) { return; } - if (this.position && this.position.lineNumber !== lineNumber) { + if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { return; } - if (!this.position && lineNumber !== this._commentThread.range.startLineNumber) { + if (!e.target.element) { return; } - if (iconClicked) { - if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN) { - return; + if (this._commentGlyph && this._commentGlyph.getPosition().position.lineNumber !== lineNumber) { + return; + } + + if (e.target.element.className.indexOf('comment-thread') >= 0) { + if (this._isCollapsed) { + this.show({ lineNumber: lineNumber, column: 1 }, 2); + } else { + this.hide(); + if (this._commentThread === null || this._commentThread.threadId === null) { + this.dispose(); + } } } - - if (this._isCollapsed) { - this.show({ lineNumber: lineNumber, column: 1 }, 2); - } else { - this.hide(); - } } private _applyTheme(theme: ITheme) { @@ -598,6 +629,23 @@ export class ReviewZoneWidget extends ZoneWidget { content.push(`.monaco-editor .review-widget .body .comment-form .monaco-editor { outline: 1px solid ${hcBorder}; }`); } + const errorBorder = theme.getColor(inputValidationErrorBorder); + if (errorBorder) { + content.push(`.monaco-editor .review-widget .body .comment-form .validation-error { border: 1px solid ${errorBorder}; }`); + } + + const errorBackground = theme.getColor(inputValidationErrorBackground); + if (errorBackground) { + content.push(`.monaco-editor .review-widget .body .comment-form .validation-error { background: ${errorBackground}; }`); + } + + const fontInfo = this.editor.getConfiguration().fontInfo; + content.push(`.monaco-editor .review-widget .body code { + font-family: ${fontInfo.fontFamily}; + font-size: ${fontInfo.fontSize}px; + font-weight: ${fontInfo.fontWeight}; + }`); + this._styleElement.innerHTML = content.join('\n'); // Editor decorations should also be responsive to theme changes @@ -623,10 +671,11 @@ export class ReviewZoneWidget extends ZoneWidget { } if (this._commentGlyph) { - this.editor.removeContentWidget(this._commentGlyph); + this._commentGlyph.dispose(); this._commentGlyph = null; } + this._globalToDispose.forEach(global => global.dispose()); this._localToDispose.forEach(local => local.dispose()); this._onDidClose.fire(); } diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index 619170cb23a..ffa0cbae652 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -6,31 +6,34 @@ import 'vs/css!./media/review'; import * as nls from 'vs/nls'; -import { $ } from 'vs/base/browser/builder'; +import { $ } from 'vs/base/browser/dom'; 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 { ICodeEditor, IEditorMouseEvent, IViewZone, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IRange } from 'vs/editor/common/core/range'; import * as modes from 'vs/editor/common/modes'; -import { peekViewEditorBackground, peekViewResultsBackground, peekViewResultsSelectionBackground } from 'vs/editor/contrib/referenceSearch/referencesWidget'; +import { peekViewResultsBackground, peekViewResultsSelectionBackground, peekViewTitleBackground } 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, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { CommentThreadCollapsibleState } from 'vs/workbench/api/node/extHostTypes'; -import { ReviewModel } from 'vs/workbench/parts/comments/common/reviewModel'; -import { CommentGlyphWidget } from 'vs/workbench/parts/comments/electron-browser/commentGlyphWidget'; import { ReviewZoneWidget, COMMENTEDITOR_DECORATION_KEY } from 'vs/workbench/parts/comments/electron-browser/commentThreadWidget'; import { ICommentService } from 'vs/workbench/parts/comments/electron-browser/commentService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { IModelDecorationOptions } from 'vs/editor/common/model'; +import { Color, RGBA } from 'vs/base/common/color'; +import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export const ctxReviewPanelVisible = new RawContextKey<boolean>('reviewPanelVisible', false); @@ -45,7 +48,7 @@ export class ReviewViewZone implements IViewZone { this.afterLineNumber = afterLineNumber; this.callback = onDomNodeTop; - this.domNode = $('.review-viewzone').getHTMLElement(); + this.domNode = $('.review-viewzone'); } onDomNodeTop(top: number): void { @@ -53,6 +56,110 @@ export class ReviewViewZone implements IViewZone { } } +const overviewRulerDefault = new Color(new RGBA(197, 197, 197, 1)); + +export const overviewRulerCommentingRangeForeground = registerColor('editorGutter.commentRangeForeground', { dark: overviewRulerDefault, light: overviewRulerDefault, hc: overviewRulerDefault }, nls.localize('editorGutterCommentRangeForeground', 'Editor gutter decoration color for commenting ranges.')); + +class CommentingRangeDecoration { + private _decorationId: string; + + get id(): string { + return this._decorationId; + } + + constructor(private _editor: ICodeEditor, private _ownerId: number, private _range: IRange, private _reply: modes.Command, commentingOptions: ModelDecorationOptions) { + const startLineNumber = _range.startLineNumber; + const endLineNumber = _range.endLineNumber; + let commentingRangeDecorations = [{ + range: { + startLineNumber: startLineNumber, startColumn: 1, + endLineNumber: endLineNumber, endColumn: 1 + }, + options: commentingOptions + }]; + + let model = this._editor.getModel(); + if (model) { + this._decorationId = model.deltaDecorations([this._decorationId], commentingRangeDecorations)[0]; + } + } + + getCommentAction(): { replyCommand: modes.Command, ownerId: number } { + return { + replyCommand: this._reply, + ownerId: this._ownerId + }; + } + + getOriginalRange() { + return this._range; + } + + getActiveRange() { + return this._editor.getModel().getDecorationRange(this._decorationId); + } +} +class CommentingRangeDecorator { + + static createDecoration(className: string, foregroundColor: string, options: { gutter: boolean, overview: boolean }): ModelDecorationOptions { + const decorationOptions: IModelDecorationOptions = { + isWholeLine: true, + }; + + decorationOptions.linesDecorationsClassName = `comment-range-glyph ${className}`; + return ModelDecorationOptions.createDynamic(decorationOptions); + } + + private commentingOptions: ModelDecorationOptions; + public commentsOptions: ModelDecorationOptions; + private commentingRangeDecorations: CommentingRangeDecoration[] = []; + private disposables: IDisposable[] = []; + + constructor( + ) { + const options = { gutter: true, overview: false }; + this.commentingOptions = CommentingRangeDecorator.createDecoration('comment-diff-added', overviewRulerCommentingRangeForeground, options); + this.commentsOptions = CommentingRangeDecorator.createDecoration('comment-thread', overviewRulerCommentingRangeForeground, options); + } + + update(editor: ICodeEditor, commentInfos: modes.CommentInfo[]) { + let model = editor.getModel(); + if (!model) { + return; + } + + let commentingRangeDecorations = []; + for (let i = 0; i < commentInfos.length; i++) { + let info = commentInfos[i]; + info.commentingRanges.forEach(range => { + commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, range, info.reply, this.commentingOptions)); + }); + } + + let oldDecorations = this.commentingRangeDecorations.map(decoration => decoration.id); + editor.deltaDecorations(oldDecorations, []); + + this.commentingRangeDecorations = commentingRangeDecorations; + } + + getMatchedCommentAction(line: number) { + for (let i = 0; i < this.commentingRangeDecorations.length; i++) { + let range = this.commentingRangeDecorations[i].getActiveRange(); + + if (range.startLineNumber <= line && line <= range.endLineNumber) { + return this.commentingRangeDecorations[i].getCommentAction(); + } + } + + return null; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + this.commentingRangeDecorations = []; + } +} + export class ReviewController implements IEditorContribution { private globalToDispose: IDisposable[]; private localToDispose: IDisposable[]; @@ -61,9 +168,11 @@ export class ReviewController implements IEditorContribution { private _commentWidgets: ReviewZoneWidget[]; private _reviewPanelVisible: IContextKey<boolean>; private _commentInfos: modes.CommentInfo[]; - private _reviewModel: ReviewModel; - private _newCommentGlyph: CommentGlyphWidget; - private _hasSetComments: boolean; + // private _hasSetComments: boolean; + private _commentingRangeDecorator: CommentingRangeDecorator; + private mouseDownInfo: { lineNumber: number } | null = null; + private _commentingRangeSpaceReserved = false; + constructor( editor: ICodeEditor, @@ -83,30 +192,10 @@ export class ReviewController implements IEditorContribution { this._commentInfos = []; this._commentWidgets = []; this._newCommentWidget = null; - this._newCommentGlyph = null; - this._hasSetComments = false; + // this._hasSetComments = false; this._reviewPanelVisible = ctxReviewPanelVisible.bindTo(contextKeyService); - this._reviewModel = new ReviewModel(); - - this._reviewModel.onDidChangeStyle(style => { - if (this._newCommentWidget) { - this._newCommentWidget.dispose(); - this._newCommentWidget = null; - } - - this._commentWidgets.forEach(zone => { - zone.dispose(); - }); - - this._commentInfos.forEach(info => { - info.threads.forEach(thread => { - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.editor, info.owner, thread, {}); - zoneWidget.display(thread.range.startLineNumber); - this._commentWidgets.push(zoneWidget); - }); - }); - }); + this._commentingRangeDecorator = new CommentingRangeDecorator(); this.globalToDispose.push(this.commentService.onDidDeleteDataProvider(e => { // Remove new comment widget and glyph, refresh comments @@ -115,11 +204,6 @@ export class ReviewController implements IEditorContribution { this._newCommentWidget = null; } - if (this._newCommentGlyph) { - this.editor.removeContentWidget(this._newCommentGlyph); - this._newCommentGlyph = null; - } - this.getComments(); })); @@ -227,34 +311,30 @@ export class ReviewController implements IEditorContribution { public onModelChanged(): void { this.localToDispose = dispose(this.localToDispose); if (this._newCommentWidget) { - // todo store view state. + // todo@peng store view state. this._newCommentWidget.dispose(); this._newCommentWidget = null; } - if (this._newCommentGlyph) { - this.editor.removeContentWidget(this._newCommentGlyph); - this._newCommentGlyph = null; - } - this._commentWidgets.forEach(zone => { zone.dispose(); }); this._commentWidgets = []; - this.localToDispose.push(this.editor.onMouseMove(e => this.onEditorMouseMove(e))); - this.localToDispose.push(this.editor.onDidBlurEditorText(() => this.onDidBlurEditorText())); + this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); + this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); this.localToDispose.push(this.editor.onDidChangeModelContent(() => { - if (this._newCommentGlyph) { - this.editor.removeContentWidget(this._newCommentGlyph); - this._newCommentGlyph = null; - } })); this.localToDispose.push(this.commentService.onDidUpdateCommentThreads(e => { const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri; if (!editorURI) { return; } + + if (!this._commentInfos.some(info => info.owner === e.owner)) { + return; + } + let added = e.added.filter(thread => thread.resource.toString() === editorURI.toString()); let removed = e.removed.filter(thread => thread.resource.toString() === editorURI.toString()); let changed = e.changed.filter(thread => thread.resource.toString() === editorURI.toString()); @@ -277,7 +357,7 @@ export class ReviewController implements IEditorContribution { }); added.forEach(thread => { let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.editor, e.owner, thread, {}); - zoneWidget.display(thread.range.startLineNumber); + zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions); this._commentWidgets.push(zoneWidget); this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread); }); @@ -285,7 +365,12 @@ export class ReviewController implements IEditorContribution { } private addComment(lineNumber: number) { - let newCommentInfo = this.getNewCommentAction(lineNumber); + if (this._newCommentWidget !== null) { + this.notificationService.warn(`Please submit the comment at line ${this._newCommentWidget.position.lineNumber} before creating a new one.`); + return; + } + + let newCommentInfo = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber); if (!newCommentInfo) { return; } @@ -307,104 +392,124 @@ export class ReviewController implements IEditorContribution { collapsibleState: CommentThreadCollapsibleState.Expanded, }, {}); - this._newCommentWidget.onDidClose(e => { + this.localToDispose.push(this._newCommentWidget.onDidClose(e => { this._newCommentWidget = null; - }); - this._newCommentWidget.display(lineNumber); + })); + + this.localToDispose.push(this._newCommentWidget.onDidCreateThread(commentWidget => { + const thread = commentWidget.commentThread; + this._commentWidgets.push(commentWidget); + this._commentInfos.filter(info => info.owner === commentWidget.owner)[0].threads.push(thread); + this._newCommentWidget = null; + })); + + this._newCommentWidget.display(lineNumber, this._commentingRangeDecorator.commentsOptions); } - private onEditorMouseMove(e: IEditorMouseEvent): void { - if (!this._hasSetComments) { + private onEditorMouseDown(e: IEditorMouseEvent): void { + this.mouseDownInfo = null; + + const range = e.target.range; + + if (!range) { return; } - if (!this.editor.hasTextFocus()) { + if (!e.event.leftButton) { return; } - const hasCommentingRanges = this._commentInfos.length && this._commentInfos.some(info => !!info.commentingRanges.length); - if (hasCommentingRanges && e.target.position && e.target.position.lineNumber !== undefined) { - if (this._newCommentGlyph && e.target.element.className !== 'comment-hint') { - this.editor.removeContentWidget(this._newCommentGlyph); - } + if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { + return; + } + const data = e.target.detail as IMarginData; + const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft; + + // don't collide with folding and git decorations + if (gutterOffsetX > 14) { + return; + } + + this.mouseDownInfo = { lineNumber: range.startLineNumber }; + } + + private onEditorMouseUp(e: IEditorMouseEvent): void { + if (!this.mouseDownInfo) { + return; + } + + const { lineNumber } = this.mouseDownInfo; + this.mouseDownInfo = null; + + const range = e.target.range; + + if (!range || range.startLineNumber !== lineNumber) { + return; + } + + if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { + return; + } + + if (!e.target.element) { + return; + } + + if (e.target.element.className.indexOf('comment-diff-added') >= 0) { const lineNumber = e.target.position.lineNumber; - if (!this.isExistingCommentThreadAtLine(lineNumber)) { - this._newCommentGlyph = this.isLineInCommentingRange(lineNumber) - ? this._newCommentGlyph = new CommentGlyphWidget('comment-hint', this.editor, lineNumber, false, () => { - this.addComment(lineNumber); - }) - : this._newCommentGlyph = new CommentGlyphWidget('comment-hint', this.editor, lineNumber, true, () => { - this.notificationService.warn('Commenting is not supported outside of diff hunk areas.'); - }); - - this.editor.layoutContentWidget(this._newCommentGlyph); - } + this.addComment(lineNumber); } } - private onDidBlurEditorText(): 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]; - const lineWithinRange = commentInfo.commentingRanges.some(range => - range.startLineNumber <= line && line <= range.endLineNumber - ); - - if (lineWithinRange) { - return { - replyCommand: commentInfo.reply, - ownerId: commentInfo.owner - }; - } - } - - return null; - } - - private isLineInCommentingRange(line: number): boolean { - return this._commentInfos.some(commentInfo => { - return commentInfo.commentingRanges.some(range => - range.startLineNumber <= line && line <= range.endLineNumber - ); - }); - } - - private isExistingCommentThreadAtLine(line: number): boolean { - const existingThread = this._commentInfos.some(commentInfo => { - return commentInfo.threads.some(thread => - thread.range.startLineNumber === line - ); - }); - - const existingNewComment = this._newCommentWidget && this._newCommentWidget.position && this._newCommentWidget.position.lineNumber === line; - - return existingThread || existingNewComment; - } - setComments(commentInfos: modes.CommentInfo[]): void { this._commentInfos = commentInfos; - this._hasSetComments = true; + let lineDecorationsWidth: number = this.editor.getConfiguration().layoutInfo.decorationsWidth; + + if (this._commentInfos.some(info => Boolean(info.commentingRanges && info.commentingRanges.length))) { + if (!this._commentingRangeSpaceReserved) { + this._commentingRangeSpaceReserved = true; + let extraEditorClassName = []; + if (this.editor.getRawConfiguration().extraEditorClassName) { + extraEditorClassName = this.editor.getRawConfiguration().extraEditorClassName.split(' '); + } + + if (this.editor.getConfiguration().contribInfo.folding) { + lineDecorationsWidth -= 16; + } + lineDecorationsWidth += 9; + extraEditorClassName.push('inline-comment'); + this.editor.updateOptions({ + extraEditorClassName: extraEditorClassName.join(' '), + lineDecorationsWidth: lineDecorationsWidth + }); + this.editor.layout(); + } + } + + // this._hasSetComments = true; // create viewzones this._commentWidgets.forEach(zone => { zone.dispose(); }); + this._commentWidgets = []; + this._commentInfos.forEach(info => { info.threads.forEach(thread => { let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.editor, info.owner, thread, {}); - zoneWidget.display(thread.range.startLineNumber); + zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions); this._commentWidgets.push(zoneWidget); }); }); - } + const commentingRanges = []; + this._commentInfos.forEach(info => { + commentingRanges.push(...info.commentingRanges); + }); + this._commentingRangeDecorator.update(this.editor, this._commentInfos); + } public closeWidget(): void { this._reviewPanelVisible.reset(); @@ -488,7 +593,7 @@ registerThemingParticipant((theme, collector) => { `}`); } - let monacoEditorBackground = theme.getColor(peekViewEditorBackground); + let monacoEditorBackground = theme.getColor(peekViewTitleBackground); if (monacoEditorBackground) { collector.addRule( `.monaco-editor .review-widget .body .comment-form .review-thread-reply-button {` + @@ -522,4 +627,22 @@ registerThemingParticipant((theme, collector) => { `}` ); } + + const commentingRangeForeground = theme.getColor(overviewRulerCommentingRangeForeground); + if (commentingRangeForeground) { + collector.addRule(` + .monaco-editor .comment-diff-added { + border-left: 3px solid ${commentingRangeForeground}; + } + .monaco-editor .comment-diff-added:before { + background: ${commentingRangeForeground}; + } + .monaco-editor .comment-thread { + border-left: 3px solid ${commentingRangeForeground}; + } + .monaco-editor .comment-thread:before { + background: ${commentingRangeForeground}; + } + `); + } }); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index c916bec1da8..44c69e089f7 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -232,7 +232,6 @@ export class CommentsPanel extends Panel { const control = editor.getControl(); if (threadToReveal && isCodeEditor(control)) { const controller = ReviewController.get(control); - console.log(commentToReveal.command); controller.revealCommentThread(threadToReveal, commentToReveal.commentId); } setCommentsForFile = null; @@ -262,7 +261,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 d8672d23d57..ed3f817715d 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import * as nls from 'vs/nls'; import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -156,6 +157,14 @@ export class CommentsModelRenderer implements ITreeRenderer { } }); + const images = renderedComment.getElementsByTagName('img'); + for (let i = 0; i < images.length; i++) { + const image = images[i]; + const textDescription = dom.$(''); + textDescription.textContent = image.alt ? nls.localize('imageWithLabel', "Image: {0}", image.alt) : nls.localize('image', "Image"); + image.parentNode.replaceChild(textDescription, image); + } + 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 dd28fb338ef..fec55286837 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -10,7 +10,7 @@ background-position: center center; } -.monaco-editor .comment-hint{ +.monaco-editor .comment-hint { height: 20px; width: 20px; padding-left: 2px; @@ -117,6 +117,10 @@ padding: 0 0.4em; } +.monaco-editor .review-widget .body span { + white-space: pre; +} + .monaco-editor .review-widget .body .comment-body img { max-width: 100%; } @@ -126,6 +130,29 @@ padding: 8px 0; } +.monaco-editor .review-widget .body .comment-form .validation-error { + display: inline-block; + overflow: hidden; + text-align: left; + width: 100%; + box-sizing: border-box; + -webkit-box-sizing: border-box; + -o-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + padding: 0.4em; + font-size: 12px; + line-height: 17px; + min-height: 34px; + margin-top: -1px; + margin-left: -1px; + word-wrap: break-word; +} + +.monaco-editor .review-widget .body .comment-form .validation-error.hidden { + display: none; +} + .monaco-editor .review-widget .body .comment-form.expand .review-thread-reply-button { display: none; } @@ -133,7 +160,7 @@ .monaco-editor .review-widget .body .comment-form.expand .monaco-editor, .monaco-editor .review-widget .body .comment-form.expand .form-actions { display: block; - box-sizing: border-box; + box-sizing: content-box; } .monaco-editor .review-widget .body .comment-form .review-thread-reply-button { @@ -141,7 +168,7 @@ display: block; width: 100%; resize: vertical; - border-radius: 3px; + border-radius: 0; box-sizing: border-box; padding: 6px 12px; font-weight: 600; @@ -164,6 +191,7 @@ max-height: 500px; border-radius: 3px; border: 0px; + padding: 6px 0 6px 12px; } .monaco-editor .review-widget .body .comment-form .form-actions { @@ -239,4 +267,52 @@ .monaco-editor .review-widget>.body { border-top: 1px solid; position: relative; -} \ No newline at end of file +} + +.monaco-editor .comment-range-glyph { + margin-left: 5px; + width: 4px !important; + cursor: pointer; + z-index: 10; +} + +.monaco-editor .comment-range-glyph:before { + position: absolute; + content: ''; + height: 100%; + width: 0; + left: -2px; + transition: width 80ms linear, left 80ms linear; +} + +.monaco-editor .margin-view-overlays>div:hover>.comment-range-glyph.comment-diff-added:before { + position: absolute; + content: '+'; + height: 100%; + width: 9px; + left: -6px; + z-index: 10; + color: black; +} + +.monaco-editor .comment-range-glyph.comment-thread { + z-index: 20; +} + +.monaco-editor .comment-range-glyph.comment-thread:before { + position: absolute; + content: '·'; + height: 100%; + width: 9px; + left: -6px; + z-index: 20; + color: black; +} + +.monaco-editor.inline-comment .margin-view-overlays .folding { + margin-left: 14px; +} + +.monaco-editor.inline-comment .margin-view-overlays .dirty-diff-glyph { + margin-left: 14px; +} diff --git a/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts b/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts index 3bbb410d3f5..a103105c641 100644 --- a/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts +++ b/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts @@ -12,7 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ICommandService } from 'vs/platform/commands/common/commands'; // Allowed Editor Contributions: -import { MenuPreventer } from 'vs/workbench/parts/codeEditor/electron-browser/menuPreventer'; +import { MenuPreventer } from 'vs/workbench/parts/codeEditor/browser/menuPreventer'; import { SelectionClipboard } from 'vs/workbench/parts/codeEditor/electron-browser/selectionClipboard'; import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index ef55c3cdac8..99e546e6d1a 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'; @@ -33,9 +29,9 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -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 { ILabelService } from 'vs/platform/label/common/label'; const $ = dom.$; @@ -94,17 +90,29 @@ export class BreakpointsView extends ViewletPanel { this.disposables.push(this.list.onOpen(e => { let isSingleClick = false; let isDoubleClick = false; + let isMiddleClick = false; let openToSide = false; const browserEvent = e.browserEvent; if (browserEvent instanceof MouseEvent) { isSingleClick = browserEvent.detail === 1; isDoubleClick = browserEvent.detail === 2; + isMiddleClick = browserEvent.button === 1; openToSide = (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey); } const focused = this.list.getFocusedElements(); const element = focused.length ? focused[0] : undefined; + + if (isMiddleClick) { + if (element instanceof Breakpoint) { + this.debugService.removeBreakpoints(element.getId()).done(undefined, onUnexpectedError); + } else if (element instanceof FunctionBreakpoint) { + this.debugService.removeFunctionBreakpoints(element.getId()).done(undefined, onUnexpectedError); + } + return; + } + if (element instanceof Breakpoint) { openBreakpointSource(element, openToSide, isSingleClick, this.debugService, this.editorService).done(undefined, onUnexpectedError); } @@ -287,9 +295,7 @@ class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateD constructor( @IDebugService private debugService: IDebugService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IEnvironmentService private environmentService: IEnvironmentService, - @ITextFileService private textFileService: ITextFileService + @ILabelService private labelService: ILabelService ) { // noop } @@ -327,15 +333,15 @@ class BreakpointsRenderer implements IRenderer<IBreakpoint, IBreakpointTemplateD data.context = breakpoint; dom.toggleClass(data.breakpoint, 'disabled', !this.debugService.getModel().areBreakpointsActivated()); - data.name.textContent = basename(getPathLabel(breakpoint.uri, this.environmentService, this.contextService)); + data.name.textContent = resources.basenameOrAuthority(breakpoint.uri); data.lineNumber.textContent = breakpoint.lineNumber.toString(); if (breakpoint.column) { data.lineNumber.textContent += `:${breakpoint.column}`; } - data.filePath.textContent = getPathLabel(resources.dirname(breakpoint.uri), this.environmentService, this.contextService); + data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), true); data.checkbox.checked = breakpoint.enabled; - const { message, className } = getBreakpointMessageAndClassName(this.debugService, this.textFileService, breakpoint); + const { message, className } = getBreakpointMessageAndClassName(this.debugService, breakpoint); data.icon.className = className + ' icon'; data.breakpoint.title = breakpoint.message || message || ''; @@ -405,8 +411,7 @@ class ExceptionBreakpointsRenderer implements IRenderer<IExceptionBreakpoint, IB class FunctionBreakpointsRenderer implements IRenderer<FunctionBreakpoint, IBaseBreakpointWithIconTemplateData> { constructor( - @IDebugService private debugService: IDebugService, - @ITextFileService private textFileService: ITextFileService + @IDebugService private debugService: IDebugService ) { // noop } @@ -439,7 +444,7 @@ class FunctionBreakpointsRenderer implements IRenderer<FunctionBreakpoint, IBase renderElement(functionBreakpoint: FunctionBreakpoint, index: number, data: IBaseBreakpointWithIconTemplateData): void { data.context = functionBreakpoint; data.name.textContent = functionBreakpoint.name; - const { className, message } = getBreakpointMessageAndClassName(this.debugService, this.textFileService, functionBreakpoint); + const { className, message } = getBreakpointMessageAndClassName(this.debugService, functionBreakpoint); data.icon.className = className + ' icon'; data.icon.title = message ? message : ''; data.checkbox.checked = functionBreakpoint.enabled; @@ -510,9 +515,12 @@ class FunctionBreakpointInputRenderer implements IRenderer<IFunctionBreakpoint, } })); toDispose.push(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { - if (!template.breakpoint.name) { - wrapUp(true); - } + // Need to react with a timeout on the blur event due to possible concurent splices #56443 + setTimeout(() => { + if (!template.breakpoint.name) { + wrapUp(true); + } + }); })); template.inputBox = inputBox; @@ -566,7 +574,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } -export function getBreakpointMessageAndClassName(debugService: IDebugService, textFileService: ITextFileService, breakpoint: IBreakpoint | FunctionBreakpoint): { message?: string, className: string } { +export function getBreakpointMessageAndClassName(debugService: IDebugService, breakpoint: IBreakpoint | FunctionBreakpoint): { message?: string, className: string } { const state = debugService.state; const debugActive = state === State.Running || state === State.Stopped; @@ -601,14 +609,6 @@ export function getBreakpointMessageAndClassName(debugService: IDebugService, te }; } - if (debugActive && textFileService.isDirty(breakpoint.uri)) { - return { - className: 'debug-breakpoint-unverified', - message: appendMessage(nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session.")), - }; - } - - if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) { const messages = []; if (breakpoint.logMessage) { 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 04fd4058642..f9b26e2ad32 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -12,9 +12,9 @@ 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 } +import { IDebugService, State, ISession, IThread, IEnablement, IBreakpoint, IStackFrame, REPL_ID } from 'vs/workbench/parts/debug/common/debug'; -import { Variable, Expression, Thread, Breakpoint, Session } from 'vs/workbench/parts/debug/common/debugModel'; +import { Variable, Expression, Thread, Breakpoint } from 'vs/workbench/parts/debug/common/debugModel'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -113,7 +113,7 @@ export class ConfigureAction extends AbstractDebugAction { configurationManager.selectConfiguration(configurationManager.getLaunches()[0]); } - return configurationManager.selectedConfiguration.launch.openConfigFile(sideBySide); + return configurationManager.selectedConfiguration.launch.openConfigFile(sideBySide, false); } } @@ -226,11 +226,11 @@ export class RestartAction extends AbstractDebugAction { } private setLabel(session: ISession): void { - this.updateLabel(session && session.state === SessionState.ATTACH ? RestartAction.RECONNECT_LABEL : RestartAction.LABEL); + this.updateLabel(session && session.configuration.request === 'attach' ? RestartAction.RECONNECT_LABEL : RestartAction.LABEL); } public run(session: ISession): TPromise<any> { - if (!(session instanceof Session)) { + if (!session || !session.getId) { session = this.debugService.getViewModel().focusedSession; } @@ -325,7 +325,7 @@ export class StopAction extends AbstractDebugAction { } public run(session: ISession): TPromise<any> { - if (!(session instanceof Session)) { + if (!session || !session.getId) { session = this.debugService.getViewModel().focusedSession; } 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<IDebugConfiguration>('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 7c09e489c3a..a827a84bb21 100644 --- a/src/vs/workbench/parts/debug/browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/browser/debugCommands.ts @@ -186,7 +186,7 @@ export function registerCommands(): void { } const launch = manager.getLaunches().filter(l => l.uri.toString() === launchUri).pop() || manager.selectedConfiguration.launch; - return launch.openConfigFile(false).done(({ editor, created }) => { + return launch.openConfigFile(false, false).done(({ editor, created }) => { if (editor && !created) { const codeEditor = <ICodeEditor>editor.getControl(); if (codeEditor) { diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts index d6332e2f2ca..3d00826481d 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -118,7 +118,7 @@ class RunToCursorAction extends EditorAction { } let breakpointToRemove: IBreakpoint; - const oneTimeListener = debugService.getViewModel().focusedSession.raw.onDidEvent(event => { + const oneTimeListener = debugService.getViewModel().focusedSession.onDidCustomEvent(event => { if (event.event === 'stopped' || event.event === 'exit') { if (breakpointToRemove) { debugService.removeBreakpoints(breakpointToRemove.getId()); diff --git a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts index 4b21fa882fb..185e38174a3 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts @@ -12,7 +12,6 @@ import { IDebugService, IBreakpoint, State, IBreakpointUpdateData } from 'vs/wor import { IModelService } from 'vs/editor/common/services/modelService'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { getBreakpointMessageAndClassName } from 'vs/workbench/parts/debug/browser/breakpointsView'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; interface IBreakpointDecoration { decorationId: string; @@ -38,7 +37,6 @@ export class DebugEditorModelManager implements IWorkbenchContribution { constructor( @IModelService private modelService: IModelService, @IDebugService private debugService: IDebugService, - @ITextFileService private textFileService: ITextFileService ) { this.modelDataMap = new Map<string, IDebugEditorModelData>(); this.toDispose = []; @@ -277,7 +275,7 @@ export class DebugEditorModelManager implements IWorkbenchContribution { } private getBreakpointDecorationOptions(breakpoint: IBreakpoint): IModelDecorationOptions { - const { className, message } = getBreakpointMessageAndClassName(this.debugService, this.textFileService, breakpoint); + const { className, message } = getBreakpointMessageAndClassName(this.debugService, breakpoint); let glyphMarginHoverMessage: MarkdownString; if (message) { diff --git a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts index 66905ae107a..86b46a38be1 100644 --- a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts +++ b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts @@ -98,7 +98,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { const configManager = this.debugService.getConfigurationManager(); const launches = configManager.getLaunches(); for (let launch of launches) { - launch.getConfigurationNames().map(config => ({ config: config, highlights: Filters.matchesContiguousSubString(input, config) })) + launch.getConfigurationNames().map(config => ({ config: config, highlights: Filters.matchesFuzzy(input, config, true) })) .filter(({ highlights }) => !!highlights) .forEach(({ config, highlights }) => { if (launch === configManager.selectedConfiguration.launch && config === configManager.selectedConfiguration.name) { @@ -110,7 +110,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { launches.filter(l => !l.hidden).forEach((l, index) => { const label = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? nls.localize("addConfigTo", "Add Config ({0})...", l.name) : nls.localize('addConfiguration', "Add Configuration..."); - const entry = new AddConfigEntry(label, l, this.commandService, this.contextService, Filters.matchesContiguousSubString(input, label)); + const entry = new AddConfigEntry(label, l, this.commandService, this.contextService, Filters.matchesFuzzy(input, label, true)); if (index === 0) { configurations.push(new Model.QuickOpenEntryGroup(entry, undefined, true)); } else { diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index da803391a97..b8bd358a6cf 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -7,18 +7,306 @@ 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 } from 'vs/platform/list/browser/listService'; +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 _showedMoreThanOne: boolean; + private _children: { [key: string]: BaseTreeItem; }; + private _source: Source; + + constructor(private _parent: BaseTreeItem, private _label: string) { + this._children = {}; + this._showedMoreThanOne = false; + } + + setSource(session: ISession, source: Source): void { + this._source = source; + } + + createIfNeeded<T extends BaseTreeItem>(key: string, factory: (parent: BaseTreeItem, label: string) => T): T { + let child = <T>this._children[key]; + if (!child) { + child = factory(this, key); + this._children[key] = child; + } + return child; + } + + remove(key: string): void { + delete this._children[key]; + } + + getTemplateId(): string { + return SOURCE_TEMPLATE_ID; + } + + // a dynamic ID based on the parent chain; required for reparenting (see #55448) + getId(): string { + const parent = this.getParent(); + return parent ? `${parent.getId()}/${this._label}` : this._label; + } + + // skips intermediate single-child nodes + getParent(): BaseTreeItem { + if (this._parent) { + const child = this._parent.oneChild(); + if (child) { + return this._parent.getParent(); + } + return this._parent; + } + return undefined; + } + + // skips intermediate single-child nodes + hasChildren(): boolean { + const child = this.oneChild(); + if (child) { + return child.hasChildren(); + } + return Object.keys(this._children).length > 0; + } + + // skips intermediate single-child nodes + getChildren(): TPromise<BaseTreeItem[]> { + 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))); + } + + // skips intermediate single-child nodes + getLabel(separateRootFolder = true) { + const child = this.oneChild(); + if (child) { + const sep = (this instanceof RootFolderTreeItem && separateRootFolder) ? ' • ' : '/'; + return `${this._label}${sep}${child.getLabel()}`; + } + return this._label; + } + + // skips intermediate single-child nodes + getHoverLabel(): string { + let label = this.getLabel(false); + if (this._parent) { + const parentLabel = this._parent.getHoverLabel(); + if (parentLabel) { + return `${parentLabel}/${label}`; + } + } + return label; + } + + // skips intermediate single-child nodes + getSource(): Source { + const child = this.oneChild(); + if (child) { + return child.getSource(); + } + return this._source; + } + + 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._showedMoreThanOne) { + const keys = Object.keys(this._children); + if (keys.length === 1) { + return this._children[keys[0]]; + } + // if a node had more than one child once, it will never be skipped again + if (keys.length > 1) { + this._showedMoreThanOne = true; + } + } + 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 { + + constructor(private _debugModel: IModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService) { + super(undefined, 'Root'); + this._debugModel.getSessions().forEach(session => { + this.add(session); + }); + } + + 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; + } + + getHoverLabel(): string { + return undefined; + } + + getTemplateId(): string { + return SESSION_TEMPLATE_ID; + } + + hasChildren(): boolean { + return true; + } + + getChildren(): TPromise<BaseTreeItem[]> { + + 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); + } + + 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 (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<string>; + private settings: any; + constructor( options: IViewletViewOptions, @@ -26,22 +314,84 @@ export class LoadedScriptsView extends TreeViewsViewletPanel { @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(), - }, { + 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.onDidNewSession(session => { + this.disposables.push(session.onDidLoadedSource(event => { + const sessionRoot = root.add(session); + sessionRoot.addPath(event.source); + + clearTimeout(timeout); + timeout = setTimeout(() => { + if (this.tree) { + this.tree.refresh(root, true); + } + }, 300); + })); + })); + + this.disposables.push(this.debugService.onDidEndSession(session => { + clearTimeout(timeout); + root.remove(session.getId()); + if (this.tree) { + this.tree.refresh(root, false); + } + })); } layoutBody(size: number): void { @@ -50,6 +400,11 @@ export class LoadedScriptsView extends TreeViewsViewletPanel { } 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 @@ -57,48 +412,116 @@ export class LoadedScriptsView extends TreeViewsViewletPanel { class LoadedScriptsDataSource implements IDataSource { getId(tree: ITree, element: any): string { - throw new Error('Method not implemented.'); + return element.getId(); } hasChildren(tree: ITree, element: any): boolean { - throw new Error('Method not implemented.'); + return element.hasChildren(); } getChildren(tree: ITree, element: any): TPromise<any> { - throw new Error('Method not implemented.'); + return element.getChildren(); } getParent(tree: ITree, element: any): TPromise<any> { - throw new Error('Method not implemented.'); + return element.getParent(); } + + 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 { - throw new Error('Method not implemented.'); + return 22; } getTemplateId(tree: ITree, element: any): string { - throw new Error('Method not implemented.'); + return element.getTemplateId(); } renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { - throw new Error('Method not implemented.'); + + 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 { - throw new Error('Method not implemented.'); + 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 { - throw new Error('Method not implemented.'); + // noop + } + + private renderSession(session: SessionTreeItem, data: ISessionTemplateData): void { + data.session.title = nls.localize('loadedScriptsSession', "Session"); + data.session.textContent = session.getLabel(); + } + + private renderSource(source: BaseTreeItem, data: ISourceTemplateData): void { + data.source.title = source.getHoverLabel(); + data.source.textContent = source.getLabel(); + } + + private renderNode(node: BaseTreeItem, data: INodeTemplateData): void { + data.node.title = node.getHoverLabel(); + data.node.textContent = node.getLabel(); } } class LoadedSciptsAccessibilityProvider implements IAccessibilityProvider { public getAriaLabel(tree: ITree, element: any): string { - return nls.localize('implement me', "implement me"); + + if (element instanceof RootFolderTreeItem) { + return nls.localize('loadedScriptsRootFolderAriaLabel', "Workspace folder {0}, loaded script, debug", element.getLabel()); + } + + if (element instanceof SessionTreeItem) { + return nls.localize('loadedScriptsSessionAriaLabel', "Session {0}, loaded script, debug", element.getLabel()); + } + + if (element instanceof BaseTreeItem) { + if (element.hasChildren()) { + return nls.localize('loadedScriptsFolderAriaLabel', "Folder {0}, loaded script, debug", element.getLabel()); + } else { + return nls.localize('loadedScriptsSourceAriaLabel', "{0}, loaded script, debug", element.getLabel()); + } + } + return null; } } diff --git a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css index 6637cb84e53..0785081abcb 100644 --- a/src/vs/workbench/parts/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/parts/debug/browser/media/debug.contribution.css @@ -53,7 +53,7 @@ } .debug-breakpoint, -.monaco-editor .debug-breakpoint-column.debug-breakpoint-column::before { +.monaco-editor .debug-breakpoint-column::before { background: url('breakpoint.svg') center center no-repeat; } @@ -90,11 +90,13 @@ background: url('breakpoint-log.svg') center center no-repeat; } -.debug-breakpoint-log-disabled { +.debug-breakpoint-log-disabled, +.monaco-editor .debug-breakpoint-log-disabled-column::before { background: url('breakpoint-log-disabled.svg') center center no-repeat; } -.debug-breakpoint-log-unverified { +.debug-breakpoint-log-unverified, +.monaco-editor .debug-breakpoint-log-unverified-column::before { background: url('breakpoint-log-unverified.svg') center center no-repeat; } diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css index 09cf7033e9a..48ab4d9099a 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -109,6 +109,7 @@ font-size: 0.9em; padding: 0 3px; margin-left: 0.8em; + line-height: 20px; } .debug-viewlet .monaco-tree .monaco-tree-row.selected .line-number, @@ -350,6 +351,10 @@ /* Breakpoints */ +.debug-viewlet .monaco-list-row { + line-height: 22px; +} + .debug-viewlet .debug-breakpoints .monaco-list-row .breakpoint { padding-left: 2px; } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 8db643264cd..799ba33fb8e 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -37,8 +37,8 @@ export const REPL_ID = 'workbench.panel.repl'; export const DEBUG_SERVICE_ID = 'debugService'; export const CONTEXT_DEBUG_TYPE = new RawContextKey<string>('debugType', undefined); export const CONTEXT_DEBUG_STATE = new RawContextKey<string>('debugState', 'inactive'); -export const CONTEXT_NOT_IN_DEBUG_MODE = CONTEXT_DEBUG_STATE.isEqualTo('inactive'); -export const CONTEXT_IN_DEBUG_MODE = CONTEXT_DEBUG_STATE.notEqualsTo('inactive'); +export const CONTEXT_IN_DEBUG_MODE = new RawContextKey<boolean>('inDebugMode', false); +export const CONTEXT_NOT_IN_DEBUG_MODE = CONTEXT_IN_DEBUG_MODE.toNegated(); export const CONTEXT_IN_DEBUG_REPL = new RawContextKey<boolean>('inDebugRepl', false); export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey<boolean>('breakpointWidgetVisible', false); export const CONTEXT_IN_BREAKPOINT_WIDGET = new RawContextKey<boolean>('inBreakpointWidget', false); @@ -49,13 +49,14 @@ export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey<boolean>('expressio export const CONTEXT_BREAKPOINT_SELECTED = new RawContextKey<boolean>('breakpointSelected', false); export const CONTEXT_CALLSTACK_ITEM_TYPE = new RawContextKey<string>('callStackItemType', undefined); export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey<boolean>('loadedScriptsSupported', false); +export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey<string>('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 @@ -108,7 +109,6 @@ export interface IExpression extends IReplElement, IExpressionContainer { } export interface IRawSession { - readonly root: IWorkspaceFolder; stackTrace(args: DebugProtocol.StackTraceArguments): TPromise<DebugProtocol.StackTraceResponse>; exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise<DebugProtocol.ExceptionInfoResponse>; scopes(args: DebugProtocol.ScopesArguments): TPromise<DebugProtocol.ScopesResponse>; @@ -116,11 +116,9 @@ export interface IRawSession { evaluate(args: DebugProtocol.EvaluateArguments): TPromise<DebugProtocol.EvaluateResponse>; readonly capabilities: DebugProtocol.Capabilities; + disconnect(restart?: boolean): TPromise<any>; terminate(restart?: boolean): TPromise<DebugProtocol.TerminateResponse>; custom(request: string, args: any): TPromise<DebugProtocol.Response>; - onDidEvent: Event<DebugProtocol.Event>; - onDidInitialize: Event<DebugProtocol.InitializedEvent>; - onDidExitAdapter: Event<{ sessionId: string }>; restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): TPromise<DebugProtocol.RestartFrameResponse>; next(args: DebugProtocol.NextArguments): TPromise<DebugProtocol.NextResponse>; @@ -135,23 +133,38 @@ export interface IRawSession { completions(args: DebugProtocol.CompletionsArguments): TPromise<DebugProtocol.CompletionsResponse>; setVariable(args: DebugProtocol.SetVariableArguments): TPromise<DebugProtocol.SetVariableResponse>; source(args: DebugProtocol.SourceArguments): TPromise<DebugProtocol.SourceResponse>; + loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise<DebugProtocol.LoadedSourcesResponse>; + } -export enum SessionState { - ATTACH, - LAUNCH -} - -export interface ISession extends ITreeElement { - getName(includeRoot: boolean): string; +export interface ISession extends ITreeElement, IDisposable { readonly configuration: IConfig; readonly raw: IRawSession; - readonly state: SessionState; + readonly state: State; + readonly root: IWorkspaceFolder; + + getName(includeRoot: boolean): string; getSourceForUri(modelUri: uri): Source; getThread(threadId: number): IThread; getAllThreads(): ReadonlyArray<IThread>; getSource(raw: DebugProtocol.Source): Source; + getLoadedSources(): TPromise<Source[]>; completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise<ISuggestion[]>; + clearThreads(removeThreads: boolean, reference?: number): void; + + rawUpdate(data: IRawModelUpdate): void; + + /** + * Allows to register on loaded source events. + */ + onDidLoadedSource: Event<LoadedSourceEvent>; + + /** + * Allows to register on custom DAP events. + */ + onDidCustomEvent: Event<DebugEvent>; + + onDidExitAdapter: Event<void>; } export interface IThread extends ITreeElement { @@ -392,6 +405,7 @@ export interface IConfig extends IEnvConfig { // internals __sessionId?: string; __restart?: any; + __autoAttach?: boolean; port?: number; // TODO } @@ -561,7 +575,7 @@ export interface ILaunch { /** * Opens the launch.json file. Creates if it does not exist. */ - openConfigFile(sideBySide: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }>; + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }>; } // Debug service interfaces @@ -572,6 +586,11 @@ export interface DebugEvent extends DebugProtocol.Event { sessionId?: string; } +export interface LoadedSourceEvent { + reason: string; + source: Source; +} + export interface IDebugService { _serviceBrand: any; @@ -595,11 +614,6 @@ export interface IDebugService { */ onDidEndSession: Event<ISession>; - /** - * Allows to register on custom DAP events. - */ - onDidCustomEvent: Event<DebugEvent>; - /** * Gets the current configuration manager. */ @@ -655,6 +669,12 @@ export interface IDebugService { */ removeFunctionBreakpoints(id?: string): TPromise<void>; + /** + * Sends all breakpoints to the passed session. + * If session is not passed, sends all breakpoints to each session. + */ + sendAllBreakpoints(session?: ISession): TPromise<any>; + /** * Adds a new expression to the repl. */ @@ -668,7 +688,7 @@ export interface IDebugService { /** * Appends the passed string to the debug repl. */ - logToRepl(value: string, sev?: severity): void; + logToRepl(value: string | IExpression, sev?: severity, source?: IReplElementSource): void; /** * Adds a new watch expression and evaluates it against the debug adapter. @@ -700,7 +720,7 @@ export interface IDebugService { /** * Restarts a session or creates a new one if there is no active session. */ - restartSession(session: ISession): TPromise<any>; + restartSession(session: ISession, restartData?: any): TPromise<any>; /** * Stops the session. If the session does not exist then stops all sessions. @@ -721,6 +741,11 @@ export interface IDebugService { * Gets the current view model. */ getViewModel(): IViewModel; + + /** + * Try to auto focus the top stack frame of the passed thread. + */ + tryToAutoFocusStackFrame(thread: IThread): TPromise<any>; } // Editor interfaces diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 6e8edd9f7cf..29fbe3310dd 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -16,17 +16,15 @@ import severity from 'vs/base/common/severity'; import { isObject, isString, isUndefinedOrNull } from 'vs/base/common/types'; import { distinct } from 'vs/base/common/arrays'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { ISuggestion } from 'vs/editor/common/modes'; -import { Position } from 'vs/editor/common/core/position'; import { ITreeElement, IExpression, IExpressionContainer, ISession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IModel, IReplElementSource, - IConfig, IRawSession, IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, SessionState, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint + IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint } from 'vs/workbench/parts/debug/common/debug'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; -import { mixin } from 'vs/base/common/objects'; import { commonSuffixLength } from 'vs/base/common/strings'; import { sep } from 'vs/base/common/paths'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; const MAX_REPL_LENGTH = 10000; @@ -197,7 +195,7 @@ export class ExpressionContainer implements IExpressionContainer { return response && response.body && response.body.variables ? distinct(response.body.variables.filter(v => !!v && isString(v.name)), v => v.name).map( v => new Variable(this.session, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type) ) : []; - }, (e: Error) => [new Variable(this.session, this, 0, null, e.message, '', 0, 0, { kind: 'virtual' }, null, false)]); + }, (e: Error) => [new Variable(this.session, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, null, false)]); } // The adapter explicitly sents the children count of an expression only if there are lots of children which should be chunked. @@ -552,167 +550,6 @@ export class Thread implements IThread { } } -export class Session implements ISession { - - private sources: Map<string, Source>; - private threads: Map<number, Thread>; - - constructor(private _configuration: { resolved: IConfig, unresolved: IConfig }, private session: IRawSession & ITreeElement) { - this.threads = new Map<number, Thread>(); - this.sources = new Map<string, Source>(); - } - - public get configuration(): IConfig { - return this._configuration.resolved; - } - - public get unresolvedConfiguration(): IConfig { - return this._configuration.unresolved; - } - - public get raw(): IRawSession & ITreeElement { - return this.session; - } - - public set raw(value: IRawSession & ITreeElement) { - this.session = value; - } - - public getName(includeRoot: boolean): string { - return includeRoot && this.raw.root ? `${this.configuration.name} (${resources.basenameOrAuthority(this.raw.root.uri)})` : this.configuration.name; - } - - public get state(): SessionState { - return this.configuration.type === 'attach' ? SessionState.ATTACH : SessionState.LAUNCH; - } - - public getSourceForUri(modelUri: uri): Source { - return this.sources.get(modelUri.toString()); - } - - public getSource(raw: DebugProtocol.Source): Source { - let source = new Source(raw, this.getId()); - if (this.sources.has(source.uri.toString())) { - source = this.sources.get(source.uri.toString()); - source.raw = mixin(source.raw, raw); - if (source.raw && raw) { - // Always take the latest presentation hint from adapter #42139 - source.raw.presentationHint = raw.presentationHint; - } - } else { - this.sources.set(source.uri.toString(), source); - } - - return source; - } - - public getThread(threadId: number): Thread { - return this.threads.get(threadId); - } - - public getAllThreads(): IThread[] { - const result: IThread[] = []; - this.threads.forEach(t => result.push(t)); - return result; - } - - public getId(): string { - return this.session.getId(); - } - - public rawUpdate(data: IRawModelUpdate): void { - - if (data.thread && !this.threads.has(data.threadId)) { - // A new thread came in, initialize it. - this.threads.set(data.threadId, new Thread(this, data.thread.name, data.thread.id)); - } else if (data.thread && data.thread.name) { - // Just the thread name got updated #18244 - this.threads.get(data.threadId).name = data.thread.name; - } - - if (data.stoppedDetails) { - // Set the availability of the threads' callstacks depending on - // whether the thread is stopped or not - if (data.stoppedDetails.allThreadsStopped) { - this.threads.forEach(thread => { - thread.stoppedDetails = thread.threadId === data.threadId ? data.stoppedDetails : { reason: undefined }; - thread.stopped = true; - thread.clearCallStack(); - }); - } else if (this.threads.has(data.threadId)) { - // One thread is stopped, only update that thread. - const thread = this.threads.get(data.threadId); - thread.stoppedDetails = data.stoppedDetails; - thread.clearCallStack(); - thread.stopped = true; - } - } - } - - public clearThreads(removeThreads: boolean, reference: number = undefined): void { - if (reference !== undefined && reference !== null) { - if (this.threads.has(reference)) { - const thread = this.threads.get(reference); - thread.clearCallStack(); - thread.stoppedDetails = undefined; - thread.stopped = false; - - if (removeThreads) { - this.threads.delete(reference); - } - } - } else { - this.threads.forEach(thread => { - thread.clearCallStack(); - thread.stoppedDetails = undefined; - thread.stopped = false; - }); - - if (removeThreads) { - this.threads.clear(); - ExpressionContainer.allValues.clear(); - } - } - } - - public completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise<ISuggestion[]> { - if (!this.raw.capabilities.supportsCompletionsRequest) { - return TPromise.as([]); - } - - return this.raw.completions({ - frameId, - text, - column: position.column, - line: position.lineNumber - }).then(response => { - const result: ISuggestion[] = []; - if (response && response.body && response.body.targets) { - response.body.targets.forEach(item => { - if (item && item.label) { - result.push({ - label: item.label, - insertText: item.text || item.label, - type: item.type, - filterText: item.start && item.length && text.substr(item.start, item.length).concat(item.label), - overwriteBefore: item.length || overwriteBefore - }); - } - }); - } - - return result; - }, () => []); - } - - setNotAvailable(modelUri: uri) { - const source = this.sources.get(modelUri.toString()); - if (source) { - source.available = false; - } - } -} - export class Enablement implements IEnablement { constructor( public enabled: boolean, @@ -786,6 +623,7 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { hitCondition: string, logMessage: string, private _adapterData: any, + private textFileService: ITextFileService, id = generateUuid() ) { super(enabled, hitCondition, condition, logMessage, id); @@ -793,7 +631,16 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { public get lineNumber(): number { const data = this.getSessionData(); - return data && typeof data.line === 'number' ? data.line : this._lineNumber; + return this.verified && data && typeof data.line === 'number' ? data.line : this._lineNumber; + } + + public get verified(): boolean { + const data = this.getSessionData(); + if (data) { + return data.verified && !this.textFileService.isDirty(this.uri); + } + + return true; } public get column(): number { @@ -804,7 +651,14 @@ export class Breakpoint extends BaseBreakpoint implements IBreakpoint { public get message(): string { const data = this.getSessionData(); - return data ? data.message : undefined; + if (!data) { + return undefined; + } + if (this.textFileService.isDirty(this.uri)) { + return nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session."); + } + + return data.message; } public get adapterData(): any { @@ -859,7 +713,8 @@ export class FunctionBreakpoint extends BaseBreakpoint implements IFunctionBreak hitCondition: string, condition: string, logMessage: string, - id = generateUuid()) { + id = generateUuid() + ) { super(enabled, hitCondition, condition, logMessage, id); } @@ -897,7 +752,7 @@ export class ThreadAndSessionIds implements ITreeElement { export class Model implements IModel { - private sessions: Session[]; + private sessions: ISession[]; private toDispose: lifecycle.IDisposable[]; private replElements: IReplElement[]; private schedulers = new Map<string, RunOnceScheduler>(); @@ -912,7 +767,8 @@ export class Model implements IModel { private breakpointsActivated: boolean, private functionBreakpoints: FunctionBreakpoint[], private exceptionBreakpoints: ExceptionBreakpoint[], - private watchExpressions: Expression[] + private watchExpressions: Expression[], + private textFileService: ITextFileService ) { this.sessions = []; this.replElements = []; @@ -927,15 +783,12 @@ export class Model implements IModel { return 'root'; } - public getSessions(): Session[] { + public getSessions(): ISession[] { return this.sessions; } - public addSession(configuration: { resolved: IConfig, unresolved: IConfig }, raw: IRawSession & ITreeElement): Session { - const session = new Session(configuration, raw); + public addSession(session: ISession): void { this.sessions.push(session); - - return session; } public removeSession(id: string): void { @@ -1053,7 +906,7 @@ export class Model implements IModel { } public addBreakpoints(uri: uri, rawData: IBreakpointData[], fireEvent = true): IBreakpoint[] { - const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition, rawBp.logMessage, undefined, rawBp.id)); + const newBreakpoints = rawData.map(rawBp => new Breakpoint(uri, rawBp.lineNumber, rawBp.column, rawBp.enabled, rawBp.condition, rawBp.hitCondition, rawBp.logMessage, undefined, this.textFileService, rawBp.id)); newBreakpoints.forEach(bp => bp.setSessionId(this.breakpointsSessionId)); this.breakpoints = this.breakpoints.concat(newBreakpoints); this.breakpointsActivated = true; @@ -1269,7 +1122,7 @@ export class Model implements IModel { } public sourceIsNotAvailable(uri: uri): void { - this.sessions.forEach(p => p.setNotAvailable(uri)); + this.sessions.forEach(p => p.getSourceForUri(uri).available = false); this._onDidChangeCallStack.fire(); } diff --git a/src/vs/workbench/parts/debug/common/debugViewModel.ts b/src/vs/workbench/parts/debug/common/debugViewModel.ts index d1d7cc88b50..b2d6e7b6d89 100644 --- a/src/vs/workbench/parts/debug/common/debugViewModel.ts +++ b/src/vs/workbench/parts/debug/common/debugViewModel.ts @@ -9,6 +9,8 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c export class ViewModel implements IViewModel { + firstSessionStart = true; + private _focusedStackFrame: IStackFrame; private _focusedSession: ISession; private _focusedThread: IThread; @@ -32,15 +34,15 @@ export class ViewModel implements IViewModel { this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService); } - public getId(): string { + getId(): string { return 'root'; } - public get focusedSession(): ISession { + get focusedSession(): ISession { return this._focusedSession; } - public get focusedThread(): IThread { + get focusedThread(): IThread { if (this._focusedStackFrame) { return this._focusedStackFrame.thread; } @@ -54,65 +56,63 @@ export class ViewModel implements IViewModel { return undefined; } - public get focusedStackFrame(): IStackFrame { + get focusedStackFrame(): IStackFrame { return this._focusedStackFrame; } - public setFocus(stackFrame: IStackFrame, thread: IThread, session: ISession, explicit: boolean): void { - let shouldEmit = this._focusedSession !== session || this._focusedThread !== thread || this._focusedStackFrame !== stackFrame; + setFocus(stackFrame: IStackFrame, thread: IThread, session: ISession, explicit: boolean): void { + const shouldEmit = this._focusedSession !== session || this._focusedThread !== thread || this._focusedStackFrame !== stackFrame; + this._focusedStackFrame = stackFrame; + this._focusedThread = thread; if (this._focusedSession !== session) { this._focusedSession = session; this._onDidFocusSession.fire(session); } - this._focusedThread = thread; - this._focusedStackFrame = stackFrame; this.loadedScriptsSupportedContextKey.set(session && session.raw.capabilities.supportsLoadedSourcesRequest); - // @weinand remove the next line which always disables the context for the view to be shown - this.loadedScriptsSupportedContextKey.set(false); if (shouldEmit) { this._onDidFocusStackFrame.fire({ stackFrame, explicit }); } } - public get onDidFocusSession(): Event<ISession> { + get onDidFocusSession(): Event<ISession> { return this._onDidFocusSession.event; } - public get onDidFocusStackFrame(): Event<{ stackFrame: IStackFrame, explicit: boolean }> { + get onDidFocusStackFrame(): Event<{ stackFrame: IStackFrame, explicit: boolean }> { return this._onDidFocusStackFrame.event; } - public getSelectedExpression(): IExpression { + getSelectedExpression(): IExpression { return this.selectedExpression; } - public setSelectedExpression(expression: IExpression) { + setSelectedExpression(expression: IExpression) { this.selectedExpression = expression; this.expressionSelectedContextKey.set(!!expression); this._onDidSelectExpression.fire(expression); } - public get onDidSelectExpression(): Event<IExpression> { + get onDidSelectExpression(): Event<IExpression> { return this._onDidSelectExpression.event; } - public getSelectedFunctionBreakpoint(): IFunctionBreakpoint { + getSelectedFunctionBreakpoint(): IFunctionBreakpoint { return this.selectedFunctionBreakpoint; } - public setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint): void { + setSelectedFunctionBreakpoint(functionBreakpoint: IFunctionBreakpoint): void { this.selectedFunctionBreakpoint = functionBreakpoint; this.breakpointSelectedContextKey.set(!!functionBreakpoint); } - public isMultiSessionView(): boolean { + isMultiSessionView(): boolean { return this.multiSessionView; } - public setMultiSessionView(isMultiSessionView: boolean): void { + setMultiSessionView(isMultiSessionView: boolean): void { this.multiSessionView = isMultiSessionView; } } diff --git a/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts b/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts index c3f164601c0..fe86cf067fd 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'; @@ -34,6 +33,8 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { getSimpleCodeEditorWidgetOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions'; +import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/browser/simpleEditorOptions'; const $ = dom.$; const IPrivateBreakpointWidgetService = createDecorator<IPrivateBreakpointWidgetService>('privateBreakopintWidgetService'); @@ -200,8 +201,8 @@ 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}:${this.editor.getId()}:breakpointinput`), true); diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 3868462dbe7..19e41a6c3c3 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDebugService, State, IStackFrame, ISession, IThread, CONTEXT_CALLSTACK_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug'; -import { Thread, StackFrame, ThreadAndSessionIds, Session, Model } from 'vs/workbench/parts/debug/common/debugModel'; +import { Thread, StackFrame, ThreadAndSessionIds, Model } from 'vs/workbench/parts/debug/common/debugModel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MenuId } from 'vs/platform/actions/common/actions'; @@ -27,8 +27,8 @@ 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 { ILabelService } from 'vs/platform/label/common/label'; +import { Session } from 'vs/workbench/parts/debug/electron-browser/debugSession'; const $ = dom.$; @@ -399,7 +399,7 @@ class CallStackRenderer implements IRenderer { constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IEnvironmentService private environmentService: IEnvironmentService + @ILabelService private labelService: ILabelService ) { // noop } @@ -518,8 +518,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.labelService.getUriLabel(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 816bea9c85c..7b3dfae36f1 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -113,8 +113,8 @@ Registry.as<PanelRegistry>(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 }]); +ViewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctor: LoadedScriptsView, order: 50, weight: 5, container: VIEW_CONTAINER, canToggleVisibility: true, collapsed: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }]); // register action to open viewlet const registry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionRegistryExtensions.WorkbenchActions); @@ -178,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.") + default: 'openOnSessionStart', + 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': { @@ -358,40 +358,47 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { order: 1 }); -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', +MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', command: { id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, - title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Conditional Breakpoint...") + 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.MenubarDebugMenu, { - group: '4_new_breakpoint', +MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', command: { - id: TOGGLE_INLINE_BREAKPOINT_ID, - title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle Inline Breakp&&oint") + id: AddFunctionBreakpointAction.ID, + title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint...") }, order: 3 }); -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', +MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', command: { - id: AddFunctionBreakpointAction.ID, - title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Function Breakpoint...") + 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', - command: { - id: TOGGLE_LOG_POINT_ID, - title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Logpoint...") - }, - order: 5 + title: nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint"), + submenu: MenuId.MenubarNewBreakpointMenu, + order: 2 }); // Modify Breakpoints @@ -422,6 +429,16 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { 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..2640da6c03a 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'; @@ -25,7 +25,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IAdapterExecutable, IDebugAdapterProvider, IDebugAdapter, ITerminalSettings, ITerminalLauncher } from 'vs/workbench/parts/debug/common/debug'; import { Debugger } from 'vs/workbench/parts/debug/node/debugger'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; @@ -33,6 +32,7 @@ import { TerminalLauncher } from 'vs/workbench/parts/debug/electron-browser/term import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/parts/debug/common/debugSchemas'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -57,7 +57,7 @@ export class ConfigurationManager implements IConfigurationManager { @IWorkspaceContextService private contextService: IWorkspaceContextService, @IEditorService private editorService: IEditorService, @IConfigurationService private configurationService: IConfigurationService, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IInstantiationService private instantiationService: IInstantiationService, @ICommandService private commandService: ICommandService, @IStorageService private storageService: IStorageService, @@ -347,10 +347,11 @@ export class ConfigurationManager implements IConfigurationManager { } candidates = candidates.sort((first, second) => first.label.localeCompare(second.label)); - return this.quickOpenService.pick([...candidates, { label: 'More...', separator: { border: true } }], { placeHolder: nls.localize('selectDebug', "Select Environment") }) + const picks = candidates.map(c => ({ label: c.label, debugger: c })); + return this.quickInputService.pick<(typeof picks)[0]>([...picks, { type: 'separator' }, { label: 'More...', debugger: undefined }], { placeHolder: nls.localize('selectDebug', "Select Environment") }) .then(picked => { - if (picked instanceof Debugger) { - return picked; + if (picked && picked.debugger) { + return picked.debugger; } if (picked) { this.commandService.executeCommand('debug.installAdditionalDebuggers'); @@ -390,7 +391,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 { @@ -441,15 +442,13 @@ class Launch implements ILaunch { return config.configurations.filter(config => config && config.name === name).shift(); } - 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 + public openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + 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 +469,31 @@ 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, + preserveFocus, + 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)); }); } } @@ -522,8 +522,11 @@ class WorkspaceLaunch extends Launch implements ILaunch { return this.configurationService.inspect<IGlobalConfig>('launch').workspace; } - openConfigFile(sideBySide: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { - return this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration }).then(editor => ({ editor, created: false })); + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + return this.editorService.openEditor({ + resource: this.contextService.getWorkspace().configuration, + options: { preserveFocus } + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created: false })); } } @@ -556,7 +559,7 @@ class UserLaunch extends Launch implements ILaunch { return this.configurationService.inspect<IGlobalConfig>('launch').user; } - openConfigFile(sideBySide: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { - return this.preferencesService.openGlobalSettings().then(editor => ({ editor, created: false })); + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor, created: false })); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 637e1aed9df..7c923be3ad0 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -4,13 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as lifecycle from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as resources from 'vs/base/common/resources'; -import * as strings from 'vs/base/common/strings'; import { generateUuid } from 'vs/base/common/uuid'; import uri from 'vs/base/common/uri'; -import * as platform from 'vs/base/common/platform'; import { first, distinct } from 'vs/base/common/arrays'; import { isObject, isUndefinedOrNull } from 'vs/base/common/types'; import * as errors from 'vs/base/common/errors'; @@ -26,9 +23,8 @@ import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/file import { IWindowService } from 'vs/platform/windows/common/windows'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import * as debug from 'vs/workbench/parts/debug/common/debug'; import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession'; -import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement, ExpressionContainer, Session, Thread } from 'vs/workbench/parts/debug/common/debugModel'; +import { Model, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, RawObjectReplElement } from 'vs/workbench/parts/debug/common/debugModel'; import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel'; import * as debugactions from 'vs/workbench/parts/debug/browser/debugActions'; import { ConfigurationManager } from 'vs/workbench/parts/debug/electron-browser/debugConfigurationManager'; @@ -43,7 +39,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; +import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/node/console'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; @@ -52,9 +48,11 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IAction, Action } from 'vs/base/common/actions'; import { normalizeDriveLetter } from 'vs/base/common/labels'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import product from 'vs/platform/node/product'; import { deepClone, equals } from 'vs/base/common/objects'; +import { Session } from 'vs/workbench/parts/debug/electron-browser/debugSession'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { IDebugService, State, ISession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IModel, IReplElementSource, IEnablement, IBreakpoint, IBreakpointData, IExpression, ICompound, IGlobalConfig, IStackFrame } from 'vs/workbench/parts/debug/common/debug'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated'; @@ -62,27 +60,24 @@ const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint'; const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions'; -export class DebugService implements debug.IDebugService { - public _serviceBrand: any; +export class DebugService implements IDebugService { + _serviceBrand: any; - private sessionStates: Map<string, debug.State>; - private readonly _onDidChangeState: Emitter<debug.State>; - private readonly _onDidNewSession: Emitter<debug.ISession>; - private readonly _onDidEndSession: Emitter<debug.ISession>; - private readonly _onDidCustomEvent: Emitter<debug.DebugEvent>; + private readonly _onDidChangeState: Emitter<State>; + private readonly _onDidNewSession: Emitter<ISession>; + private readonly _onDidEndSession: Emitter<ISession>; private model: Model; private viewModel: ViewModel; - private allSessions: Map<string, debug.ISession>; private configurationManager: ConfigurationManager; - private toDispose: lifecycle.IDisposable[]; - private toDisposeOnSessionEnd: Map<string, lifecycle.IDisposable[]>; + private allSessions = new Map<string, ISession>(); + private toDispose: IDisposable[]; private debugType: IContextKey<string>; private debugState: IContextKey<string>; + private inDebugMode: IContextKey<boolean>; private breakpointsToSendOnResourceSaved: Set<string>; - private firstSessionStart: boolean; private skipRunningTask: boolean; - private previousState: debug.State; - private fetchThreadsSchedulers: Map<string, RunOnceScheduler>; + private initializing = false; + private previousState: State; constructor( @IStorageService private storageService: IStorageService, @@ -107,26 +102,21 @@ export class DebugService implements debug.IDebugService { @IConfigurationService private configurationService: IConfigurationService, ) { this.toDispose = []; - this.toDisposeOnSessionEnd = new Map<string, lifecycle.IDisposable[]>(); this.breakpointsToSendOnResourceSaved = new Set<string>(); - this._onDidChangeState = new Emitter<debug.State>(); - this._onDidNewSession = new Emitter<debug.ISession>(); - this._onDidEndSession = new Emitter<debug.ISession>(); - this._onDidCustomEvent = new Emitter<debug.DebugEvent>(); - this.sessionStates = new Map<string, debug.State>(); - this.allSessions = new Map<string, debug.ISession>(); - this.fetchThreadsSchedulers = new Map<string, RunOnceScheduler>(); + this._onDidChangeState = new Emitter<State>(); + this._onDidNewSession = new Emitter<ISession>(); + this._onDidEndSession = new Emitter<ISession>(); this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); this.toDispose.push(this.configurationManager); - this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); - this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); + this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + this.inDebugMode = 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()); + this.loadExceptionBreakpoints(), this.loadWatchExpressions(), this.textFileService); this.toDispose.push(this.model); this.viewModel = new ViewModel(contextKeyService); - this.firstSessionStart = true; this.registerListeners(); } @@ -139,42 +129,32 @@ export class DebugService implements debug.IDebugService { this.toDispose.push(this.viewModel.onDidFocusSession(s => { const id = s ? s.getId() : undefined; this.model.setBreakpointsSessionId(id); + this.onStateChange(); })); } private onBroadcast(broadcast: IBroadcast): void { - // attach: PH is ready to be attached to const session = <Session>this.allSessions.get(broadcast.payload.debugId); if (!session) { // Ignore attach events for sessions that never existed (wrong vscode windows) return; } - const raw = <RawDebugSession>session.raw; if (broadcast.channel === EXTENSION_ATTACH_BROADCAST_CHANNEL) { - const initialAttach = session.configuration.request === 'launch'; - session.configuration.request = 'attach'; session.configuration.port = broadcast.payload.port; - // Do not end process on initial attach (since the request is still 'launch') - if (initialAttach) { - const root = raw.root; - lifecycle.dispose(this.toDisposeOnSessionEnd[raw.getId()]); - this.initializeRawSession(root, { resolved: session.configuration, unresolved: session.unresolvedConfiguration }, session.getId(), session).then(session => { - (<RawDebugSession>session.raw).attach(session.configuration); - }); - } else { - const root = raw.root; - raw.dispose(); - this.doCreateSession(root, { resolved: session.configuration, unresolved: session.unresolvedConfiguration }, session.getId()); - } + const dbgr = this.configurationManager.getDebugger(session.configuration.type); + session.initialize(dbgr).then(() => { + (<RawDebugSession>session.raw).attach(session.configuration); + this.focusStackFrame(undefined, undefined, session); + }); return; } if (broadcast.channel === EXTENSION_TERMINATE_BROADCAST_CHANNEL) { - raw.terminate().done(undefined, errors.onUnexpectedError); + session.raw.terminate().done(undefined, errors.onUnexpectedError); return; } @@ -184,7 +164,7 @@ export class DebugService implements debug.IDebugService { let sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info; const { args, stack } = parse(extensionOutput); - let source: debug.IReplElementSource; + let source: IReplElementSource; if (stack) { const frame = getFirstFrame(stack); if (frame) { @@ -259,7 +239,7 @@ export class DebugService implements debug.IDebugService { } } - private tryToAutoFocusStackFrame(thread: debug.IThread): TPromise<any> { + tryToAutoFocusStackFrame(thread: IThread): TPromise<any> { const callStack = thread.getCallStack(); if (!callStack.length || (this.viewModel.focusedStackFrame && this.viewModel.focusedStackFrame.thread.getId() === thread.getId())) { return TPromise.as(null); @@ -273,8 +253,8 @@ export class DebugService implements debug.IDebugService { this.focusStackFrame(stackFrameToFocus); if (thread.stoppedDetails) { - if (this.configurationService.getValue<debug.IDebugConfiguration>('debug').openDebug === 'openOnDebugBreak') { - this.viewletService.openViewlet(debug.VIEWLET_ID).done(undefined, errors.onUnexpectedError); + if (this.configurationService.getValue<IDebugConfiguration>('debug').openDebug === 'openOnDebugBreak') { + this.viewletService.openViewlet(VIEWLET_ID).done(undefined, errors.onUnexpectedError); } this.windowService.focusWindow(); aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrameToFocus.source ? stackFrameToFocus.source.name : '', stackFrameToFocus.range.startLineNumber)); @@ -283,196 +263,11 @@ export class DebugService implements debug.IDebugService { return stackFrameToFocus.openInEditor(this.editorService, true); } - private registerSessionListeners(session: debug.ISession, raw: RawDebugSession): void { - this.toDisposeOnSessionEnd.get(raw.getId()).push(raw.onDidInitialize(event => { - aria.status(nls.localize('debuggingStarted', "Debugging started.")); - const sendConfigurationDone = () => { - if (raw && raw.capabilities.supportsConfigurationDoneRequest) { - return raw.configurationDone().done(null, e => { - // Disconnect the debug session on configuration done error #10596 - if (raw) { - raw.dispose(); - } - this.notificationService.error(e.message); - }); - } - }; - - this.sendAllBreakpoints(session).then(sendConfigurationDone, sendConfigurationDone) - .done(() => this.fetchThreads(raw), errors.onUnexpectedError); - })); - - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidStop(event => { - this.updateStateAndEmit(session.getId(), debug.State.Stopped); - this.fetchThreads(raw, event.body).done(() => { - const thread = session && session.getThread(event.body.threadId); - if (thread) { - // Call fetch call stack twice, the first only return the top stack frame. - // Second retrieves the rest of the call stack. For performance reasons #25605 - this.model.fetchCallStack(<Thread>thread).then(() => { - return !event.body.preserveFocusHint ? this.tryToAutoFocusStackFrame(thread) : undefined; - }); - } - }, errors.onUnexpectedError); - })); - - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidThread(event => { - if (event.body.reason === 'started') { - // debounce to reduce threadsRequest frequency and improve performance - let scheduler = this.fetchThreadsSchedulers.get(session.getId()); - if (!scheduler) { - scheduler = new RunOnceScheduler(() => { - this.fetchThreads(raw).done(undefined, errors.onUnexpectedError); - }, 100); - this.fetchThreadsSchedulers.set(session.getId(), scheduler); - this.toDisposeOnSessionEnd.get(session.getId()).push(scheduler); - } - if (!scheduler.isScheduled()) { - scheduler.schedule(); - } - } else if (event.body.reason === 'exited') { - this.model.clearThreads(session.getId(), true, event.body.threadId); - } - })); - - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidTerminateDebugee(event => { - aria.status(nls.localize('debuggingStopped', "Debugging stopped.")); - if (session && session.getId() === event.sessionId) { - if (event.body && event.body.restart && session) { - this.restartSession(session, event.body.restart).done(null, err => this.notificationService.error(err.message)); - } else { - raw.dispose(); - } - } - })); - - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidContinued(event => { - const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId; - this.model.clearThreads(session.getId(), false, threadId); - if (this.viewModel.focusedSession.getId() === session.getId()) { - this.focusStackFrame(undefined, this.viewModel.focusedThread, this.viewModel.focusedSession); - } - this.updateStateAndEmit(session.getId(), debug.State.Running); - })); - - let outputPromises: TPromise<void>[] = []; - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidOutput(event => { - if (!event.body) { - return; - } - - const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info; - if (event.body.category === 'telemetry') { - // only log telemetry events from debug adapter if the debug extension provided the telemetry key - // and the user opted in telemetry - if (raw.customTelemetryService && this.telemetryService.isOptedIn) { - // __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly. - raw.customTelemetryService.publicLog(event.body.output, event.body.data); - } - - return; - } - - // Make sure to append output in the correct order by properly waiting on preivous promises #33822 - const waitFor = outputPromises.slice(); - const source = event.body.source ? { - lineNumber: event.body.line, - column: event.body.column ? event.body.column : 1, - source: session.getSource(event.body.source) - } : undefined; - if (event.body.variablesReference) { - const container = new ExpressionContainer(session, event.body.variablesReference, generateUuid()); - outputPromises.push(container.getChildren().then(children => { - return TPromise.join(waitFor).then(() => children.forEach(child => { - // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) - child.name = null; - this.logToRepl(child, outputSeverity, source); - })); - })); - } else if (typeof event.body.output === 'string') { - TPromise.join(waitFor).then(() => this.logToRepl(event.body.output, outputSeverity, source)); - } - TPromise.join(outputPromises).then(() => outputPromises = []); - })); - - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidBreakpoint(event => { - const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined; - const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop(); - const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop(); - - if (event.body.reason === 'new' && event.body.breakpoint.source) { - const source = session.getSource(event.body.breakpoint.source); - const bps = this.model.addBreakpoints(source.uri, [{ - column: event.body.breakpoint.column, - enabled: true, - lineNumber: event.body.breakpoint.line, - }], false); - if (bps.length === 1) { - this.model.updateBreakpoints({ [bps[0].getId()]: event.body.breakpoint }); - } - } - - if (event.body.reason === 'removed') { - if (breakpoint) { - this.model.removeBreakpoints([breakpoint]); - } - if (functionBreakpoint) { - this.model.removeFunctionBreakpoints(functionBreakpoint.getId()); - } - } - - if (event.body.reason === 'changed') { - if (breakpoint) { - if (!breakpoint.column) { - event.body.breakpoint.column = undefined; - } - this.model.setBreakpointSessionData(session.getId(), { [breakpoint.getId()]: event.body.breakpoint }); - } - if (functionBreakpoint) { - this.model.setBreakpointSessionData(session.getId(), { [functionBreakpoint.getId()]: event.body.breakpoint }); - } - } - })); - - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidExitAdapter(event => { - // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 - if (strings.equalsIgnoreCase(session.configuration.type, 'extensionhost') && this.sessionStates.get(session.getId()) === debug.State.Running && - session && session.raw.root && session.configuration.noDebug) { - this.broadcastService.broadcast({ - channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, - payload: [session.raw.root.uri.fsPath] - }); - } - if (session && session.getId() === event.sessionId) { - this.onExitAdapter(raw); - } - })); - - this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidCustomEvent(event => { - this._onDidCustomEvent.fire(event); - })); - } - - private fetchThreads(session: RawDebugSession, stoppedDetails?: debug.IRawStoppedDetails): TPromise<any> { - return session.threads().then(response => { - if (response && response.body && response.body.threads) { - response.body.threads.forEach(thread => { - this.model.rawUpdate({ - sessionId: session.getId(), - threadId: thread.id, - thread, - stoppedDetails: stoppedDetails && thread.id === stoppedDetails.threadId ? stoppedDetails : undefined - }); - }); - } - }); - } - private loadBreakpoints(): Breakpoint[] { let result: Breakpoint[]; try { result = JSON.parse(this.storageService.get(DEBUG_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((breakpoint: any) => { - return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.logMessage, breakpoint.adapterData); + return new Breakpoint(uri.parse(breakpoint.uri.external || breakpoint.source.uri.external), breakpoint.lineNumber, breakpoint.column, breakpoint.enabled, breakpoint.condition, breakpoint.hitCondition, breakpoint.logMessage, breakpoint.adapterData, this.textFileService); }); } catch (e) { } @@ -512,59 +307,31 @@ export class DebugService implements debug.IDebugService { return result || []; } - public get state(): debug.State { - const focusedThread = this.viewModel.focusedThread; - if (focusedThread && focusedThread.stopped) { - return debug.State.Stopped; - } + get state(): State { const focusedSession = this.viewModel.focusedSession; - if (focusedSession && this.sessionStates.has(focusedSession.getId())) { - return this.sessionStates.get(focusedSession.getId()); + if (focusedSession) { + return focusedSession.state; } - if (this.sessionStates.size > 0) { - return debug.State.Initializing; + if (this.initializing) { + return State.Initializing; } - return debug.State.Inactive; + return State.Inactive; } - public get onDidChangeState(): Event<debug.State> { + get onDidChangeState(): Event<State> { return this._onDidChangeState.event; } - public get onDidNewSession(): Event<debug.ISession> { + get onDidNewSession(): Event<ISession> { return this._onDidNewSession.event; } - public get onDidEndSession(): Event<debug.ISession> { + get onDidEndSession(): Event<ISession> { return this._onDidEndSession.event; } - public get onDidCustomEvent(): Event<debug.DebugEvent> { - return this._onDidCustomEvent.event; - } - - private updateStateAndEmit(sessionId?: string, newState?: debug.State): void { - if (sessionId) { - if (newState === debug.State.Inactive) { - this.sessionStates.delete(sessionId); - } else { - this.sessionStates.set(sessionId, newState); - } - } - - const state = this.state; - if (this.previousState !== state) { - const stateLabel = debug.State[state]; - if (stateLabel) { - this.debugState.set(stateLabel.toLowerCase()); - } - this.previousState = state; - this._onDidChangeState.fire(state); - } - } - - public focusStackFrame(stackFrame: debug.IStackFrame, thread?: debug.IThread, session?: debug.ISession, explicit?: boolean): void { + focusStackFrame(stackFrame: IStackFrame, thread?: IThread, session?: ISession, explicit?: boolean): void { if (!session) { if (stackFrame || thread) { session = stackFrame ? stackFrame.thread.session : thread.session; @@ -591,10 +358,9 @@ export class DebugService implements debug.IDebugService { } this.viewModel.setFocus(stackFrame, thread, session, explicit); - this.updateStateAndEmit(); } - public enableOrDisableBreakpoints(enable: boolean, breakpoint?: debug.IEnablement): TPromise<void> { + enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): TPromise<void> { if (breakpoint) { this.model.setEnablement(breakpoint, enable); if (breakpoint instanceof Breakpoint) { @@ -610,14 +376,14 @@ export class DebugService implements debug.IDebugService { return this.sendAllBreakpoints(); } - public addBreakpoints(uri: uri, rawBreakpoints: debug.IBreakpointData[]): TPromise<debug.IBreakpoint[]> { + addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise<IBreakpoint[]> { const breakpoints = this.model.addBreakpoints(uri, rawBreakpoints); breakpoints.forEach(bp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", bp.lineNumber, uri.fsPath))); return this.sendBreakpoints(uri).then(() => breakpoints); } - public updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }, sendOnResourceSaved: boolean): void { + updateBreakpoints(uri: uri, data: { [id: string]: DebugProtocol.Breakpoint }, sendOnResourceSaved: boolean): void { this.model.updateBreakpoints(data); if (sendOnResourceSaved) { this.breakpointsToSendOnResourceSaved.add(uri.toString()); @@ -626,7 +392,7 @@ export class DebugService implements debug.IDebugService { } } - public removeBreakpoints(id?: string): TPromise<any> { + removeBreakpoints(id?: string): TPromise<any> { const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id); toRemove.forEach(bp => aria.status(nls.localize('breakpointRemoved', "Removed breakpoint, line {0}, file {1}", bp.lineNumber, bp.uri.fsPath))); const urisToClear = distinct(toRemove, bp => bp.uri.toString()).map(bp => bp.uri); @@ -636,37 +402,37 @@ export class DebugService implements debug.IDebugService { return TPromise.join(urisToClear.map(uri => this.sendBreakpoints(uri))); } - public setBreakpointsActivated(activated: boolean): TPromise<void> { + setBreakpointsActivated(activated: boolean): TPromise<void> { this.model.setBreakpointsActivated(activated); return this.sendAllBreakpoints(); } - public addFunctionBreakpoint(name?: string, id?: string): void { + addFunctionBreakpoint(name?: string, id?: string): void { const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id); this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint); } - public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> { + renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise<void> { this.model.renameFunctionBreakpoint(id, newFunctionName); return this.sendFunctionBreakpoints(); } - public removeFunctionBreakpoints(id?: string): TPromise<void> { + removeFunctionBreakpoints(id?: string): TPromise<void> { this.model.removeFunctionBreakpoints(id); return this.sendFunctionBreakpoints(); } - public addReplExpression(name: string): TPromise<void> { + addReplExpression(name: string): TPromise<void> { return this.model.addReplExpression(this.viewModel.focusedSession, this.viewModel.focusedStackFrame, name) // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. .then(() => this.focusStackFrame(this.viewModel.focusedStackFrame, this.viewModel.focusedThread, this.viewModel.focusedSession)); } - public removeReplExpressions(): void { + removeReplExpressions(): void { this.model.removeReplExpressions(); } - public logToRepl(value: string | debug.IExpression, sev = severity.Info, source?: debug.IReplElementSource): void { + logToRepl(value: string | IExpression, sev = severity.Info, source?: IReplElementSource): void { const clearAnsiSequence = '\u001b[2J'; if (typeof value === 'string' && value.indexOf(clearAnsiSequence) >= 0) { // [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php @@ -678,31 +444,25 @@ export class DebugService implements debug.IDebugService { this.model.appendToRepl(value, sev, source); } - public addWatchExpression(name: string): void { + addWatchExpression(name: string): void { const we = this.model.addWatchExpression(name); this.viewModel.setSelectedExpression(we); } - public renameWatchExpression(id: string, newName: string): void { + renameWatchExpression(id: string, newName: string): void { return this.model.renameWatchExpression(id, newName); } - public moveWatchExpression(id: string, position: number): void { + moveWatchExpression(id: string, position: number): void { this.model.moveWatchExpression(id, position); } - public removeWatchExpressions(id?: string): void { + removeWatchExpressions(id?: string): void { this.model.removeWatchExpressions(id); } - public startDebugging(launch: debug.ILaunch, configOrName?: debug.IConfig | string, noDebug = false, unresolvedConfiguration?: debug.IConfig, ): TPromise<void> { + startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug = false, unresolvedConfiguration?: IConfig, ): TPromise<void> { const sessionId = generateUuid(); - this.updateStateAndEmit(sessionId, debug.State.Initializing); - const wrapUpState = () => { - if (this.sessionStates.get(sessionId) === debug.State.Initializing) { - this.updateStateAndEmit(sessionId, debug.State.Inactive); - } - }; // make sure to save all files and that the configuration is up to date return this.extensionService.activateByEvent('onDebug').then(() => this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() => @@ -712,7 +472,7 @@ export class DebugService implements debug.IDebugService { this.allSessions.clear(); } - let config: debug.IConfig, compound: debug.ICompound; + let config: IConfig, compound: ICompound; if (!configOrName) { configOrName = this.configurationManager.selectedConfiguration.name; } @@ -722,7 +482,7 @@ export class DebugService implements debug.IDebugService { const sessions = this.model.getSessions(); const alreadyRunningMessage = nls.localize('configurationAlreadyRunning', "There is already a debug configuration \"{0}\" running.", configOrName); - if (sessions.some(p => p.getName(false) === configOrName && (!launch || !launch.workspace || !p.raw.root || p.raw.root.uri.toString() === launch.workspace.uri.toString()))) { + if (sessions.some(s => s.getName(false) === configOrName && (!launch || !launch.workspace || !s.root || s.root.uri.toString() === launch.workspace.uri.toString()))) { return TPromise.wrapError(new Error(alreadyRunningMessage)); } if (compound && compound.configurations && sessions.some(p => compound.configurations.indexOf(p.getName(false)) !== -1)) { @@ -744,7 +504,7 @@ export class DebugService implements debug.IDebugService { return TPromise.as(null); } - let launchForName: debug.ILaunch; + let launchForName: ILaunch; if (typeof configData === 'string') { const launchesContainingName = this.configurationManager.getLaunches().filter(l => !!l.getConfiguration(name)); if (launchesContainingName.length === 1) { @@ -781,7 +541,7 @@ export class DebugService implements debug.IDebugService { type = config.type; } else { // a no-folder workspace has no launch.config - config = <debug.IConfig>{}; + config = <IConfig>{}; } unresolvedConfiguration = unresolvedConfiguration || deepClone(config); @@ -797,18 +557,15 @@ export class DebugService implements debug.IDebugService { } if (launch && type) { - return launch.openConfigFile(false, type).done(undefined, errors.onUnexpectedError); + return launch.openConfigFile(false, true, type).done(undefined, errors.onUnexpectedError); } }) ).then(() => undefined); }) - ))).then(() => wrapUpState(), err => { - wrapUpState(); - return TPromise.wrapError(err); - }); + ))); } - private substituteVariables(launch: debug.ILaunch | undefined, config: debug.IConfig): TPromise<debug.IConfig> { + private substituteVariables(launch: ILaunch | undefined, config: IConfig): TPromise<IConfig> { const dbg = this.configurationManager.getDebugger(config.type); if (dbg) { let folder: IWorkspaceFolder = undefined; @@ -830,7 +587,9 @@ export class DebugService implements debug.IDebugService { return TPromise.as(config); } - private createSession(launch: debug.ILaunch, config: debug.IConfig, unresolvedConfig: debug.IConfig, sessionId: string): TPromise<void> { + private createSession(launch: ILaunch, config: IConfig, unresolvedConfig: IConfig, sessionId: string): TPromise<void> { + this.initializing = true; + this.onStateChange(); return this.textFileService.saveAll().then(() => this.substituteVariables(launch, config).then(resolvedConfig => { @@ -853,12 +612,14 @@ export class DebugService implements debug.IDebugService { return this.showError(message); } - this.toDisposeOnSessionEnd.set(sessionId, []); - const workspace = launch ? launch.workspace : undefined; - return this.runTask(sessionId, workspace, resolvedConfig.preLaunchTask, resolvedConfig, unresolvedConfig, - () => this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, sessionId) - ); + return this.runTask(sessionId, workspace, resolvedConfig.preLaunchTask, resolvedConfig, unresolvedConfig).then(success => { + if (!success) { + return undefined; + } + + return this.doCreateSession(workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, sessionId); + }); }, err => { if (err && err.message) { return this.showError(err.message); @@ -867,49 +628,24 @@ export class DebugService implements debug.IDebugService { return this.showError(nls.localize('noFolderWorkspaceDebugError', "The active file can not be debugged. Make sure it is saved on disk and that you have a debug extension installed for that file type.")); } - return launch && launch.openConfigFile(false).then(editor => void 0); + return launch && launch.openConfigFile(false, true).then(editor => void 0); }) - ); - } - - private initializeRawSession(root: IWorkspaceFolder, configuration: { resolved: debug.IConfig, unresolved: debug.IConfig }, sessionId: string, session?: Session): TPromise<Session> { - const dbg = this.configurationManager.getDebugger(configuration.resolved.type); - return dbg.getCustomTelemetryService().then(customTelemetryService => { - - const raw = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.resolved.debugServer, dbg, customTelemetryService, root); - if (!session) { - session = this.model.addSession(configuration, raw); - this.allSessions.set(session.getId(), session); - } else { - session.raw = raw; - } - this.registerSessionListeners(session, raw); - - return raw.initialize({ - clientID: 'vscode', - clientName: product.nameLong, - adapterID: configuration.resolved.type, - pathFormat: 'path', - linesStartAt1: true, - columnsStartAt1: true, - supportsVariableType: true, // #8858 - supportsVariablePaging: true, // #9537 - supportsRunInTerminalRequest: true, // #10574 - locale: platform.locale - }).then((result: DebugProtocol.InitializeResponse) => { - this.model.setExceptionBreakpoints(raw.capabilities.exceptionBreakpointFilters); - return session; - }); + ).then(() => { + this.initializing = false; + this.onStateChange(); }); } - private doCreateSession(root: IWorkspaceFolder, configuration: { resolved: debug.IConfig, unresolved: debug.IConfig }, sessionId: string): TPromise<debug.ISession> { + private doCreateSession(root: IWorkspaceFolder, configuration: { resolved: IConfig, unresolved: IConfig }, sessionId: string): TPromise<any> { const resolved = configuration.resolved; resolved.__sessionId = sessionId; - const dbg = this.configurationManager.getDebugger(resolved.type); - return this.initializeRawSession(root, configuration, sessionId).then(session => { + const dbgr = this.configurationManager.getDebugger(resolved.type); + const session = this.instantiationService.createInstance(Session, sessionId, configuration, root, this.model); + this.allSessions.set(sessionId, session); + return session.initialize(dbgr).then(() => { + this.registerSessionListeners(session); const raw = <RawDebugSession>session.raw; return (resolved.request === 'attach' ? raw.attach(resolved) : raw.launch(resolved)) .then((result: DebugProtocol.Response) => { @@ -919,23 +655,22 @@ export class DebugService implements debug.IDebugService { this.focusStackFrame(undefined, undefined, session); this._onDidNewSession.fire(session); - const internalConsoleOptions = resolved.internalConsoleOptions || this.configurationService.getValue<debug.IDebugConfiguration>('debug').internalConsoleOptions; - if (internalConsoleOptions === 'openOnSessionStart' || (this.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) { - this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError); + const internalConsoleOptions = resolved.internalConsoleOptions || this.configurationService.getValue<IDebugConfiguration>('debug').internalConsoleOptions; + if (internalConsoleOptions === 'openOnSessionStart' || (this.viewModel.firstSessionStart && internalConsoleOptions === 'openOnFirstSessionStart')) { + this.panelService.openPanel(REPL_ID, false).done(undefined, errors.onUnexpectedError); } - const openDebug = this.configurationService.getValue<debug.IDebugConfiguration>('debug').openDebug; + const openDebug = this.configurationService.getValue<IDebugConfiguration>('debug').openDebug; // Open debug viewlet based on the visibility of the side bar and openDebug setting - if (openDebug === 'openOnSessionStart' || (openDebug === 'openOnFirstSessionStart' && this.firstSessionStart)) { - this.viewletService.openViewlet(debug.VIEWLET_ID); + if (openDebug === 'openOnSessionStart' || (openDebug === 'openOnFirstSessionStart' && this.viewModel.firstSessionStart)) { + this.viewletService.openViewlet(VIEWLET_ID); } - this.firstSessionStart = false; + this.viewModel.firstSessionStart = false; this.debugType.set(resolved.type); if (this.model.getSessions().length > 1) { this.viewModel.setMultiSessionView(true); } - this.updateStateAndEmit(raw.getId(), debug.State.Running); /* __GDPR__ "debugSessionStart" : { @@ -953,11 +688,15 @@ export class DebugService implements debug.IDebugService { breakpointCount: this.model.getBreakpoints().length, exceptionBreakpoints: this.model.getExceptionBreakpoints(), watchExpressionsCount: this.model.getWatchExpressions().length, - extensionName: dbg.extensionDescription.id, - isBuiltin: dbg.extensionDescription.isBuiltin, - launchJsonExists: root && !!this.configurationService.getValue<debug.IGlobalConfig>('launch', { resource: root.uri }) + extensionName: dbgr.extensionDescription.id, + isBuiltin: dbgr.extensionDescription.isBuiltin, + launchJsonExists: root && !!this.configurationService.getValue<IGlobalConfig>('launch', { resource: root.uri }) }); }).then(() => session, (error: Error | string) => { + if (session) { + session.dispose(); + } + if (errors.isPromiseCanceledError(error)) { // Do not show 'canceled' error messages to the user #7906 return TPromise.as(null); @@ -971,24 +710,102 @@ 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(); - } else if (session) { - this.model.removeSession(session.getId()); - } // Show the repl if some error got logged there #5870 if (this.model.getReplElements().length > 0) { - this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError); + this.panelService.openPanel(REPL_ID, false).done(undefined, errors.onUnexpectedError); } - this.showError(errorMessage, errors.isErrorWithActions(error) ? error.actions : []); + if (resolved && resolved.request === 'attach' && resolved.__autoAttach) { + // ignore attach timeouts in auto attach mode + } else { + this.showError(errorMessage, errors.isErrorWithActions(error) ? error.actions : []); + } return undefined; }); + }).then(undefined, err => { + if (session) { + session.dispose(); + } + + return TPromise.wrapError(err); }); } + private onStateChange(): void { + const state = this.state; + if (this.previousState !== state) { + const stateLabel = State[state]; + if (stateLabel) { + this.debugState.set(stateLabel.toLowerCase()); + this.inDebugMode.set(state !== State.Inactive); + } + this.previousState = state; + this._onDidChangeState.fire(state); + } + } + + private registerSessionListeners(session: Session): void { + this.toDispose.push(session.onDidChangeState((state) => { + if (state === State.Running && this.viewModel.focusedSession && this.viewModel.focusedSession.getId() === session.getId()) { + this.focusStackFrame(undefined); + } + this.onStateChange(); + })); + + this.toDispose.push(session.onDidExitAdapter(() => { + // 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905 + if (equalsIgnoreCase(session.configuration.type, 'extensionhost') && session.state === State.Running && session.configuration.noDebug) { + this.broadcastService.broadcast({ + channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, + payload: [session.root.uri.toString()] + }); + } + + + const breakpoints = this.model.getBreakpoints(); + /* __GDPR__ + "debugSessionStop" : { + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "success": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "sessionLengthInSeconds": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('debugSessionStop', { + type: session && session.configuration.type, + success: (<RawDebugSession>session.raw).emittedStopped || breakpoints.length === 0, + sessionLengthInSeconds: (<RawDebugSession>session.raw).getLengthInSeconds(), + breakpointCount: breakpoints.length, + watchExpressionsCount: this.model.getWatchExpressions().length + }); + + if (session.configuration.postDebugTask) { + this.doRunTask(session.getId(), session.root, session.configuration.postDebugTask).done(undefined, err => + this.notificationService.error(err) + ); + } + session.dispose(); + this._onDidEndSession.fire(session); + + const focusedSession = this.viewModel.focusedSession; + if (focusedSession && focusedSession.getId() === session.getId()) { + this.focusStackFrame(undefined); + } + + if (this.model.getSessions().length === 0) { + this.debugType.reset(); + this.viewModel.setMultiSessionView(false); + + if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue<IDebugConfiguration>('debug').openExplorerOnEnd) { + this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError); + } + } + + })); + } + private showError(message: string, actions: IAction[] = []): TPromise<any> { const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL); actions.push(configureAction); @@ -1001,7 +818,7 @@ export class DebugService implements debug.IDebugService { }); } - private runTask(sessionId: string, root: IWorkspaceFolder, taskId: string | TaskIdentifier, config: debug.IConfig, unresolvedConfig: debug.IConfig, onSuccess: () => TPromise<any>): TPromise<any> { + private runTask(sessionId: string, root: IWorkspaceFolder, taskId: string | TaskIdentifier, config: IConfig, unresolvedConfig: IConfig): TPromise<boolean> { const debugAnywayAction = new Action('debug.debugAnyway', nls.localize('debugAnyway', "Debug Anyway"), undefined, true, () => { return this.doCreateSession(root, { resolved: config, unresolved: unresolvedConfig }, sessionId); }); @@ -1011,7 +828,7 @@ export class DebugService implements debug.IDebugService { const successExitCode = taskSummary && taskSummary.exitCode === 0; const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0; if (successExitCode || (errorCount === 0 && !failureExitCode)) { - return onSuccess(); + return true; } const message = errorCount > 1 ? nls.localize('preLaunchTaskErrors', "Build errors have been detected during preLaunchTask '{0}'.", config.preLaunchTask) : @@ -1022,9 +839,9 @@ export class DebugService implements debug.IDebugService { return this.panelService.openPanel(Constants.MARKERS_PANEL_ID).then(() => undefined); }); - return this.showError(message, [debugAnywayAction, showErrorsAction]); + return this.showError(message, [debugAnywayAction, showErrorsAction]).then(() => false); }, (err: TaskError) => { - return this.showError(err.message, [debugAnywayAction, this.taskService.configureAction()]); + return this.showError(err.message, [debugAnywayAction, this.taskService.configureAction()]).then(() => false); }); } @@ -1060,16 +877,12 @@ export class DebugService implements debug.IDebugService { // task is already running - nothing to do. return TPromise.as(null); } - this.toDisposeOnSessionEnd.get(sessionId).push( - once(TaskEventKind.Active, this.taskService.onDidStateChange)(() => { - taskStarted = true; - }) - ); + once(TaskEventKind.Active, this.taskService.onDidStateChange)((taskEvent) => { + taskStarted = true; + }); const taskPromise = this.taskService.run(task); if (task.isBackground) { - return new TPromise((c, e) => this.toDisposeOnSessionEnd.get(sessionId).push( - once(TaskEventKind.Inactive, this.taskService.onDidStateChange)(() => c(null))) - ); + return new TPromise((c, e) => once(TaskEventKind.Inactive, this.taskService.onDidStateChange)(() => c(null))); } return taskPromise; @@ -1093,16 +906,17 @@ export class DebugService implements debug.IDebugService { }); } - public sourceIsNotAvailable(uri: uri): void { + sourceIsNotAvailable(uri: uri): void { this.model.sourceIsNotAvailable(uri); } - public restartSession(session: debug.ISession, restartData?: any): TPromise<any> { + restartSession(session: ISession, restartData?: any): TPromise<any> { return this.textFileService.saveAll().then(() => { const unresolvedConfiguration = (<Session>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)); + return this.runTask(session.getId(), session.root, session.configuration.postDebugTask, session.configuration, unresolvedConfiguration) + .then(success => success ? this.runTask(session.getId(), session.root, session.configuration.preLaunchTask, session.configuration, unresolvedConfiguration) + .then(success => success ? session.raw.custom('restart', null) : undefined) : TPromise.as(<any>undefined)); } const focusedSession = this.viewModel.focusedSession; @@ -1110,20 +924,22 @@ 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 (strings.equalsIgnoreCase(session.configuration.type, 'extensionHost') && session.raw.root) { - return this.broadcastService.broadcast({ - channel: EXTENSION_RELOAD_BROADCAST_CHANNEL, - payload: [session.raw.root.uri.fsPath] - }); - } + if (equalsIgnoreCase(session.configuration.type, 'extensionHost') && session.root) { + return this.broadcastService.broadcast({ + channel: EXTENSION_RELOAD_BROADCAST_CHANNEL, + payload: [session.root.uri.toString()] + }); + } + + // If the restart is automatic disconnect, otherwise send the terminate signal #55064 + return (!!restartData ? session.raw.disconnect(true) : session.raw.terminate(true)).then(() => { return new TPromise<void>((c, e) => { setTimeout(() => { // Read the configuration again if a launch.json has been changed, if not just use the inmemory configuration let configToUse = session.configuration; - const launch = session.raw.root ? this.configurationManager.getLaunch(session.raw.root.uri) : undefined; + const launch = session.root ? this.configurationManager.getLaunch(session.root.uri) : undefined; if (launch) { const config = launch.getConfiguration(session.configuration.name); if (config && !equals(config, unresolvedConfiguration)) { @@ -1150,7 +966,7 @@ export class DebugService implements debug.IDebugService { }); } - public stopSession(session: debug.ISession): TPromise<any> { + stopSession(session: ISession): TPromise<any> { if (session) { return session.raw.terminate(); } @@ -1160,80 +976,32 @@ export class DebugService implements debug.IDebugService { return TPromise.join(sessions.map(s => s.raw.terminate(false))); } - this.sessionStates.clear(); this._onDidChangeState.fire(); return undefined; } - private onExitAdapter(raw: RawDebugSession): void { - const breakpoints = this.model.getBreakpoints(); - const session = this.model.getSessions().filter(p => p.getId() === raw.getId()).pop(); - /* __GDPR__ - "debugSessionStop" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "success": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "sessionLengthInSeconds": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "watchExpressionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('debugSessionStop', { - type: session && session.configuration.type, - success: raw.emittedStopped || breakpoints.length === 0, - sessionLengthInSeconds: raw.getLengthInSeconds(), - breakpointCount: breakpoints.length, - watchExpressionsCount: this.model.getWatchExpressions().length - }); - - this.model.removeSession(raw.getId()); - if (session) { - this._onDidEndSession.fire(session); - if (session.configuration.postDebugTask) { - this.doRunTask(session.getId(), session.raw.root, session.configuration.postDebugTask).done(undefined, err => - this.notificationService.error(err) - ); - } - } - - lifecycle.dispose(this.toDisposeOnSessionEnd.get(raw.getId())); - const focusedSession = this.viewModel.focusedSession; - if (focusedSession && focusedSession.getId() === raw.getId()) { - this.focusStackFrame(null); - } - this.updateStateAndEmit(raw.getId(), debug.State.Inactive); - - if (this.model.getSessions().length === 0) { - this.debugType.reset(); - this.viewModel.setMultiSessionView(false); - - if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue<debug.IDebugConfiguration>('debug').openExplorerOnEnd) { - this.viewletService.openViewlet(EXPLORER_VIEWLET_ID).done(null, errors.onUnexpectedError); - } - } - } - - public getModel(): debug.IModel { + getModel(): IModel { return this.model; } - public getViewModel(): debug.IViewModel { + getViewModel(): IViewModel { return this.viewModel; } - public getConfigurationManager(): debug.IConfigurationManager { + getConfigurationManager(): IConfigurationManager { return this.configurationManager; } - private sendAllBreakpoints(session?: debug.ISession): TPromise<any> { + sendAllBreakpoints(session?: ISession): TPromise<any> { return TPromise.join(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))) .then(() => this.sendFunctionBreakpoints(session)) // send exception breakpoints at the end since some debug adapters rely on the order .then(() => this.sendExceptionBreakpoints(session)); } - private sendBreakpoints(modelUri: uri, sourceModified = false, session?: debug.ISession): TPromise<void> { + private sendBreakpoints(modelUri: uri, sourceModified = false, session?: ISession): TPromise<void> { - const sendBreakpointsToSession = (session: debug.ISession): TPromise<void> => { + const sendBreakpointsToSession = (session: ISession): TPromise<void> => { const raw = <RawDebugSession>session.raw; if (!raw.readyForBreakpoints) { return TPromise.as(null); @@ -1270,15 +1038,15 @@ export class DebugService implements debug.IDebugService { for (let i = 0; i < breakpointsToSend.length; i++) { data[breakpointsToSend[i].getId()] = response.body.breakpoints[i]; } - this.model.setBreakpointSessionData(raw.getId(), data); + this.model.setBreakpointSessionData(session.getId(), data); }); }; return this.sendToOneOrAllSessions(session, sendBreakpointsToSession); } - private sendFunctionBreakpoints(session?: debug.ISession): TPromise<void> { - const sendFunctionBreakpointsToSession = (session: debug.ISession): TPromise<void> => { + private sendFunctionBreakpoints(session?: ISession): TPromise<void> { + const sendFunctionBreakpointsToSession = (session: ISession): TPromise<void> => { const raw = <RawDebugSession>session.raw; if (!raw.readyForBreakpoints || !raw.capabilities.supportsFunctionBreakpoints) { return TPromise.as(null); @@ -1294,15 +1062,15 @@ export class DebugService implements debug.IDebugService { for (let i = 0; i < breakpointsToSend.length; i++) { data[breakpointsToSend[i].getId()] = response.body.breakpoints[i]; } - this.model.setBreakpointSessionData(raw.getId(), data); + this.model.setBreakpointSessionData(session.getId(), data); }); }; return this.sendToOneOrAllSessions(session, sendFunctionBreakpointsToSession); } - private sendExceptionBreakpoints(session?: debug.ISession): TPromise<void> { - const sendExceptionBreakpointsToSession = (session: debug.ISession): TPromise<any> => { + private sendExceptionBreakpoints(session?: ISession): TPromise<void> { + const sendExceptionBreakpointsToSession = (session: ISession): TPromise<any> => { const raw = <RawDebugSession>session.raw; if (!raw.readyForBreakpoints || this.model.getExceptionBreakpoints().length === 0) { return TPromise.as(null); @@ -1315,7 +1083,7 @@ export class DebugService implements debug.IDebugService { return this.sendToOneOrAllSessions(session, sendExceptionBreakpointsToSession); } - private sendToOneOrAllSessions(session: debug.ISession, send: (session: debug.ISession) => TPromise<void>): TPromise<void> { + private sendToOneOrAllSessions(session: ISession, send: (session: ISession) => TPromise<void>): TPromise<void> { if (session) { return send(session); } @@ -1374,8 +1142,7 @@ export class DebugService implements debug.IDebugService { } } - public dispose(): void { - this.toDisposeOnSessionEnd.forEach(toDispose => lifecycle.dispose(toDispose)); - this.toDispose = lifecycle.dispose(this.toDispose); + dispose(): void { + this.toDispose = dispose(this.toDispose); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts new file mode 100644 index 00000000000..ae2b8c72761 --- /dev/null +++ b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts @@ -0,0 +1,437 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import uri from 'vs/base/common/uri'; +import * as resources from 'vs/base/common/resources'; +import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; +import * as errors from 'vs/base/common/errors'; +import severity from 'vs/base/common/severity'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { Event, Emitter } from 'vs/base/common/event'; +import { ISuggestion } from 'vs/editor/common/modes'; +import { Position } from 'vs/editor/common/core/position'; +import * as aria from 'vs/base/browser/ui/aria/aria'; +import { ISession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, IRawSession, LoadedSourceEvent, DebugEvent } from 'vs/workbench/parts/debug/common/debug'; +import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { mixin } from 'vs/base/common/objects'; +import { Thread, ExpressionContainer, Model } from 'vs/workbench/parts/debug/common/debugModel'; +import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Debugger } from 'vs/workbench/parts/debug/node/debugger'; +import product from 'vs/platform/node/product'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { generateUuid } from 'vs/base/common/uuid'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +export class Session implements ISession { + + private sources = new Map<string, Source>(); + private threads = new Map<number, Thread>(); + private rawListeners: IDisposable[] = []; + private fetchThreadsScheduler: RunOnceScheduler; + private _raw: RawDebugSession; + private _state: State; + private readonly _onDidLoadedSource = new Emitter<LoadedSourceEvent>(); + private readonly _onDidCustomEvent = new Emitter<DebugEvent>(); + private readonly _onDidChangeState = new Emitter<State>(); + private readonly _onDidExitAdapter = new Emitter<void>(); + + constructor( + private id: string, + private _configuration: { resolved: IConfig, unresolved: IConfig }, + public root: IWorkspaceFolder, + private model: Model, + @IInstantiationService private instantiationService: IInstantiationService, + @INotificationService private notificationService: INotificationService, + @IDebugService private debugService: IDebugService, + @ITelemetryService private telemetryService: ITelemetryService, + ) { + this.state = State.Initializing; + } + + get raw(): IRawSession { + return this._raw; + } + + get configuration(): IConfig { + return this._configuration.resolved; + } + + get unresolvedConfiguration(): IConfig { + return this._configuration.unresolved; + } + + getName(includeRoot: boolean): string { + return includeRoot && this.root ? `${this.configuration.name} (${resources.basenameOrAuthority(this.root.uri)})` : this.configuration.name; + } + + get state(): State { + return this._state; + } + + set state(value: State) { + this._state = value; + this._onDidChangeState.fire(value); + } + + get onDidChangeState(): Event<State> { + return this._onDidChangeState.event; + } + + getId(): string { + return this.id; + } + + getSourceForUri(modelUri: uri): Source { + return this.sources.get(modelUri.toString()); + } + + getSource(raw: DebugProtocol.Source): Source { + let source = new Source(raw, this.getId()); + if (this.sources.has(source.uri.toString())) { + source = this.sources.get(source.uri.toString()); + source.raw = mixin(source.raw, raw); + if (source.raw && raw) { + // Always take the latest presentation hint from adapter #42139 + source.raw.presentationHint = raw.presentationHint; + } + } else { + this.sources.set(source.uri.toString(), source); + } + + return source; + } + + getThread(threadId: number): Thread { + return this.threads.get(threadId); + } + + getAllThreads(): IThread[] { + const result: IThread[] = []; + this.threads.forEach(t => result.push(t)); + return result; + } + + getLoadedSources(): TPromise<Source[]> { + return this._raw.loadedSources({}).then(response => { + return response.body.sources.map(src => this.getSource(src)); + }, () => { + return []; + }); + } + + get onDidLoadedSource(): Event<LoadedSourceEvent> { + return this._onDidLoadedSource.event; + } + + get onDidCustomEvent(): Event<DebugEvent> { + return this._onDidCustomEvent.event; + } + + get onDidExitAdapter(): Event<void> { + return this._onDidExitAdapter.event; + } + + rawUpdate(data: IRawModelUpdate): void { + + if (data.thread && !this.threads.has(data.threadId)) { + // A new thread came in, initialize it. + this.threads.set(data.threadId, new Thread(this, data.thread.name, data.thread.id)); + } else if (data.thread && data.thread.name) { + // Just the thread name got updated #18244 + this.threads.get(data.threadId).name = data.thread.name; + } + + if (data.stoppedDetails) { + // Set the availability of the threads' callstacks depending on + // whether the thread is stopped or not + if (data.stoppedDetails.allThreadsStopped) { + this.threads.forEach(thread => { + thread.stoppedDetails = thread.threadId === data.threadId ? data.stoppedDetails : { reason: undefined }; + thread.stopped = true; + thread.clearCallStack(); + }); + } else if (this.threads.has(data.threadId)) { + // One thread is stopped, only update that thread. + const thread = this.threads.get(data.threadId); + thread.stoppedDetails = data.stoppedDetails; + thread.clearCallStack(); + thread.stopped = true; + } + } + } + + clearThreads(removeThreads: boolean, reference: number = undefined): void { + if (reference !== undefined && reference !== null) { + if (this.threads.has(reference)) { + const thread = this.threads.get(reference); + thread.clearCallStack(); + thread.stoppedDetails = undefined; + thread.stopped = false; + + if (removeThreads) { + this.threads.delete(reference); + } + } + } else { + this.threads.forEach(thread => { + thread.clearCallStack(); + thread.stoppedDetails = undefined; + thread.stopped = false; + }); + + if (removeThreads) { + this.threads.clear(); + ExpressionContainer.allValues.clear(); + } + } + } + + completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise<ISuggestion[]> { + if (!this._raw.capabilities.supportsCompletionsRequest) { + return TPromise.as([]); + } + + return this._raw.completions({ + frameId, + text, + column: position.column, + line: position.lineNumber + }).then(response => { + const result: ISuggestion[] = []; + if (response && response.body && response.body.targets) { + response.body.targets.forEach(item => { + if (item && item.label) { + result.push({ + label: item.label, + insertText: item.text || item.label, + type: item.type, + filterText: item.start && item.length && text.substr(item.start, item.length).concat(item.label), + overwriteBefore: item.length || overwriteBefore + }); + } + }); + } + + return result; + }, () => []); + } + + initialize(dbgr: Debugger): TPromise<void> { + if (this._raw) { + // If there was already a connection make sure to remove old listeners + this.dispose(); + } + + return dbgr.getCustomTelemetryService().then(customTelemetryService => { + this._raw = this.instantiationService.createInstance(RawDebugSession, this.id, this._configuration.resolved.debugServer, dbgr, customTelemetryService, this.root); + this.registerListeners(); + + return this._raw.initialize({ + clientID: 'vscode', + clientName: product.nameLong, + adapterID: this.configuration.type, + pathFormat: 'path', + linesStartAt1: true, + columnsStartAt1: true, + supportsVariableType: true, // #8858 + supportsVariablePaging: true, // #9537 + supportsRunInTerminalRequest: true, // #10574 + locale: platform.locale + }).then(() => { + this.model.addSession(this); + this.state = State.Running; + this.model.setExceptionBreakpoints(this._raw.capabilities.exceptionBreakpointFilters); + }); + }); + } + + private registerListeners(): void { + this.rawListeners.push(this._raw.onDidInitialize(() => { + aria.status(nls.localize('debuggingStarted', "Debugging started.")); + const sendConfigurationDone = () => { + if (this._raw && this._raw.capabilities.supportsConfigurationDoneRequest) { + return this._raw.configurationDone().done(null, e => { + // Disconnect the debug session on configuration done error #10596 + if (this._raw) { + this._raw.disconnect().done(undefined, errors.onUnexpectedError); + } + this.notificationService.error(e.message); + }); + } + }; + + // Send all breakpoints + this.debugService.sendAllBreakpoints(this).then(sendConfigurationDone, sendConfigurationDone) + .done(() => this.fetchThreads(), errors.onUnexpectedError); + })); + + this.rawListeners.push(this._raw.onDidStop(event => { + this.state = State.Stopped; + this.fetchThreads(event.body).done(() => { + const thread = this.getThread(event.body.threadId); + if (thread) { + // Call fetch call stack twice, the first only return the top stack frame. + // Second retrieves the rest of the call stack. For performance reasons #25605 + this.model.fetchCallStack(<Thread>thread).then(() => { + return !event.body.preserveFocusHint ? this.debugService.tryToAutoFocusStackFrame(thread) : undefined; + }); + } + }, errors.onUnexpectedError); + })); + + this.rawListeners.push(this._raw.onDidThread(event => { + if (event.body.reason === 'started') { + // debounce to reduce threadsRequest frequency and improve performance + if (!this.fetchThreadsScheduler) { + this.fetchThreadsScheduler = new RunOnceScheduler(() => { + this.fetchThreads().done(undefined, errors.onUnexpectedError); + }, 100); + this.rawListeners.push(this.fetchThreadsScheduler); + } + if (!this.fetchThreadsScheduler.isScheduled()) { + this.fetchThreadsScheduler.schedule(); + } + } else if (event.body.reason === 'exited') { + this.model.clearThreads(this.getId(), true, event.body.threadId); + } + })); + + this.rawListeners.push(this._raw.onDidTerminateDebugee(event => { + aria.status(nls.localize('debuggingStopped', "Debugging stopped.")); + if (event.body && event.body.restart) { + this.debugService.restartSession(this, event.body.restart).done(null, err => this.notificationService.error(err.message)); + } else { + this._raw.disconnect().done(undefined, errors.onUnexpectedError); + } + })); + + this.rawListeners.push(this._raw.onDidContinued(event => { + const threadId = event.body.allThreadsContinued !== false ? undefined : event.body.threadId; + this.model.clearThreads(this.getId(), false, threadId); + this.state = State.Running; + })); + + let outputPromises: TPromise<void>[] = []; + this.rawListeners.push(this._raw.onDidOutput(event => { + if (!event.body) { + return; + } + + const outputSeverity = event.body.category === 'stderr' ? severity.Error : event.body.category === 'console' ? severity.Warning : severity.Info; + if (event.body.category === 'telemetry') { + // only log telemetry events from debug adapter if the debug extension provided the telemetry key + // and the user opted in telemetry + if (this._raw.customTelemetryService && this.telemetryService.isOptedIn) { + // __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly. + this._raw.customTelemetryService.publicLog(event.body.output, event.body.data); + } + + return; + } + + // Make sure to append output in the correct order by properly waiting on preivous promises #33822 + const waitFor = outputPromises.slice(); + const source = event.body.source ? { + lineNumber: event.body.line, + column: event.body.column ? event.body.column : 1, + source: this.getSource(event.body.source) + } : undefined; + if (event.body.variablesReference) { + const container = new ExpressionContainer(this, event.body.variablesReference, generateUuid()); + outputPromises.push(container.getChildren().then(children => { + return TPromise.join(waitFor).then(() => children.forEach(child => { + // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) + child.name = null; + this.debugService.logToRepl(child, outputSeverity, source); + })); + })); + } else if (typeof event.body.output === 'string') { + TPromise.join(waitFor).then(() => this.debugService.logToRepl(event.body.output, outputSeverity, source)); + } + TPromise.join(outputPromises).then(() => outputPromises = []); + })); + + this.rawListeners.push(this._raw.onDidBreakpoint(event => { + const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined; + const breakpoint = this.model.getBreakpoints().filter(bp => bp.idFromAdapter === id).pop(); + const functionBreakpoint = this.model.getFunctionBreakpoints().filter(bp => bp.idFromAdapter === id).pop(); + + if (event.body.reason === 'new' && event.body.breakpoint.source) { + const source = this.getSource(event.body.breakpoint.source); + const bps = this.model.addBreakpoints(source.uri, [{ + column: event.body.breakpoint.column, + enabled: true, + lineNumber: event.body.breakpoint.line, + }], false); + if (bps.length === 1) { + this.model.updateBreakpoints({ [bps[0].getId()]: event.body.breakpoint }); + } + } + + if (event.body.reason === 'removed') { + if (breakpoint) { + this.model.removeBreakpoints([breakpoint]); + } + if (functionBreakpoint) { + this.model.removeFunctionBreakpoints(functionBreakpoint.getId()); + } + } + + if (event.body.reason === 'changed') { + if (breakpoint) { + if (!breakpoint.column) { + event.body.breakpoint.column = undefined; + } + this.model.setBreakpointSessionData(this.getId(), { [breakpoint.getId()]: event.body.breakpoint }); + } + if (functionBreakpoint) { + this.model.setBreakpointSessionData(this.getId(), { [functionBreakpoint.getId()]: event.body.breakpoint }); + } + } + })); + + this.rawListeners.push(this._raw.onDidLoadedSource(event => { + this._onDidLoadedSource.fire({ + reason: event.body.reason, + source: this.getSource(event.body.source) + }); + })); + + this.rawListeners.push(this._raw.onDidCustomEvent(event => { + this._onDidCustomEvent.fire(event); + })); + + this.rawListeners.push(this._raw.onDidExitAdapter(() => this._onDidExitAdapter.fire())); + } + + private fetchThreads(stoppedDetails?: IRawStoppedDetails): TPromise<any> { + return this._raw.threads().then(response => { + if (response && response.body && response.body.threads) { + response.body.threads.forEach(thread => { + this.model.rawUpdate({ + sessionId: this.getId(), + threadId: thread.id, + thread, + stoppedDetails: stoppedDetails && thread.id === stoppedDetails.threadId ? stoppedDetails : undefined + }); + }); + } + }); + } + + dispose(): void { + dispose(this.rawListeners); + this.model.removeSession(this.getId()); + if (!this._raw.disconnected) { + this._raw.disconnect().done(undefined, errors.onUnexpectedError); + } + this._raw = undefined; + } +} diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index a2bf801d4cc..84f37b4e5c9 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -16,8 +16,8 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { formatPII } from 'vs/workbench/parts/debug/common/debugUtils'; import { SocketDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; -import { SessionState, DebugEvent, IRawSession, IDebugAdapter } from 'vs/workbench/parts/debug/common/debug'; - +import { DebugEvent, IRawSession, IDebugAdapter } from 'vs/workbench/parts/debug/common/debug'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; export interface SessionExitedEvent extends DebugEvent { body: { @@ -44,10 +44,10 @@ export class RawDebugSession implements IRawSession { private startTime: number; public disconnected: boolean; private terminated: boolean; - private sentPromises: TPromise<DebugProtocol.Response>[]; + private cancellationTokens: CancellationTokenSource[]; private _capabilities: DebugProtocol.Capabilities; private allThreadsContinued: boolean; - private state: SessionState = SessionState.LAUNCH; + private isAttached: boolean; private readonly _onDidInitialize: Emitter<DebugProtocol.InitializedEvent>; private readonly _onDidStop: Emitter<DebugProtocol.StoppedEvent>; @@ -58,15 +58,16 @@ export class RawDebugSession implements IRawSession { private readonly _onDidThread: Emitter<DebugProtocol.ThreadEvent>; private readonly _onDidOutput: Emitter<DebugProtocol.OutputEvent>; private readonly _onDidBreakpoint: Emitter<DebugProtocol.BreakpointEvent>; + private readonly _onDidLoadedSource: Emitter<DebugProtocol.LoadedSourceEvent>; private readonly _onDidCustomEvent: Emitter<DebugEvent>; private readonly _onDidEvent: Emitter<DebugProtocol.Event>; constructor( - private id: string, + private sessionId: string, private debugServerPort: number, private _debugger: Debugger, public customTelemetryService: ITelemetryService, - public root: IWorkspaceFolder, + private root: IWorkspaceFolder, @INotificationService private notificationService: INotificationService, @ITelemetryService private telemetryService: ITelemetryService, @IOutputService private outputService: IOutputService @@ -74,7 +75,7 @@ export class RawDebugSession implements IRawSession { this.emittedStopped = false; this.readyForBreakpoints = false; this.allThreadsContinued = true; - this.sentPromises = []; + this.cancellationTokens = []; this._onDidInitialize = new Emitter<DebugProtocol.InitializedEvent>(); this._onDidStop = new Emitter<DebugProtocol.StoppedEvent>(); @@ -85,14 +86,11 @@ export class RawDebugSession implements IRawSession { this._onDidThread = new Emitter<DebugProtocol.ThreadEvent>(); this._onDidOutput = new Emitter<DebugProtocol.OutputEvent>(); this._onDidBreakpoint = new Emitter<DebugProtocol.BreakpointEvent>(); + this._onDidLoadedSource = new Emitter<DebugProtocol.LoadedSourceEvent>(); this._onDidCustomEvent = new Emitter<DebugEvent>(); this._onDidEvent = new Emitter<DebugProtocol.Event>(); } - public getId(): string { - return this.id; - } - public get onDidInitialize(): Event<DebugProtocol.InitializedEvent> { return this._onDidInitialize.event; } @@ -129,6 +127,10 @@ export class RawDebugSession implements IRawSession { return this._onDidBreakpoint.event; } + public get onDidLoadedSource(): Event<DebugProtocol.LoadedSourceEvent> { + return this._onDidLoadedSource.event; + } + public get onDidCustomEvent(): Event<DebugEvent> { return this._onDidCustomEvent.event; } @@ -176,7 +178,8 @@ export class RawDebugSession implements IRawSession { private send<R extends DebugProtocol.Response>(command: string, args: any, cancelOnDisconnect = true): TPromise<R> { return this.initServer().then(() => { - const promise = this.internalSend<R>(command, args).then(response => response, (errorResponse: DebugProtocol.ErrorResponse) => { + const cancellationSource = new CancellationTokenSource(); + const promise = this.internalSend<R>(command, args, cancellationSource.token).then(response => response, (errorResponse: DebugProtocol.ErrorResponse) => { const error = errorResponse && errorResponse.body ? errorResponse.body.error : null; const errorMessage = errorResponse ? errorResponse.message : ''; const telemetryMessage = error ? formatPII(error.format, true, error.variables) : errorMessage; @@ -211,16 +214,15 @@ export class RawDebugSession implements IRawSession { }); if (cancelOnDisconnect) { - this.sentPromises.push(promise); + this.cancellationTokens.push(cancellationSource); } return promise; }); } - private internalSend<R extends DebugProtocol.Response>(command: string, args: any): TPromise<R> { - let errorCallback: (error: Error) => void; + private internalSend<R extends DebugProtocol.Response>(command: string, args: any, cancelationToken: CancellationToken): TPromise<R> { return new TPromise<R>((completeDispatch, errorDispatch) => { - errorCallback = errorDispatch; + cancelationToken.onCancellationRequested(() => errorDispatch(errors.canceled())); this.debugAdapter.sendRequest(command, args, (result: R) => { if (result.success) { completeDispatch(result); @@ -228,13 +230,15 @@ export class RawDebugSession implements IRawSession { errorDispatch(result); } }); - }, () => errorCallback(errors.canceled())); + }); } private onDapEvent(event: DebugEvent): void { - event.sessionId = this.id; + event.sessionId = this.sessionId; - if (event.event === 'initialized') { + if (event.event === 'loadedSource') { // most frequent comes first + this._onDidLoadedSource.fire(<DebugProtocol.LoadedSourceEvent>event); + } else if (event.event === 'initialized') { this.readyForBreakpoints = true; this._onDidInitialize.fire(event); } else if (event.event === 'capabilities' && event.body) { @@ -284,7 +288,7 @@ export class RawDebugSession implements IRawSession { } public attach(args: DebugProtocol.AttachRequestArguments): TPromise<DebugProtocol.AttachResponse> { - this.state = SessionState.ATTACH; + this.isAttached = true; return this.send('attach', args).then(response => this.readCapabilities(response)); } @@ -343,13 +347,12 @@ export class RawDebugSession implements IRawSession { } public terminate(restart = false): TPromise<DebugProtocol.TerminateResponse> { - if (this.capabilities.supportsTerminateRequest && !this.terminated && this.state === SessionState.LAUNCH) { + if (this.capabilities.supportsTerminateRequest && !this.terminated && !this.isAttached) { this.terminated = true; return this.send('terminate', { restart }); } - this.dispose(restart); - return TPromise.as(null); + return this.disconnect(restart); } public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise<DebugProtocol.SetBreakpointsResponse> { @@ -388,6 +391,11 @@ export class RawDebugSession implements IRawSession { return this.send<DebugProtocol.SourceResponse>('source', args); } + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise<DebugProtocol.LoadedSourcesResponse> { + return this.send<DebugProtocol.LoadedSourcesResponse>('loadedSources', args); + } + + public threads(): TPromise<DebugProtocol.ThreadsResponse> { return this.send<DebugProtocol.ThreadsResponse>('threads', null); } @@ -419,47 +427,44 @@ export class RawDebugSession implements IRawSession { } private dispatchRequest(request: DebugProtocol.Request): void { + const response: DebugProtocol.Response = { + type: 'response', + seq: 0, + command: request.command, + request_seq: request.seq, + success: true + }; + const sendResponse = (response) => this.debugAdapter && this.debugAdapter.sendResponse(response); - if (this.debugAdapter) { + if (request.command === 'runInTerminal') { - const response: DebugProtocol.Response = { - type: 'response', - seq: 0, - command: request.command, - request_seq: request.seq, - success: true - }; - - if (request.command === 'runInTerminal') { - - this._debugger.runInTerminal(<DebugProtocol.RunInTerminalRequestArguments>request.arguments).then(_ => { - response.body = {}; - this.debugAdapter.sendResponse(response); - }, err => { - response.success = false; - response.message = err.message; - this.debugAdapter.sendResponse(response); - }); - - } else if (request.command === 'handshake') { - try { - const vsda = <any>require.__$__nodeRequire('vsda'); - const obj = new vsda.signer(); - const sig = obj.sign(request.arguments.value); - response.body = { - signature: sig - }; - this.debugAdapter.sendResponse(response); - } catch (e) { - response.success = false; - response.message = e.message; - this.debugAdapter.sendResponse(response); - } - } else { + this._debugger.runInTerminal(<DebugProtocol.RunInTerminalRequestArguments>request.arguments).then(_ => { + response.body = {}; + sendResponse(response); + }, err => { response.success = false; - response.message = `unknown request '${request.command}'`; - this.debugAdapter.sendResponse(response); + response.message = err.message; + sendResponse(response); + }); + + } else if (request.command === 'handshake') { + try { + const vsda = <any>require.__$__nodeRequire('vsda'); + const obj = new vsda.signer(); + const sig = obj.sign(request.arguments.value); + response.body = { + signature: sig + }; + sendResponse(response); + } catch (e) { + response.success = false; + response.message = e.message; + sendResponse(response); } + } else { + response.success = false; + response.message = `unknown request '${request.command}'`; + sendResponse(response); } } @@ -475,24 +480,25 @@ export class RawDebugSession implements IRawSession { }); } - public dispose(restart = false): void { + public disconnect(restart = false): TPromise<any> { 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.cancellationTokens.forEach(token => token.cancel()); + this.cancellationTokens = []; + }, 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<any> { @@ -502,13 +508,12 @@ export class RawDebugSession implements IRawSession { this.cachedInitServerP = null; } - this._onDidExitAdapter.fire({ sessionId: this.getId() }); + this._onDidExitAdapter.fire({ sessionId: this.sessionId }); this.disconnected = true; if (!this.debugAdapter || this.debugAdapter instanceof SocketDebugAdapter) { return TPromise.as(null); } - return this.debugAdapter.stopSession(); } @@ -523,6 +528,6 @@ export class RawDebugSession implements IRawSession { if (!this.disconnected) { this.notificationService.error(nls.localize('debugAdapterCrash', "Debug adapter process has terminated unexpectedly")); } - this._onDidExitAdapter.fire({ sessionId: this.getId() }); + this._onDidExitAdapter.fire({ sessionId: this.sessionId }); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 10bbf3327fa..68d06e337a4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -30,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'; @@ -48,6 +47,8 @@ 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 { getSimpleCodeEditorWidgetOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions'; +import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/browser/simpleEditorOptions'; const $ = dom.$; @@ -173,7 +174,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: ['.'], diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index c76295bd8f7..cbc0efa5f74 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 { ILabelService } from 'vs/platform/label/common/label'; const $ = dom.$; @@ -98,7 +97,7 @@ export class ReplExpressionsRenderer implements IRenderer { constructor( @IEditorService private editorService: IEditorService, @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService + @ILabelService private labelService: ILabelService ) { 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.labelService.getUriLabel(element.sourceData.source.uri) : ''; templateData.getReplElementSource = () => element.sourceData; } diff --git a/src/vs/workbench/parts/debug/electron-browser/simpleDebugEditor.ts b/src/vs/workbench/parts/debug/electron-browser/simpleDebugEditor.ts deleted file mode 100644 index 720b75f1cad..00000000000 --- a/src/vs/workbench/parts/debug/electron-browser/simpleDebugEditor.ts +++ /dev/null @@ -1,57 +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 { 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'; -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 { - - 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 - } - }; - } -} diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index a322f0d8779..98342cab47d 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -12,7 +12,6 @@ import * as paths from 'vs/base/common/paths'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; -import * as stdfork from 'vs/base/node/stdFork'; import { Emitter, Event } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -238,7 +237,7 @@ export class SocketDebugAdapter extends StreamDebugAdapter { } stopSession(): TPromise<void> { - if (this.socket !== null) { + if (this.socket) { this.socket.end(); this.socket = undefined; } @@ -285,13 +284,15 @@ export class DebugAdapter extends StreamDebugAdapter { if (this.adapterExecutable.command === 'node' && this.outputService) { if (Array.isArray(this.adapterExecutable.args) && this.adapterExecutable.args.length > 0) { - stdfork.fork(this.adapterExecutable.args[0], this.adapterExecutable.args.slice(1), {}, (err, child) => { - if (err) { - e(new Error(nls.localize('unableToLaunchDebugAdapter', "Unable to launch debug adapter from '{0}'.", this.adapterExecutable.args[0]))); - } - this.serverProcess = child; - c(null); + const child = cp.fork(this.adapterExecutable.args[0], this.adapterExecutable.args.slice(1), { + execArgv: ['-e', 'delete process.env.ELECTRON_RUN_AS_NODE;require(process.argv[1])'].concat(process.execArgv || []), + silent: true }); + if (!child.pid) { + e(new Error(nls.localize('unableToLaunchDebugAdapter', "Unable to launch debug adapter from '{0}'.", this.adapterExecutable.args[0]))); + } + this.serverProcess = child; + c(null); } else { e(new Error(nls.localize('unableToLaunchDebugAdapterNoArgs', "Unable to launch debug adapter."))); } diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index cde530adaef..ccb0083218c 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; -import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; +import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IConfig, IDebuggerContribution, IAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, IDebugConfiguration, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; @@ -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/telemetryApp.ts b/src/vs/workbench/parts/debug/node/telemetryApp.ts index db5e7ec6f62..490dc714fcc 100644 --- a/src/vs/workbench/parts/debug/node/telemetryApp.ts +++ b/src/vs/workbench/parts/debug/node/telemetryApp.ts @@ -5,7 +5,7 @@ import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; -import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc'; +import { TelemetryAppenderChannel } from 'vs/platform/telemetry/node/telemetryIpc'; const appender = new AppInsightsAppender(process.argv[2], JSON.parse(process.argv[3]), process.argv[4]); process.once('exit', () => appender.dispose()); diff --git a/src/vs/workbench/parts/debug/node/terminals.ts b/src/vs/workbench/parts/debug/node/terminals.ts index a46ea642248..221a8bd7bc2 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, @@ -312,7 +312,7 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments // try to determine the shell type shell = shell.trim().toLowerCase(); - if (shell.indexOf('powershell') >= 0) { + if (shell.indexOf('powershell') >= 0 || shell.indexOf('pwsh') >= 0) { shellType = ShellType.powershell; } else if (shell.indexOf('cmd.exe') >= 0) { shellType = ShellType.cmd; @@ -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/browser/baseDebugView.test.ts b/src/vs/workbench/parts/debug/test/browser/baseDebugView.test.ts index d52a69598b8..837cb46a9b6 100644 --- a/src/vs/workbench/parts/debug/test/browser/baseDebugView.test.ts +++ b/src/vs/workbench/parts/debug/test/browser/baseDebugView.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { replaceWhitespace, renderExpressionValue, renderVariable } from 'vs/workbench/parts/debug/browser/baseDebugView'; import * as dom from 'vs/base/browser/dom'; -import { Expression, Variable, Session, Scope, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; +import { Expression, Variable, Scope, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; const $ = dom.$; @@ -52,8 +52,7 @@ suite('Debug - Base Debug View', () => { }); test('render variable', () => { - const rawSession = new MockSession(); - const session = new Session({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, rawSession); + const session = new MockSession(); const thread = new Thread(session, 'mockthread', 1); const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: undefined, endColumn: undefined }, 0); const scope = new Scope(stackFrame, 1, 'local', 1, false, 10, 10); diff --git a/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts b/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts index 733ae781c37..a466b211fe4 100644 --- a/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts +++ b/src/vs/workbench/parts/debug/test/common/debugViewModel.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { ViewModel } from 'vs/workbench/parts/debug/common/debugViewModel'; -import { StackFrame, Expression, Thread, Session } from 'vs/workbench/parts/debug/common/debugModel'; +import { StackFrame, Expression, Thread } from 'vs/workbench/parts/debug/common/debugModel'; import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; @@ -23,8 +23,7 @@ suite('Debug - View Model', () => { test('focused stack frame', () => { assert.equal(model.focusedStackFrame, null); assert.equal(model.focusedThread, null); - const mockSession = new MockSession(); - const session = new Session({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, mockSession); + const session = new MockSession(); const thread = new Thread(session, 'myThread', 1); const frame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startColumn: 1, startLineNumber: 1, endColumn: undefined, endLineNumber: undefined }, 0); model.setFocus(frame, thread, session, false); diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index ceb4cc25a49..eaa8b94a0c3 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -4,22 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import uri from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event } 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 { Position } from 'vs/editor/common/core/position'; +import { ILaunch, IDebugService, State, DebugEvent, ISession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IModel, IViewModel, IRawSession, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate } from 'vs/workbench/parts/debug/common/debug'; +import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { ISuggestion } from 'vs/editor/common/modes'; export class MockDebugService implements IDebugService { + public _serviceBrand: any; public get state(): State { return null; } - public get onDidCustomEvent(): Event<DebugEvent> { - return null; - } - public get onDidNewSession(): Event<ISession> { return null; } @@ -39,6 +39,10 @@ export class MockDebugService implements IDebugService { public focusStackFrame(focusedStackFrame: IStackFrame): void { } + sendAllBreakpoints(session?: ISession): TPromise<any> { + return TPromise.as(null); + } + public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise<IBreakpoint[]> { return TPromise.as(null); } @@ -108,19 +112,75 @@ export class MockDebugService implements IDebugService { public logToRepl(value: string): void { } public sourceIsNotAvailable(uri: uri): void { } + + public tryToAutoFocusStackFrame(thread: IThread): TPromise<any> { + return TPromise.as(null); + } } -export class MockSession implements IRawSession { +export class MockSession implements ISession { + + configuration: IConfig = { type: 'mock', request: 'launch' }; + raw: IRawSession = new MockRawSession(); + state = State.Stopped; + root: IWorkspaceFolder; + + getName(includeRoot: boolean): string { + return 'mockname'; + } + + getSourceForUri(modelUri: uri): Source { + return null; + } + + getThread(threadId: number): IThread { + return null; + } + + get onDidCustomEvent(): Event<DebugEvent> { + return null; + } + + get onDidLoadedSource(): Event<LoadedSourceEvent> { + return null; + } + + get onDidExitAdapter(): Event<void> { + return null; + } + + getAllThreads(): ReadonlyArray<IThread> { + return []; + } + + getSource(raw: DebugProtocol.Source): Source { + return undefined; + } + + getLoadedSources(): TPromise<Source[]> { + return TPromise.as([]); + } + + completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise<ISuggestion[]> { + return TPromise.as([]); + } + + clearThreads(removeThreads: boolean, reference?: number): void { } + + rawUpdate(data: IRawModelUpdate): void { } + + getId(): string { + return 'mock'; + } + + dispose(): void { } +} + +export class MockRawSession implements IRawSession { public readyForBreakpoints = true; public emittedStopped = true; - public getId() { - return 'mockrawsession'; - } - - public root: IWorkspaceFolder; - public getLengthInSeconds(): number { return 100; } @@ -167,21 +227,6 @@ export class MockSession implements IRawSession { return {}; } - public get onDidEvent(): Event<DebugEvent> { - return null; - } - - public get onDidInitialize(): Event<DebugProtocol.InitializedEvent> { - const emitter = new Emitter<DebugProtocol.InitializedEvent>(); - return emitter.event; - } - - public get onDidExitAdapter(): Event<{ sessionId: string }> { - const emitter = new Emitter<{ sessionId: string }>(); - return emitter.event; - } - - public custom(request: string, args: any): TPromise<DebugProtocol.Response> { return TPromise.as(null); } @@ -190,6 +235,10 @@ export class MockSession implements IRawSession { return TPromise.as(null); } + public disconnect(restart?: boolean): TPromise<any> { + return TPromise.as(null); + } + public threads(): TPromise<DebugProtocol.ThreadsResponse> { return TPromise.as(null); } @@ -218,7 +267,7 @@ export class MockSession implements IRawSession { return TPromise.as(null); } - public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise<DebugProtocol.TerminateThreadsResponse, any> { + public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise<DebugProtocol.TerminateThreadsResponse> { return TPromise.as(null); } @@ -242,6 +291,10 @@ export class MockSession implements IRawSession { return TPromise.as(null); } + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise<DebugProtocol.LoadedSourcesResponse> { + return TPromise.as(null); + } + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise<DebugProtocol.SetBreakpointsResponse> { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/debug/test/node/debugModel.test.ts b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts similarity index 88% rename from src/vs/workbench/parts/debug/test/node/debugModel.test.ts rename to src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts index e480fb71cb6..d72aeacfb53 100644 --- a/src/vs/workbench/parts/debug/test/node/debugModel.test.ts +++ b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts @@ -6,18 +6,19 @@ import * as assert from 'assert'; import uri from 'vs/base/common/uri'; import severity from 'vs/base/common/severity'; -import { SimpleReplElement, Model, Session, Expression, RawObjectReplElement, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; +import { SimpleReplElement, Model, Expression, RawObjectReplElement, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; import * as sinon from 'sinon'; -import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; +import { MockRawSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { Session } from 'vs/workbench/parts/debug/electron-browser/debugSession'; suite('Debug - Model', () => { let model: Model; - let rawSession: MockSession; + let rawSession: MockRawSession; setup(() => { - model = new Model([], true, [], [], []); - rawSession = new MockSession(); + model = new Model([], true, [], [], [], <any>{ isDirty: (e: any) => false }); + rawSession = new MockRawSession(); }); teardown(() => { @@ -108,18 +109,18 @@ suite('Debug - Model', () => { test('threads simple', () => { const threadId = 1; const threadName = 'firstThread'; + const session = new Session('mock', { resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined); + model.addSession(session); - model.addSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, rawSession); assert.equal(model.getSessions().length, 1); model.rawUpdate({ - sessionId: rawSession.getId(), + sessionId: 'mock', threadId: threadId, thread: { id: threadId, name: threadName } }); - const session = model.getSessions().filter(p => p.getId() === rawSession.getId()).pop(); assert.equal(session.getThread(threadId).name, threadName); @@ -140,9 +141,13 @@ suite('Debug - Model', () => { const stoppedReason = 'breakpoint'; // Add the threads - model.addSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, rawSession); + const session = new Session('mock', { resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined); + model.addSession(session); + + session['_raw'] = <any>rawSession; + model.rawUpdate({ - sessionId: rawSession.getId(), + sessionId: 'mock', threadId: threadId1, thread: { id: threadId1, @@ -151,7 +156,7 @@ suite('Debug - Model', () => { }); model.rawUpdate({ - sessionId: rawSession.getId(), + sessionId: 'mock', threadId: threadId2, thread: { id: threadId2, @@ -161,7 +166,7 @@ suite('Debug - Model', () => { // Stopped event with all threads stopped model.rawUpdate({ - sessionId: rawSession.getId(), + sessionId: 'mock', threadId: threadId1, stoppedDetails: { reason: stoppedReason, @@ -169,7 +174,6 @@ suite('Debug - Model', () => { allThreadsStopped: true }, }); - const session = model.getSessions().filter(p => p.getId() === rawSession.getId()).pop(); const thread1 = session.getThread(threadId1); const thread2 = session.getThread(threadId2); @@ -230,10 +234,14 @@ suite('Debug - Model', () => { const runningThreadId = 2; const runningThreadName = 'runningThread'; const stoppedReason = 'breakpoint'; - model.addSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, rawSession); + const session = new Session('mock', { resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined); + model.addSession(session); + + session['_raw'] = <any>rawSession; + // Add the threads model.rawUpdate({ - sessionId: rawSession.getId(), + sessionId: 'mock', threadId: stoppedThreadId, thread: { id: stoppedThreadId, @@ -242,7 +250,7 @@ suite('Debug - Model', () => { }); model.rawUpdate({ - sessionId: rawSession.getId(), + sessionId: 'mock', threadId: runningThreadId, thread: { id: runningThreadId, @@ -252,7 +260,7 @@ suite('Debug - Model', () => { // Stopped event with only one thread stopped model.rawUpdate({ - sessionId: rawSession.getId(), + sessionId: 'mock', threadId: stoppedThreadId, stoppedDetails: { reason: stoppedReason, @@ -260,7 +268,6 @@ suite('Debug - Model', () => { allThreadsStopped: false } }); - const session = model.getSessions().filter(p => p.getId() === rawSession.getId()).pop(); const stoppedThread = session.getThread(stoppedThreadId); const runningThread = session.getThread(runningThreadId); @@ -341,7 +348,10 @@ suite('Debug - Model', () => { test('repl expressions', () => { assert.equal(model.getReplElements().length, 0); - const session = new Session({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, rawSession); + const session = new Session('mock', { resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined); + model.addSession(session); + + session['_raw'] = <any>rawSession; const thread = new Thread(session, 'mockthread', 1); const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1); model.addReplExpression(session, stackFrame, 'myVariable').done(); @@ -360,7 +370,9 @@ suite('Debug - Model', () => { }); test('stack frame get specific source name', () => { - const session = new Session({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, rawSession); + const session = new Session('mock', { resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined); + model.addSession(session); + let firstStackFrame: StackFrame; let secondStackFrame: StackFrame; const thread = new class extends Thread { 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 ea5ff884fe7..30e8e987ef9 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<IRawExperiment[]> { - 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 => { @@ -281,6 +279,24 @@ export class ExperimentService extends Disposable implements IExperimentService } } + private checkExperimentDependencies(experiment: IRawExperiment): boolean { + if (experiment.condition.experimentsPreviouslyRun) { + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + let includeCheck = true; + let excludeCheck = true; + if (Array.isArray(experiment.condition.experimentsPreviouslyRun.includes)) { + includeCheck = runExperimentIdsFromStorage.some(x => experiment.condition.experimentsPreviouslyRun.includes.indexOf(x) > -1); + } + if (includeCheck && Array.isArray(experiment.condition.experimentsPreviouslyRun.excludes)) { + excludeCheck = !runExperimentIdsFromStorage.some(x => experiment.condition.experimentsPreviouslyRun.excludes.indexOf(x) > -1); + } + if (!includeCheck || !excludeCheck) { + return false; + } + } + return true; + } + private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): TPromise<ExperimentState> { if (processedExperiment.state !== ExperimentState.Evaluating) { return TPromise.wrap(processedExperiment.state); @@ -294,19 +310,8 @@ export class ExperimentService extends Disposable implements IExperimentService return TPromise.wrap(ExperimentState.Run); } - if (experiment.condition.experimentsPreviouslyRun) { - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); - let includeCheck = true; - let excludeCheck = true; - if (Array.isArray(experiment.condition.experimentsPreviouslyRun.includes)) { - includeCheck = runExperimentIdsFromStorage.some(x => experiment.condition.experimentsPreviouslyRun.includes.indexOf(x) > -1); - } - if (includeCheck && Array.isArray(experiment.condition.experimentsPreviouslyRun.excludes)) { - excludeCheck = !runExperimentIdsFromStorage.some(x => experiment.condition.experimentsPreviouslyRun.excludes.indexOf(x) > -1); - } - if (!includeCheck || !excludeCheck) { - return TPromise.wrap(ExperimentState.NoRun); - } + if (!this.checkExperimentDependencies(experiment)) { + return TPromise.wrap(ExperimentState.NoRun); } if (this.environmentService.appQuality === 'stable' && experiment.condition.insidersOnly === true) { @@ -401,7 +406,7 @@ export class ExperimentService extends Disposable implements IExperimentService } }); if (latestExperimentState.editCount >= experiment.condition.fileEdits.minEditCount) { - processedExperiment.state = latestExperimentState.state = Math.random() < experiment.condition.userProbability ? ExperimentState.Run : ExperimentState.NoRun; + processedExperiment.state = latestExperimentState.state = (Math.random() < experiment.condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); if (latestExperimentState.state === ExperimentState.Run && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) { this.fireRunExperiment(processedExperiment); diff --git a/src/vs/workbench/parts/extensions/common/extensionQuery.ts b/src/vs/workbench/parts/extensions/common/extensionQuery.ts index 4a3cc885ec5..5f85a0ee0e0 100644 --- a/src/vs/workbench/parts/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/parts/extensions/common/extensionQuery.ts @@ -12,7 +12,7 @@ export class Query { this.value = value.trim(); } - static autocompletions(): string[] { + static suggestions(query: string): string[] { const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'recommended', 'sort', 'category', 'tag', 'ext']; const subcommands = { 'sort': ['installs', 'rating', 'name'], @@ -21,7 +21,23 @@ export class Query { 'ext': [''] }; - return flatten(commands.map(command => subcommands[command] ? subcommands[command].map(subcommand => `${command}:${subcommand}`) : [command])); + 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 { diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index bf50d9d44ae..8b3d72dff9f 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -8,7 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { Event } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; import { IPager } from 'vs/base/common/paging'; -import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension, IGalleryExtension, ExtensionRecommendationSource } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -52,7 +52,7 @@ export interface IExtension { extensionPack: string[]; telemetryData: any; preview: boolean; - getManifest(): TPromise<IExtensionManifest>; + getManifest(): TPromise<IExtensionManifest | undefined>; getReadme(): TPromise<string>; hasReadme(): boolean; getChangelog(): TPromise<string>; @@ -61,7 +61,6 @@ export interface IExtension { locals?: ILocalExtension[]; gallery?: IGalleryExtension; isMalicious: boolean; - recommendationSources: ExtensionRecommendationSource[]; } export interface IExtensionDependencies { @@ -96,11 +95,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/common/extensionsInput.ts b/src/vs/workbench/parts/extensions/common/extensionsInput.ts index 3a7642cd39a..fab4abc019a 100644 --- a/src/vs/workbench/parts/extensions/common/extensionsInput.ts +++ b/src/vs/workbench/parts/extensions/common/extensionsInput.ts @@ -10,17 +10,14 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput } from 'vs/workbench/common/editor'; import { IExtension } from 'vs/workbench/parts/extensions/common/extensions'; import URI from 'vs/base/common/uri'; -import { IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; export class ExtensionsInput extends EditorInput { static readonly ID = 'workbench.extensions.input2'; get extension(): IExtension { return this._extension; } - get servers(): IExtensionManagementServer[] { return this.extensionManagementServerService.extensionManagementServers; } constructor( private _extension: IExtension, - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService ) { super(); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 4f850fe16ea..22151f18517 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -9,11 +9,11 @@ import 'vs/css!./media/extensionEditor'; import { localize } from 'vs/nls'; import { TPromise, Promise } from 'vs/base/common/winjs.base'; import { marked } from 'vs/base/common/marked/marked'; -import { always } from 'vs/base/common/async'; +import { createCancelablePromise, wireCancellationToken } from 'vs/base/common/async'; import * as arrays from 'vs/base/common/arrays'; import { OS } from 'vs/base/common/platform'; import { Event, Emitter, once, chain } from 'vs/base/common/event'; -import Cache from 'vs/base/common/cache'; +import Cache, { CacheResult } from 'vs/base/common/cache'; import { Action } from 'vs/base/common/actions'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; @@ -30,7 +30,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension import { RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, ReloadAction, MaliciousStatusLabelAction, DisabledStatusLabelAction, MultiServerInstallAction, MultiServerUpdateAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { CombinedInstallAction, UpdateAction, EnableAction, DisableAction, ReloadAction, MaliciousStatusLabelAction, DisabledStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { WebviewElement } from 'vs/workbench/parts/webview/electron-browser/webviewElement'; import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -250,12 +250,6 @@ export class ExtensionEditor extends BaseEditor { if (action.id === DisableAction.ID) { return (<DisableAction>action).actionItem; } - if (action.id === MultiServerInstallAction.ID) { - return (<MultiServerInstallAction>action).actionItem; - } - if (action.id === MultiServerUpdateAction.ID) { - return (<MultiServerUpdateAction>action).actionItem; - } return null; } }); @@ -286,14 +280,13 @@ export class ExtensionEditor extends BaseEditor { setInput(input: ExtensionsInput, options: EditorOptions, token: CancellationToken): Thenable<void> { this.editorLoadComplete = false; const extension = input.extension; - const servers = input.servers; this.transientDisposables = dispose(this.transientDisposables); - this.extensionReadme = new Cache(() => extension.getReadme()); - this.extensionChangelog = new Cache(() => extension.getChangelog()); - this.extensionManifest = new Cache(() => extension.getManifest()); - this.extensionDependencies = new Cache(() => this.extensionsWorkbenchService.loadDependencies(extension)); + this.extensionReadme = new Cache(() => createCancelablePromise(token => wireCancellationToken(token, extension.getReadme()))); + this.extensionChangelog = new Cache(() => createCancelablePromise(token => wireCancellationToken(token, extension.getChangelog()))); + this.extensionManifest = new Cache(() => createCancelablePromise(token => wireCancellationToken(token, extension.getManifest()))); + this.extensionDependencies = new Cache(() => createCancelablePromise(token => wireCancellationToken(token, this.extensionsWorkbenchService.loadDependencies(extension)))); const onError = once(domEvent(this.icon, 'error')); onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables); @@ -357,6 +350,8 @@ export class ExtensionEditor extends BaseEditor { this.name.onclick = null; this.rating.onclick = null; this.publisher.onclick = null; + this.license.onclick = null; + this.license.style.display = 'none'; } if (extension.repository) { @@ -377,7 +372,7 @@ export class ExtensionEditor extends BaseEditor { const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, true); const disabledStatusAction = this.instantiationService.createInstance(DisabledStatusLabelAction); const installAction = this.instantiationService.createInstance(CombinedInstallAction); - const updateAction = servers.length === 1 ? this.instantiationService.createInstance(UpdateAction) : this.instantiationService.createInstance(MultiServerUpdateAction); + const updateAction = this.instantiationService.createInstance(UpdateAction); const enableAction = this.instantiationService.createInstance(EnableAction); const disableAction = this.instantiationService.createInstance(DisableAction); const reloadAction = this.instantiationService.createInstance(ReloadAction); @@ -429,11 +424,12 @@ export class ExtensionEditor extends BaseEditor { this.navbar.push(NavbarSection.Readme, localize('details', "Details"), localize('detailstooltip', "Extension details, rendered from the extension's 'README.md' file")); } this.extensionManifest.get() + .promise .then(manifest => { if (extension.extensionPack.length) { this.navbar.push(NavbarSection.ExtensionPack, localize('extensionPack', "Extension Pack"), localize('extensionsPack', "Set of extensions that can be installed together")); } - if (manifest.contributes) { + if (manifest && manifest.contributes) { this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"), localize('contributionstooltip', "Lists contributions to VS Code by this extension")); } if (extension.hasChangelog()) { @@ -479,8 +475,8 @@ export class ExtensionEditor extends BaseEditor { } } - private openMarkdown(content: TPromise<string>, noContentCopy: string) { - return this.loadContents(() => content + private openMarkdown(cacheResult: CacheResult<string>, noContentCopy: string): void { + this.loadContents(() => cacheResult) .then(marked.parse) .then(renderBody) .then(removeEmbeddedSVGs) @@ -507,19 +503,19 @@ export class ExtensionEditor extends BaseEditor { .then(null, () => { const p = append(this.content, $('p.nocontent')); p.textContent = noContentCopy; - })); + }); } - private openReadme() { - return this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available.")); + private openReadme(): void { + this.openMarkdown(this.extensionReadme.get(), localize('noReadme', "No README available.")); } - private openChangelog() { - return this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available.")); + private openChangelog(): void { + this.openMarkdown(this.extensionChangelog.get(), localize('noChangelog', "No Changelog available.")); } - private openContributions() { - return this.loadContents(() => this.extensionManifest.get() + private openContributions(): void { + this.loadContents(() => this.extensionManifest.get()) .then(manifest => { const content = $('div', { class: 'subcontent' }); const scrollableContent = new DomScrollableElement(content, {}); @@ -554,17 +550,17 @@ export class ExtensionEditor extends BaseEditor { } }, () => { append(this.content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions"); - })); + }); } - private openDependencies(extension: IExtension) { + private openDependencies(extension: IExtension): void { if (extension.dependencies.length === 0) { append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); return; } - return this.loadContents(() => { - return this.extensionDependencies.get().then(extensionDependencies => { + this.loadContents(() => this.extensionDependencies.get()) + .then(extensionDependencies => { const content = $('div', { class: 'subcontent' }); const scrollableContent = new DomScrollableElement(content, {}); append(this.content, scrollableContent.getDomNode()); @@ -585,7 +581,6 @@ export class ExtensionEditor extends BaseEditor { append(this.content, $('p.nocontent')).textContent = error; this.notificationService.error(error); }); - }); } private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree { @@ -617,26 +612,23 @@ export class ExtensionEditor extends BaseEditor { return this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extensionDependencies), container); } - private openExtensionPack(extension: IExtension) { - return this.loadContents(() => { - const content = $('div', { class: 'subcontent' }); - const scrollableContent = new DomScrollableElement(content, {}); - append(this.content, scrollableContent.getDomNode()); - this.contentDisposables.push(scrollableContent); + private openExtensionPack(extension: IExtension): void { + const content = $('div', { class: 'subcontent' }); + const scrollableContent = new DomScrollableElement(content, {}); + append(this.content, scrollableContent.getDomNode()); + this.contentDisposables.push(scrollableContent); - const dependenciesTree = this.renderExtensionPack(content, extension); - const layout = () => { - scrollableContent.scanDomNode(); - const scrollDimensions = scrollableContent.getScrollDimensions(); - dependenciesTree.layout(scrollDimensions.height); - }; - const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); - this.contentDisposables.push(toDisposable(removeLayoutParticipant)); - - this.contentDisposables.push(dependenciesTree); + const dependenciesTree = this.renderExtensionPack(content, extension); + const layout = () => { scrollableContent.scanDomNode(); - return TPromise.as(null); - }); + const scrollDimensions = scrollableContent.getScrollDimensions(); + dependenciesTree.layout(scrollDimensions.height); + }; + const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); + this.contentDisposables.push(toDisposable(removeLayoutParticipant)); + + this.contentDisposables.push(dependenciesTree); + scrollableContent.scanDomNode(); } private renderExtensionPack(container: HTMLElement, extension: IExtension): Tree { @@ -1073,13 +1065,16 @@ export class ExtensionEditor extends BaseEditor { return this.keybindingService.resolveKeybinding(keyBinding)[0]; } - private loadContents(loadingTask: () => TPromise<any>): void { + private loadContents<T>(loadingTask: () => CacheResult<T>): Thenable<T> { addClass(this.content, 'loading'); - let promise = loadingTask(); - promise = always(promise, () => removeClass(this.content, 'loading')); + const result = loadingTask(); + const onDone = () => removeClass(this.content, 'loading'); + result.promise.then(onDone, onDone); - this.contentDisposables.push(toDisposable(() => promise.cancel())); + this.contentDisposables.push(toDisposable(() => result.dispose())); + + return result.promise; } layout(): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index ab53ea18d8b..4e0524ccdc6 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -12,7 +12,7 @@ import { match } from 'vs/base/common/glob'; import * as json from 'vs/base/common/json'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, ExtensionRecommendationReason, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN, - IExtensionsConfigContent, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource, IExtensionManagementServerService, InstallOperation + IExtensionsConfigContent, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextModel } from 'vs/editor/common/model'; @@ -23,7 +23,7 @@ import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsA import Severity from 'vs/base/common/severity'; import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; +import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import * as pfs from 'vs/base/node/pfs'; @@ -104,7 +104,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe @IViewletService private viewletService: IViewletService, @INotificationService private notificationService: INotificationService, @IExtensionManagementService private extensionManagementService: IExtensionManagementService, - @IExtensionManagementServerService private extensionManagementServiceService: IExtensionManagementServerService, + @IExtensionsWorkbenchService private extensionWorkbenchService: IExtensionsWorkbenchService, @IExperimentService private experimentService: IExperimentService, ) { super(); @@ -179,7 +179,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } private isEnabled(): boolean { - return this._galleryService.isEnabled() && !this.environmentService.extensionDevelopmentPath; + return this._galleryService.isEnabled() && !this.environmentService.extensionDevelopmentLocationURI; } getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } { @@ -311,7 +311,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe .then(content => <IExtensionsConfigContent>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 || []))) @@ -339,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 { @@ -548,9 +545,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const importantRecommendationsIgnoreList = <string[]>JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); recommendationsToSuggest = recommendationsToSuggest.filter(id => importantRecommendationsIgnoreList.indexOf(id) === -1 && this.isExtensionAllowedToBeRecommended(id)); - const server = this.extensionManagementServiceService.getExtensionManagementServer(model.uri); - const importantTipsPromise = recommendationsToSuggest.length === 0 ? TPromise.as(null) : server.extensionManagementService.getInstalled(LocalExtensionType.User).then(local => { - const localExtensions = local.map(e => `${e.manifest.publisher.toLowerCase()}.${e.manifest.name.toLowerCase()}`); + const importantTipsPromise = recommendationsToSuggest.length === 0 ? TPromise.as(null) : this.extensionWorkbenchService.queryLocal().then(local => { + const localExtensions = local.map(e => e.id); recommendationsToSuggest = recommendationsToSuggest.filter(id => localExtensions.every(local => local !== id.toLowerCase())); if (!recommendationsToSuggest.length) { return; @@ -578,9 +574,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } */ this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'install', extensionId: name }); - - const installAction = this.instantiationService.createInstance(InstallRecommendedExtensionAction, id, server); - installAction.run().then(() => installAction.dispose()); + this.instantiationService.createInstance(InstallRecommendedExtensionAction, id).run(); } }, { label: localize('showRecommendations', "Show Recommendations"), @@ -676,8 +670,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( 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 1415dd8bcfa..af62cf9de7b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -10,8 +10,8 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; 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'; +import { IExtensionTipsService, ExtensionsLabel, ExtensionsChannelId, PreferencesLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; + import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -44,9 +44,8 @@ import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensio import { ExtensionHostProfileService } from 'vs/workbench/parts/extensions/electron-browser/extensionProfileService'; // Singletons -registerSingleton(IExtensionGalleryService, ExtensionGalleryService); -registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); +registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService); const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench); @@ -204,23 +203,32 @@ Registry.as<IConfigurationRegistry>(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 } } @@ -249,6 +257,15 @@ MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { order: 2 }); +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '1_settings', + command: { + id: VIEWLET_ID, + title: localize({ key: 'miPreferencesExtensions', comment: ['&& denotes a mnemonic'] }, "&&Extensions") + }, + order: 2 +}); + // View menu MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index ce4e5210290..0b5c603141a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -5,10 +5,10 @@ import 'vs/css!./media/extensionActions'; import { localize } from 'vs/nls'; -import * as semver from 'semver'; import { TPromise } from 'vs/base/common/winjs.base'; import { IAction, Action } from 'vs/base/common/actions'; import { Throttler } from 'vs/base/common/async'; +import severity from 'vs/base/common/severity'; import * as DOM from 'vs/base/browser/dom'; import * as paths from 'vs/base/common/paths'; import { Event } from 'vs/base/common/event'; @@ -18,7 +18,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; -import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionManagementServer, IExtensionManagementServerService, IExtensionRecommendation, ExtensionRecommendationSource, IExtensionGalleryService, IGalleryExtension, ILocalExtension, IExtensionsConfigContent } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ToggleViewletAction } from 'vs/workbench/browser/viewlet'; @@ -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'; +import { CancellationToken } from 'vs/base/common/cancellation'; 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`; @@ -74,21 +74,6 @@ const promptDownloadManually = (extension: IGalleryExtension, message: string, i }]); }; -const getExtensionManagementServerForRecommendationSource = (source: ExtensionRecommendationSource, extensionManagementServerService: IExtensionManagementServerService, contextService: IWorkspaceContextService): IExtensionManagementServer => { - if (source instanceof URI) { - return extensionManagementServerService.getExtensionManagementServer(source); - } - if (source === contextService.getWorkspace()) { - return extensionManagementServerService.getDefaultExtensionManagementServer(); - } - for (const workspaceFolder of contextService.getWorkspace().folders) { - if (source === workspaceFolder) { - return extensionManagementServerService.getExtensionManagementServer(workspaceFolder.uri); - } - } - return extensionManagementServerService.getDefaultExtensionManagementServer(); -}; - export interface IExtensionAction extends IAction { extension: IExtension; } @@ -232,8 +217,8 @@ export class UninstallAction extends Action { export class CombinedInstallAction extends Action { private static readonly NoExtensionClass = 'extension-action prominent install no-extension'; - private installAction: MultiServerInstallAction | InstallAction; - private uninstallAction: MultiServerUninstallAction | UninstallAction; + private installAction: InstallAction; + private uninstallAction: UninstallAction; private disposables: IDisposable[] = []; private _extension: IExtension; get extension(): IExtension { return this._extension; } @@ -244,13 +229,12 @@ export class CombinedInstallAction extends Action { } constructor( - @IInstantiationService instantiationService: IInstantiationService, - @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService + @IInstantiationService instantiationService: IInstantiationService ) { super('extensions.combinedInstall', '', '', false); - this.installAction = extensionManagementServerService.extensionManagementServers.length > 1 ? instantiationService.createInstance(MultiServerInstallAction, false) : instantiationService.createInstance(InstallAction); - this.uninstallAction = extensionManagementServerService.extensionManagementServers.length > 1 ? instantiationService.createInstance(MultiServerUninstallAction) : instantiationService.createInstance(UninstallAction); + this.installAction = instantiationService.createInstance(InstallAction); + this.uninstallAction = instantiationService.createInstance(UninstallAction); this.disposables.push(this.installAction, this.uninstallAction); this.installAction.onDidChange(this.update, this, this.disposables); @@ -376,383 +360,6 @@ export class UpdateAction extends Action { } } -export class InstallGalleryExtensionAction extends Action { - - private _server: IExtensionManagementServer; - private _extension: IGalleryExtension; - get extension(): IGalleryExtension { return this._extension; } - set extension(extension: IGalleryExtension) { this._extension = extension; this.enabled = !!this._extension; } - - constructor( - id: string, label: string, server: IExtensionManagementServer, - @INotificationService private notificationService: INotificationService, - @IInstantiationService private instantiationService: IInstantiationService, - @IOpenerService private openerService: IOpenerService - ) { - super(id, label, null, false); - this._server = server; - } - - run(): TPromise<any> { - if (this.extension) { - return this._server.extensionManagementService.installFromGallery(this.extension) - .then(() => null, err => { - console.error(err); - promptDownloadManually(this.extension, localize('failedToInstall', "Failed to install \'{0}\'.", this.extension.identifier.id), this.instantiationService, this.notificationService, this.openerService); - }); - } - return TPromise.as(null); - } -} - -export class UninstallExtensionAction extends Action { - - private _server: IExtensionManagementServer; - private _extension: ILocalExtension; - get extension(): ILocalExtension { return this._extension; } - set extension(extension: ILocalExtension) { this._extension = extension; this.enabled = !!this._extension; } - - constructor( - id: string, label: string, server: IExtensionManagementServer, - ) { - super(id, label, null, false); - this._server = server; - } - - run(): TPromise<any> { - if (this.extension) { - return this._server.extensionManagementService.uninstall(this.extension); - } - return TPromise.as(null); - } -} - -export class UpdateGalleryExtensionAction extends Action { - - private server: IExtensionManagementServer; - - private local: ILocalExtension; - private gallery: IGalleryExtension; - get extension(): { local: ILocalExtension, gallery: IGalleryExtension } { return { local: this.local, gallery: this.gallery }; } - set extension(extension: { local: ILocalExtension, gallery: IGalleryExtension }) { this.local = extension ? extension.local : null; this.gallery = extension ? extension.gallery : null; this.update(); } - - constructor( - id: string, label: string, server: IExtensionManagementServer, - @INotificationService private notificationService: INotificationService, - @IInstantiationService private instantiationService: IInstantiationService, - @IOpenerService private openerService: IOpenerService - ) { - super(id, label, null, false); - this.server = server; - } - - private update(): void { - this.enabled = this.local && this.gallery && this.local.type === LocalExtensionType.User && semver.gt(this.gallery.version, this.local.manifest.version); - this.label = this.enabled ? localize('updateToInServer', "Update to {0} ({1})", this.gallery.version, this.server.location.authority) : localize('updateLabelInServer', "Update ({0})", this.server.location.authority); - } - - run(): TPromise<any> { - if (this.gallery) { - return this.server.extensionManagementService.installFromGallery(this.gallery) - .then(() => null, err => { - console.error(err); - promptDownloadManually(this.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", this.gallery.identifier.id), this.instantiationService, this.notificationService, this.openerService); - }); - } - return TPromise.as(null); - } -} - -export class MultiServerInstallAction extends Action { - - static ID: string = 'extensions.multiserver.install'; - - private static readonly InstallLabel = localize('installAction', "Install"); - private static readonly InstallingLabel = localize('installing', "Installing"); - - private static readonly Class = 'extension-action multiserver prominent install'; - private static readonly InstallingClass = 'extension-action multiserver install installing'; - - private readonly disableWhenInstalled: boolean; - - readonly actions: InstallGalleryExtensionAction[] = []; - private _actionItem: DropDownMenuActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - private disposables: IDisposable[] = []; - - constructor( - disableWhenInstalled: boolean, - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { - super(MultiServerInstallAction.ID, MultiServerInstallAction.InstallLabel, MultiServerInstallAction.Class, false); - this.disableWhenInstalled = disableWhenInstalled; - this.actions = this.extensionManagementServerService.extensionManagementServers.map(server => this.instantiationService.createInstance(InstallGalleryExtensionAction, `extensions.install.${server.location.authority}`, localize('installInServer', "{0}", server.location.authority), server)); - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this.actions]); - this.disposables.push(...[this._actionItem, ...this.actions]); - this.disposables.push(this.extensionsWorkbenchService.onChange(() => { - if (this.extension) { - this.extension = this.extensionsWorkbenchService.local.filter(l => areSameExtensions({ id: l.id }, { id: this.extension.id }))[0] || this.extension; - } - })); - this.update(); - } - - private update(): void { - if (!this.extension || this.extension.type === LocalExtensionType.System) { - this.enabled = false; - this.class = MultiServerInstallAction.Class; - this.label = MultiServerInstallAction.InstallLabel; - return; - } - - if (this.extension.state === ExtensionState.Installing) { - this.label = MultiServerInstallAction.InstallingLabel; - this.class = MultiServerInstallAction.InstallingClass; - this.tooltip = MultiServerInstallAction.InstallingLabel; - } else { - this.label = MultiServerInstallAction.InstallLabel; - this.class = MultiServerInstallAction.Class; - this.tooltip = MultiServerInstallAction.InstallLabel; - } - - const isInstalled = this.extension.locals.length > 0; - - if (isInstalled && this.disableWhenInstalled) { - this.enabled = false; - return; - } - - let isExtensionNotInstalledInRecommendedServer: boolean = false; - this.actions.forEach((installAction, index) => { - const server = this.extensionManagementServerService.extensionManagementServers[index]; - installAction.extension = this.extension.gallery; - installAction.label = localize('installInServer', "{0}", server.location.authority); - installAction.enabled = this.extension.gallery && !this.extension.locals.some(local => this.extensionManagementServerService.getExtensionManagementServer(local.location) === server); - if (this.extension.recommendationSources && this.extension.recommendationSources.length) { - if (this.extension.recommendationSources.some(recommendationSource => getExtensionManagementServerForRecommendationSource(recommendationSource, this.extensionManagementServerService, this.contextService) === server)) { - installAction.label = localize('installInRecommendedServer', "{0} (Recommended)", server.location.authority); - isExtensionNotInstalledInRecommendedServer = isExtensionNotInstalledInRecommendedServer || installAction.enabled; - } - } - }); - - this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && (isExtensionNotInstalledInRecommendedServer || this.extension.locals.length === 0); - } - - public run(): TPromise<any> { - this._actionItem.showMenu(); - return TPromise.wrap(null); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class MultiServerInstallSubMenuAction extends ContextSubMenu { - - private readonly action: MultiServerInstallAction; - private disposables: IDisposable[] = []; - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.action.extension = extension; } - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - ) { - super('', []); - this.action = instantiationService.createInstance(MultiServerInstallAction, false); - this.disposables.push(this.action); - this.entries = this.action.actions; - this.disposables.push(this.onDidChange(() => this.update())); - this.update(); - } - - private update(): void { - this.label = this.action.label; - this.enabled = this.action.enabled; - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class MultiServerUnInstallSubMenuAction extends ContextSubMenu { - - private readonly action: MultiServerUninstallAction; - private disposables: IDisposable[] = []; - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.action.extension = extension; } - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - ) { - super('', []); - this.action = instantiationService.createInstance(MultiServerUninstallAction); - this.disposables.push(this.action); - this.entries = this.action.actions; - this.disposables.push(this.onDidChange(() => this.update())); - this.update(); - } - - private update(): void { - this.label = this.action.label; - this.enabled = this.action.enabled; - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class MultiServerUpdateAction extends Action { - - static ID: string = 'extensions.multiserver.update'; - - private static readonly Class = 'extension-action multiserver prominent update'; - - private _updateActions: UpdateGalleryExtensionAction[] = []; - private _actionItem: DropDownMenuActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor( - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService - ) { - super(MultiServerUpdateAction.ID, localize('update', "Update"), MultiServerUpdateAction.Class, false); - this._updateActions = this.extensionManagementServerService.extensionManagementServers.map(server => this.instantiationService.createInstance(UpdateGalleryExtensionAction, `extensions.update.${server.location.authority}`, localize('installInServer', "{0}", server.location.authority), server)); - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._updateActions]); - this.disposables.push(this._actionItem); - this.disposables.push(...this._updateActions); - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.update())); - this.update(); - } - - private update(): void { - this._updateActions.forEach((updateAction, index) => { - updateAction.extension = null; - if (this.extension && this.extension.locals && this.extension.gallery) { - const server = this.extensionManagementServerService.extensionManagementServers[index]; - const local = this.extension.locals.filter(local => this.extensionManagementServerService.getExtensionManagementServer(local.location) === server)[0]; - updateAction.extension = { local, gallery: this.extension.gallery }; - } - }); - this.enabled = this._updateActions.some(action => action.enabled); - } - - public run(): TPromise<any> { - this._actionItem.showMenu(); - return TPromise.wrap(null); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - -export class MultiServerUninstallAction extends Action { - - static ID: string = 'extensions.multiserver.uninstall'; - - private static readonly UninstallLabel = localize('uninstallAction', "Uninstall"); - private static readonly UninstallingLabel = localize('Uninstalling', "Uninstalling"); - - private static readonly UninstallClass = 'extension-action uninstall'; - private static readonly UnInstallingClass = 'extension-action uninstall uninstalling'; - - readonly actions: UninstallExtensionAction[] = []; - private _actionItem: DropDownMenuActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - private disposables: IDisposable[] = []; - - constructor( - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - ) { - super(MultiServerUninstallAction.ID, MultiServerUninstallAction.UninstallLabel, MultiServerUninstallAction.UninstallClass, false); - this.actions = this.extensionManagementServerService.extensionManagementServers.map(server => this.instantiationService.createInstance(UninstallExtensionAction, `extensions.uninstall.${server.location.authority}`, server.location.authority, server)); - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this.actions]); - this.disposables.push(...[this._actionItem, ...this.actions]); - this.disposables.push(this.extensionsWorkbenchService.onChange(() => this.extension = this.extension ? this.extensionsWorkbenchService.local.filter(l => areSameExtensions({ id: l.id }, { id: this.extension.id }))[0] : this.extension)); - this.update(); - } - - private update(): void { - if (!this.extension) { - this.enabled = false; - } else { - const state = this.extension.state; - - if (state === ExtensionState.Uninstalling) { - this.label = MultiServerUninstallAction.UninstallingLabel; - this.class = MultiServerUninstallAction.UnInstallingClass; - this.enabled = false; - return; - } - - this.label = MultiServerUninstallAction.UninstallLabel; - this.class = MultiServerUninstallAction.UninstallClass; - - const installedExtensions = this.extensionsWorkbenchService.local.filter(e => e.id === this.extension.id); - - if (!installedExtensions.length) { - this.enabled = false; - return; - } - - if (installedExtensions[0].type !== LocalExtensionType.User) { - this.enabled = false; - return; - } - - this.enabled = true; - - this.actions.forEach((installAction, index) => { - const server = this.extensionManagementServerService.extensionManagementServers[index]; - installAction.extension = this.extension.locals.filter(local => this.extensionManagementServerService.getExtensionManagementServer(local.location) === server)[0]; - }); - } - } - - public run(): TPromise<any> { - this._actionItem.showMenu(); - return TPromise.wrap(null); - } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } -} - export class DropDownMenuActionItem extends ActionItem { private disposables: IDisposable[] = []; @@ -770,7 +377,7 @@ export class DropDownMenuActionItem extends ActionItem { public showMenu(): void { const actions = this.getActions(); - let elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); + let elementPosition = DOM.getDomNodePagePosition(this.element); const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, @@ -811,8 +418,7 @@ export class ManageExtensionAction extends Action { constructor( @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionManagementServerService private extensionManagmentServerService: IExtensionManagementServerService + @IInstantiationService private instantiationService: IInstantiationService ) { super(ManageExtensionAction.ID); @@ -833,12 +439,7 @@ export class ManageExtensionAction extends Action { this.instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL), this.instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL) ]); - if (this.extensionManagmentServerService.extensionManagementServers.length > 1) { - groups.push([this.instantiationService.createInstance(MultiServerInstallSubMenuAction)]); - groups.push([this.instantiationService.createInstance(MultiServerUnInstallSubMenuAction)]); - } else { - groups.push([this.instantiationService.createInstance(UninstallAction)]); - } + groups.push([this.instantiationService.createInstance(UninstallAction)]); return groups; } @@ -1156,18 +757,37 @@ export class DisableAction extends Action { export class CheckForUpdatesAction extends Action { static readonly ID = 'workbench.extensions.action.checkForUpdates'; - static LABEL = localize('checkForUpdates', "Check for Updates"); + static LABEL = localize('checkForUpdates', "Check for Extension Updates"); constructor( - id = UpdateAllAction.ID, - label = UpdateAllAction.LABEL, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + id = CheckForUpdatesAction.ID, + label = CheckForUpdatesAction.LABEL, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IViewletService private viewletService: IViewletService, + @INotificationService private notificationService: INotificationService ) { super(id, label, '', true); } + private checkUpdatesAndNotify(): void { + this.extensionsWorkbenchService.queryLocal().then( + extensions => { + const outdatedCount = extensions.filter(ext => ext.outdated === true).length; + let msgAvailableExtensions = localize('noUpdatesAvailable', "All Extensions are up to date."); + if (outdatedCount > 0) { + msgAvailableExtensions = outdatedCount === 1 ? localize('updateAvailable', "An Extension update is available.") + : localize('updatesAvailable', "{0} extensions updates are available.", outdatedCount); + this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet.search('')); + } + this.notificationService.notify({ severity: severity.Info, message: msgAvailableExtensions }); + } + ); + } + run(): TPromise<any> { - return this.extensionsWorkbenchService.checkForUpdates(); + return this.extensionsWorkbenchService.checkForUpdates().then(() => this.checkUpdatesAndNotify()); } } @@ -1289,8 +909,8 @@ export class ReloadAction extends Action { @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @IWindowService private windowService: IWindowService, @IExtensionService private extensionService: IExtensionService, - @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService ) { super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); this.throttler = new Throttler(); @@ -1326,29 +946,26 @@ export class ReloadAction extends Action { if (installed && installed.local) { if (runningExtension) { - const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation); - const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); - const isSameLocation = runningExtensionServer.location.toString() === installedExtensionServer.location.toString(); - if (isSameLocation) { - const isDifferentVersionRunning = this.extension.version !== runningExtension.version; - if (isDifferentVersionRunning && !isDisabled) { - // Requires reload to run the updated extension - this.enabled = true; - this.tooltip = localize('postUpdateTooltip', "Reload to update"); - this.reloadMessage = localize('postUpdateMessage', "Reload this window to activate the updated extension '{0}'?", this.extension.displayName); - return; - } - if (isDisabled) { - // Requires reload to disable the extension - this.enabled = true; - this.tooltip = localize('postDisableTooltip', "Reload to deactivate"); - this.reloadMessage = localize('postDisableMessage', "Reload this window to deactivate the extension '{0}'?", this.extension.displayName); - return; - } + const isDifferentVersionRunning = this.extension.version !== runningExtension.version; + if (isDifferentVersionRunning && !isDisabled) { + // Requires reload to run the updated extension + this.enabled = true; + this.tooltip = localize('postUpdateTooltip', "Reload to update"); + this.reloadMessage = localize('postUpdateMessage', "Reload this window to activate the updated extension '{0}'?", this.extension.displayName); + return; + } + if (isDisabled) { + // Requires reload to disable the extension + this.enabled = true; + this.tooltip = localize('postDisableTooltip', "Reload to deactivate"); + this.reloadMessage = localize('postDisableMessage', "Reload this window to deactivate the extension '{0}'?", this.extension.displayName); + return; } - return; } else { - if (!isDisabled) { + const extensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); + const localServer = this.extensionManagementServerService.getLocalExtensionManagementServer(); + // Only extension from local server requires reload if it is not running on the server + if (extensionServer && extensionServer.authority === localServer.authority && !isDisabled) { // Requires reload to enable the extension this.enabled = true; this.tooltip = localize('postEnableTooltip', "Reload to activate"); @@ -1595,8 +1212,6 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { static readonly ID = 'workbench.extensions.action.installWorkspaceRecommendedExtensions'; static LABEL = localize('installWorkspaceRecommendedExtensions', "Install All Workspace Recommended Extensions"); - private disposables: IDisposable[] = []; - private _recommendations: IExtensionRecommendation[] = []; get recommendations(): IExtensionRecommendation[] { return this._recommendations; } set recommendations(recommendations: IExtensionRecommendation[]) { this._recommendations = recommendations; this.enabled = this._recommendations.length > 0; } @@ -1605,13 +1220,11 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { id: string = InstallWorkspaceRecommendedExtensionsAction.ID, label: string = InstallWorkspaceRecommendedExtensionsAction.LABEL, recommendations: IExtensionRecommendation[], - @IWorkspaceContextService private contextService: IWorkspaceContextService, @IViewletService private viewletService: IViewletService, @INotificationService private notificationService: INotificationService, @IInstantiationService private instantiationService: IInstantiationService, @IOpenerService private openerService: IOpenerService, - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, - @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService + @IExtensionsWorkbenchService private extensionWorkbenchService: IExtensionsWorkbenchService ) { super(id, label, 'extension-action'); this.recommendations = recommendations; @@ -1623,52 +1236,25 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { .then(viewlet => { viewlet.search('@recommended '); viewlet.focus(); - if (this.recommendations.length === 0) { - this.notificationService.info(localize('extensionInstalled', "The recommended extension has already been installed")); - return TPromise.as(null); - } - const names = this.recommendations.map(({ extensionId }) => extensionId); - return this.extensionGalleryService.query({ names, source: 'install-all-workspace-recommendations' }).then(pager => { + return this.extensionWorkbenchService.queryGallery({ names, source: 'install-all-workspace-recommendations' }).then(pager => { let installPromises = []; let model = new PagedModel(pager); for (let i = 0; i < pager.total; i++) { - installPromises.push(model.resolve(i).then(e => { - return this.install(e); + installPromises.push(model.resolve(i, CancellationToken.None).then(e => { + return this.extensionWorkbenchService.install(e).then(null, err => { + console.error(err); + promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.id), this.instantiationService, this.notificationService, this.openerService); + }); })); } return TPromise.join(installPromises); }); }); } - - private install(extension: IGalleryExtension): TPromise<void> { - const servers: IExtensionManagementServer[] = []; - const recommendation = this.recommendations.filter(r => areSameExtensions({ id: r.extensionId }, extension.identifier))[0]; - if (recommendation) { - for (const source of recommendation.sources || []) { - const server = getExtensionManagementServerForRecommendationSource(source, this.extensionManagementServerService, this.contextService); - if (servers.indexOf(server) === -1) { - servers.push(server); - } - } - } - if (!servers.length) { - servers.push(this.extensionManagementServerService.getDefaultExtensionManagementServer()); - } - return TPromise.join(servers.map(server => server.extensionManagementService.installFromGallery(extension).then(null, err => { - console.error(err); - promptDownloadManually(extension, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), this.instantiationService, this.notificationService, this.openerService); - }))).then(() => null); - } - - dispose(): void { - this.disposables = dispose(this.disposables); - super.dispose(); - } } -export class InstallRecommendedExtensionAction extends InstallGalleryExtensionAction { +export class InstallRecommendedExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.installRecommendedExtension'; static LABEL = localize('installRecommendedExtension', "Install Recommended Extension"); @@ -1676,14 +1262,14 @@ export class InstallRecommendedExtensionAction extends InstallGalleryExtensionAc private extensionId: string; constructor( - extensionId: string, server: IExtensionManagementServer, + extensionId: string, @IViewletService private viewletService: IViewletService, - @INotificationService notificationService: INotificationService, - @IInstantiationService instantiationService: IInstantiationService, - @IOpenerService openerService: IOpenerService, - @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService + @INotificationService private notificationService: INotificationService, + @IInstantiationService private instantiationService: IInstantiationService, + @IOpenerService private openerService: IOpenerService, + @IExtensionsWorkbenchService private extensionWorkbenchService: IExtensionsWorkbenchService ) { - super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, server, notificationService, instantiationService, openerService); + super(InstallRecommendedExtensionAction.ID, InstallRecommendedExtensionAction.LABEL, null, false); this.extensionId = extensionId; } @@ -1693,12 +1279,17 @@ export class InstallRecommendedExtensionAction extends InstallGalleryExtensionAc .then(viewlet => { viewlet.search('@recommended '); viewlet.focus(); - return this.extensionGalleryService.query({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }) + return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }) .then(pager => { if (pager && pager.firstPage && pager.firstPage.length) { - this.extension = pager.firstPage[0]; + const extension = pager.firstPage[0]; + return this.extensionWorkbenchService.install(extension) + .then(() => null, err => { + console.error(err); + promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + }); } - return super.run(); + return null; }); }); } @@ -2694,7 +2285,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 +2297,22 @@ export class ReinstallAction extends Action { } run(): TPromise<any> { - 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<IPickOpenEntry[]> { + private getEntries() { return this.extensionsWorkbenchService.queryLocal() .then(local => { - const entries: IPickOpenEntry[] = local + const entries = local .filter(extension => extension.type === LocalExtensionType.User) .map(extension => { - return <IPickOpenEntry>{ + return { id: extension.id, label: extension.displayName, description: extension.id, - run: () => this.reinstallExtension(extension), - }; + extension, + } as (IQuickPickItem & { extension: IExtension }); }); return entries; }); @@ -2752,13 +2344,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..961904fe50b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -16,7 +16,7 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { once } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; -import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, DisabledStatusLabelAction, MultiServerInstallAction, MultiServerUpdateAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, DisabledStatusLabelAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Label, RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -89,12 +89,6 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> { if (action.id === ManageExtensionAction.ID) { return (<ManageExtensionAction>action).actionItem; } - if (action.id === MultiServerInstallAction.ID) { - return (<MultiServerInstallAction>action).actionItem; - } - if (action.id === MultiServerUpdateAction.ID) { - return (<MultiServerUpdateAction>action).actionItem; - } return null; } }); @@ -106,10 +100,8 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> { const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, false); const disabledStatusAction = this.instantiationService.createInstance(DisabledStatusLabelAction); - const installAction = this.extensionManagementServerService.extensionManagementServers.length === 1 ? this.instantiationService.createInstance(InstallAction) - : this.instantiationService.createInstance(MultiServerInstallAction, true); - const updateAction = this.extensionManagementServerService.extensionManagementServers.length === 1 ? this.instantiationService.createInstance(UpdateAction) - : this.instantiationService.createInstance(MultiServerUpdateAction); + const installAction = this.instantiationService.createInstance(InstallAction); + const updateAction = this.instantiationService.createInstance(UpdateAction); const reloadAction = this.instantiationService.createInstance(ReloadAction); const manageAction = this.instantiationService.createInstance(ManageExtensionAction); @@ -156,7 +148,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> { this.extensionService.getExtensions().then(runningExtensions => { if (installed && installed.local) { const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); - const isSameExtensionRunning = runningExtensions.some(e => areSameExtensions(e, extension) && installedExtensionServer.location.toString() === this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation).location.toString()); + const isSameExtensionRunning = runningExtensions.some(e => areSameExtensions(e, extension) && installedExtensionServer.authority === this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation).authority); toggleClass(data.root, 'disabled', !isSameExtensionRunning); } else { removeClass(data.root, 'disabled'); @@ -189,8 +181,10 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> { data.extension = extension; 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); } + if (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); } + } }); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 0c222ed08c5..0deb9a35143 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -6,21 +6,18 @@ '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, Emitter, chain } from 'vs/base/common/event'; +import { Event as EventOf, Emitter } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -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, $, addClass, removeClass, toggleClass, Dimension } from 'vs/base/browser/dom'; +import { append, $, addClass, 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'; @@ -32,14 +29,13 @@ import { } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { LocalExtensionType, IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; -import { ExtensionsListView, InstalledExtensionsView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, GroupByServerExtensionsView, DefaultRecommendedExtensionsView } from './extensionsViews'; +import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, GroupByServerExtensionsView, DefaultRecommendedExtensionsView } from './extensionsViews'; import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; 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, 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'; @@ -59,12 +55,7 @@ import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/e 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 { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { ITextModel } from 'vs/editor/common/model'; +import { SuggestEnabledInput } from 'vs/workbench/parts/codeEditor/browser/suggestEnabledInput'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -73,7 +64,7 @@ interface SearchInputEvent extends Event { const NonEmptyWorkspaceContext = new RawContextKey<boolean>('nonEmptyWorkspace', false); const SearchExtensionsContext = new RawContextKey<boolean>('searchExtensions', false); -const SearchInstalledExtensionsContext = new RawContextKey<boolean>('searchInstalledExtensions', false); +const HasInstalledExtensionsContext = new RawContextKey<boolean>('hasInstalledExtensions', true); const SearchBuiltInExtensionsContext = new RawContextKey<boolean>('searchBuiltInExtensions', false); const RecommendedExtensionsContext = new RawContextKey<boolean>('recommendedExtensions', false); const DefaultRecommendedExtensionsContext = new RawContextKey<boolean>('defaultRecommendedExtensions', false); @@ -92,10 +83,10 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push(this.createMarketPlaceExtensionsListViewDescriptor()); viewDescriptors.push(this.createEnabledExtensionsListViewDescriptor()); viewDescriptors.push(this.createDisabledExtensionsListViewDescriptor()); - viewDescriptors.push(this.createSearchInstalledExtensionsListViewDescriptor()); - viewDescriptors.push(this.createSearchBuiltInExtensionsListViewDescriptor()); - viewDescriptors.push(this.createSearchBuiltInBasicsExtensionsListViewDescriptor()); - viewDescriptors.push(this.createSearchBuiltInThemesExtensionsListViewDescriptor()); + viewDescriptors.push(this.createPopularExtensionsListViewDescriptor()); + viewDescriptors.push(this.createBuiltInExtensionsListViewDescriptor()); + viewDescriptors.push(this.createBuiltInBasicsExtensionsListViewDescriptor()); + viewDescriptors.push(this.createBuiltInThemesExtensionsListViewDescriptor()); viewDescriptors.push(this.createDefaultRecommendedExtensionsListViewDescriptor()); viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor()); viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor()); @@ -126,8 +117,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio name: localize('enabledExtensions', "Enabled"), container: VIEW_CONTAINER, ctor: EnabledExtensionsView, - when: ContextKeyExpr.not('searchExtensions'), - weight: 30, + when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')), + weight: 40, canToggleVisibility: true, order: 1 }; @@ -139,7 +130,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio name: localize('disabledExtensions', "Disabled"), container: VIEW_CONTAINER, ctor: DisabledExtensionsView, - when: ContextKeyExpr.not('searchExtensions'), + when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')), weight: 10, canToggleVisibility: true, order: 3, @@ -147,21 +138,22 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } - private createSearchInstalledExtensionsListViewDescriptor(): IViewDescriptor { + private createPopularExtensionsListViewDescriptor(): IViewDescriptor { return { - id: 'extensions.searchInstalledList', - name: localize('searchInstalledExtensions', "Installed"), + id: 'extensions.popularExtensionsList', + name: localize('popularExtensions', "Popular"), container: VIEW_CONTAINER, - ctor: InstalledExtensionsView, - when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions'), ContextKeyExpr.not('groupByServersContext')), - weight: 100 + ctor: ExtensionsListView, + when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.not('hasInstalledExtensions')), + weight: 60, + order: 1 }; } private createExtensionsViewDescriptorsForServer(server: IExtensionManagementServer): IViewDescriptor[] { return [{ - id: `server.extensionsList.${server.location.toString()}`, - name: server.location.authority, + id: `server.extensionsList.${server.authority}`, + name: server.label, container: VIEW_CONTAINER, ctor: GroupByServerExtensionsView, when: ContextKeyExpr.has('groupByServersContext'), @@ -176,7 +168,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio container: VIEW_CONTAINER, ctor: DefaultRecommendedExtensionsView, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')), - weight: 70, + weight: 40, order: 2, canToggleVisibility: true }; @@ -208,7 +200,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } - private createSearchBuiltInExtensionsListViewDescriptor(): IViewDescriptor { + private createBuiltInExtensionsListViewDescriptor(): IViewDescriptor { return { id: 'extensions.builtInExtensionsList', name: localize('builtInExtensions', "Features"), @@ -220,7 +212,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } - private createSearchBuiltInThemesExtensionsListViewDescriptor(): IViewDescriptor { + private createBuiltInThemesExtensionsListViewDescriptor(): IViewDescriptor { return { id: 'extensions.builtInThemesExtensionsList', name: localize('builtInThemesExtensions', "Themes"), @@ -232,7 +224,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } - private createSearchBuiltInBasicsExtensionsListViewDescriptor(): IViewDescriptor { + private createBuiltInBasicsExtensionsListViewDescriptor(): IViewDescriptor { return { id: 'extensions.builtInBasicsExtensionsList', name: localize('builtInBasicsExtensions', "Programming Languages"), @@ -250,7 +242,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio private onSearchChange: EventOf<string>; private nonEmptyWorkspaceContextKey: IContextKey<boolean>; private searchExtensionsContextKey: IContextKey<boolean>; - private searchInstalledExtensionsContextKey: IContextKey<boolean>; + private hasInstalledExtensionsContextKey: IContextKey<boolean>; private searchBuiltInExtensionsContextKey: IContextKey<boolean>; private groupByServersContextKey: IContextKey<boolean>; private recommendedExtensionsContextKey: IContextKey<boolean>; @@ -259,14 +251,12 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio private searchDelayer: ThrottledDelayer<any>; private root: HTMLElement; - private searchBox: CodeEditorWidget; + private searchBox: SuggestEnabledInput; 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, @@ -284,15 +274,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, - @IModelService private modelService: IModelService, + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService ) { super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); this.searchDelayer = new ThrottledDelayer(500); this.nonEmptyWorkspaceContextKey = NonEmptyWorkspaceContext.bindTo(contextKeyService); this.searchExtensionsContextKey = SearchExtensionsContext.bindTo(contextKeyService); - this.searchInstalledExtensionsContextKey = SearchInstalledExtensionsContext.bindTo(contextKeyService); + this.hasInstalledExtensionsContextKey = HasInstalledExtensionsContext.bindTo(contextKeyService); this.searchBuiltInExtensionsContextKey = SearchBuiltInExtensionsContext.bindTo(contextKeyService); this.recommendedExtensionsContextKey = RecommendedExtensionsContext.bindTo(contextKeyService); this.groupByServersContextKey = GroupByServersContext.bindTo(contextKeyService); @@ -300,6 +289,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(ShowRecommendationsOnlyOnDemandKey)); this.disposables.push(this.viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables)); + this.extensionManagementService.getInstalled(LocalExtensionType.User).then(result => { + this.hasInstalledExtensionsContextKey.set(result.length > 0); + }); + this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { this.secondaryActions = null; @@ -309,28 +302,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue<boolean>(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: <modes.SuggestionType>'keyword' - })) - }; - } - }); } create(parent: HTMLElement): TPromise<void> { @@ -338,57 +309,38 @@ 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, SEARCH_INPUT_OPTIONS, { isSimpleWidget: true }); - this.placeholderText = append(this.monacoStyleContainer, $('.search-placeholder', null, localize('searchExtensions', "Search Extensions in Marketplace"))); + + const placeholder = localize('searchExtensions', "Search Extensions in Marketplace"); + + this.searchBox = this.instantiationService.createInstance(SuggestEnabledInput, `${VIEWLET_ID}.searchbox`, header, { + triggerCharacters: ['@'], + sortKey: item => { + 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'; } + }, + provideResults: (query) => Query.suggestions(query) + }, placeholder, 'extensions:searchinput', { placeholderText: placeholder }); + + this.disposables.push(this.searchBox); + + const _searchChange = new Emitter<string>(); + this.onSearchChange = _searchChange.event; + this.searchBox.onInputDidChange(() => { + this.triggerSearch(); + _searchChange.fire(this.searchBox.getValue()); + }, this, this.disposables); + + this.searchBox.onShouldFocusResults(() => this.focusListView(), this, this.disposables); this.extensionsBox = append(this.root, $('.extensions')); - - this.searchBox.setModel(this.modelService.createModel('', null, uri.parse('extensions:searchinput'), true)); - - this.disposables.push(this.searchBox.onDidFocusEditorText(() => addClass(this.monacoStyleContainer, 'synthetic-focus'))); - this.disposables.push(this.searchBox.onDidBlurEditorText(() => removeClass(this.monacoStyleContainer, 'synthetic-focus'))); - - 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).on(() => this.focusListView(), this, this.disposables); - - const searchChangeEvent = new Emitter<string>(); - this.onSearchChange = searchChangeEvent.event; - - this.disposables.push(this.searchBox.getModel().onDidChangeContent(() => { - this.triggerSearch(); - const content = this.searchBox.getValue(); - searchChangeEvent.fire(content); - this.placeholderText.style.visibility = content ? 'hidden' : 'visible'; - })); - - return super.create(this.extensionsBox) - .then(() => this.extensionManagementService.getInstalled(LocalExtensionType.User)) - .then(installed => { - if (installed.length === 0) { - this.searchBox.setValue('@sort:installs'); - this.searchExtensionsContextKey.set(true); - } - }); + return super.create(this.extensionsBox); } public updateStyles(): void { super.updateStyles(); - - 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.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); - } + this.searchBox.updateStyles(); } setVisible(visible: boolean): TPromise<void> { @@ -397,7 +349,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio if (isVisibilityChanged) { if (visible) { this.searchBox.focus(); - this.searchBox.setSelection(new Range(1, 1, 1, this.searchBox.getValue().length + 1)); + this.searchBox.selectAll(); } } }); @@ -409,9 +361,7 @@ 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 - 30 }); - this.placeholderText.style.width = '' + (dimension.width - 30) + 'px'; - + this.searchBox.layout({ height: 20, width: dimension.width - 34 }); super.layout(new Dimension(dimension.width, dimension.height - 38)); } @@ -433,7 +383,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio if (!this.groupByServerAction) { this.groupByServerAction = this.instantiationService.createInstance(ChangeGroupAction, 'extensions.group.servers', localize('group by servers', "Group By: Server"), this.onSearchChange, 'server'); this.disposables.push(this.onSearchChange(value => { - this.groupByServerAction.enabled = !value || InstalledExtensionsView.isInstalledExtensionsQuery(value) || ExtensionsListView.isBuiltInExtensionsQuery(value); + this.groupByServerAction.enabled = !value || ExtensionsListView.isInstalledExtensionsQuery(value) || ExtensionsListView.isBuiltInExtensionsQuery(value); })); } this.secondaryActions = [ @@ -474,13 +424,12 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio } private normalizedQuery(): string { - return (this.searchBox.getValue() || '').replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:'); + return this.searchBox.getValue().replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:'); } private doSearch(): TPromise<any> { const value = this.normalizedQuery(); this.searchExtensionsContextKey.set(!!value); - this.searchInstalledExtensionsContextKey.set(InstalledExtensionsView.isInstalledExtensionsQuery(value)); this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value)); this.groupByServersContextKey.set(ExtensionsListView.isGroupByServersExtensionsQuery(value)); this.recommendedExtensionsContextKey.set(ExtensionsListView.isRecommendedExtensionsQuery(value)); @@ -500,7 +449,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { for (const extensionManagementServer of this.extensionManagementServerService.extensionManagementServers) { - if (viewDescriptor.id === `server.extensionsList.${extensionManagementServer.location.toString()}`) { + if (viewDescriptor.id === `server.extensionsList.${extensionManagementServer.authority}`) { const servicesCollection: ServiceCollection = new ServiceCollection(); servicesCollection.set(IExtensionManagementServerService, new SingleServerExtensionManagementServerService(extensionManagementServer)); servicesCollection.set(IExtensionManagementService, extensionManagementServer.extensionManagementService); @@ -512,15 +461,6 @@ 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 }[] { - if (query.lastIndexOf('@', position - 1) !== query.lastIndexOf(' ', position - 1) + 1) { return []; } - - let wordStart = query.lastIndexOf('@', position - 1) + 1; - let alreadyTypedCount = position - wordStart - 1; - - return Query.autocompletions().map(replacement => ({ fullText: replacement, overwrite: alreadyTypedCount })); - } - private count(): number { return this.panels.reduce((count, view) => (<ExtensionsListView>view).count() + count, 0); } @@ -662,34 +602,4 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { dispose(): void { this.disposables = dispose(this.disposables); } -} - -let SEARCH_INPUT_OPTIONS: IEditorOptions = -{ - fontSize: 13, - lineHeight: 22, - wordWrap: 'off', - overviewRulerLanes: 0, - glyphMargin: false, - lineNumbers: 'off', - folding: false, - selectOnLineNumbers: false, - hideCursorInOverviewRuler: true, - selectionHighlight: false, - scrollbar: { - horizontal: 'hidden', - vertical: 'hidden' - }, - ariaLabel: localize('searchExtensions', "Search Extensions in Marketplace"), - cursorWidth: 1, - lineDecorationsWidth: 0, - overviewRulerBorder: false, - scrollBeyondLastLine: false, - renderLineHighlight: 'none', - fixedOverflowWidgets: true, - acceptSuggestionOnEnter: 'smart', - minimap: { - enabled: false - }, - fontFamily: ' -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif' -}; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 02cd5b25c9f..a01efaa710b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -12,8 +12,8 @@ import { dispose } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { chain } from 'vs/base/common/event'; import { isPromiseCanceledError, create as createError } from 'vs/base/common/errors'; -import { PagedModel, IPagedModel, IPager } from 'vs/base/common/paging'; -import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, EnablementState, IExtensionRecommendation, IExtensionManagementServerService, ExtensionRecommendationSource, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; +import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, EnablementState, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -39,7 +39,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { distinct } from 'vs/base/common/arrays'; -import URI from 'vs/base/common/uri'; import { IExperimentService } from 'vs/workbench/parts/experiments/node/experimentService'; export class ExtensionsListView extends ViewletPanel { @@ -65,12 +64,15 @@ export class ExtensionsListView extends ViewletPanel { @ITelemetryService private telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, - @IExtensionManagementServerService protected extensionManagementServerService: IExtensionManagementServerService, @IExperimentService private experimentService: IExperimentService ) { 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 +109,10 @@ export class ExtensionsListView extends ViewletPanel { } async show(query: string): Promise<IPagedModel<IExtension>> { - 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 +145,12 @@ export class ExtensionsListView extends ViewletPanel { return this.list.length; } + protected showEmptyModel(): TPromise<IPagedModel<IExtension>> { + const emptyModel = new PagedModel([]); + this.setModel(emptyModel); + return TPromise.as(emptyModel); + } + private async query(value: string): Promise<IPagedModel<IExtension>> { const query = Query.parse(value); @@ -152,6 +163,9 @@ export class ExtensionsListView extends ViewletPanel { case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break; case 'name': options = assign(options, { sortBy: SortBy.Title }); break; } + if (!value || !value.trim()) { + options.sortBy = SortBy.InstallCount; + } if (/@builtin/i.test(value)) { const showThemesOnly = /@builtin:themes/i.test(value); @@ -205,9 +219,9 @@ export class ExtensionsListView extends ViewletPanel { return new PagedModel(this.sortExtensions(result, options)); } - if (!value || ExtensionsListView.isInstalledExtensionsQuery(value)) { + if (/@installed/i.test(value)) { // Show installed extensions - value = value ? value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : ''; + value = value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); let result = await this.extensionsWorkbenchService.queryLocal(); @@ -217,12 +231,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)); } @@ -299,8 +317,7 @@ export class ExtensionsListView extends ViewletPanel { if (text !== query.value) { options = assign(options, { text: text.substr(0, 350), source: 'file-extension-tags' }); - const pager = await this.extensionsWorkbenchService.queryGallery(options); - return new PagedModel(pager); + return this.extensionsWorkbenchService.queryGallery(options).then(pager => new PagedModel(pager)); } } @@ -310,8 +327,7 @@ export class ExtensionsListView extends ViewletPanel { options.source = 'viewlet'; } - const pager = await this.extensionsWorkbenchService.queryGallery(options); - return new PagedModel(pager); + return this.extensionsWorkbenchService.queryGallery(options).then(pager => new PagedModel(pager)); } private sortExtensions(extensions: IExtension[], options: IQueryOptions): IExtension[] { @@ -467,39 +483,7 @@ export class ExtensionsListView extends ViewletPanel { } private isRecommendationInstalled(recommendation: IExtensionRecommendation, installed: IExtension[]): boolean { - const extension = installed.filter(i => areSameExtensions({ id: i.id }, { id: recommendation.extensionId }))[0]; - if (extension && extension.locals) { - const servers: IExtensionManagementServer[] = []; - for (const local of extension.locals) { - const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); - if (servers.indexOf(server) === -1) { - servers.push(server); - } - } - for (const server of servers) { - if (extension.recommendationSources && extension.recommendationSources.length) { - if (extension.recommendationSources.some(recommendationSource => this.getExtensionManagementServerForRecommendationSource(recommendationSource) === server)) { - return true; - } - } - } - } - return false; - } - - private getExtensionManagementServerForRecommendationSource(source: ExtensionRecommendationSource): IExtensionManagementServer { - if (source instanceof URI) { - return this.extensionManagementServerService.getExtensionManagementServer(source); - } - if (source === this.contextService.getWorkspace()) { - return this.extensionManagementServerService.getDefaultExtensionManagementServer(); - } - for (const workspaceFolder of this.contextService.getWorkspace().folders) { - if (source === workspaceFolder) { - return this.extensionManagementServerService.getExtensionManagementServer(workspaceFolder.uri); - } - } - return this.extensionManagementServerService.getDefaultExtensionManagementServer(); + return installed.some(i => areSameExtensions({ id: i.id }, { id: recommendation.extensionId })); } private getWorkspaceRecommendationsModel(query: Query, options: IQueryOptions): TPromise<IPagedModel<IExtension>> { @@ -546,7 +530,7 @@ export class ExtensionsListView extends ViewletPanel { private setModel(model: IPagedModel<IExtension>) { if (this.list) { - this.list.model = model; + this.list.model = new DelayedPagedModel(model); this.list.scrollTop = 0; const count = this.count(); @@ -606,25 +590,13 @@ export class ExtensionsListView extends ViewletPanel { } static isInstalledExtensionsQuery(query: string): boolean { - return /@installed/i.test(query); + return /@installed|@outdated|@enabled|@disabled/i.test(query); } static isGroupByServersExtensionsQuery(query: string): boolean { return !!Query.parse(query).groupBy; } - static isOutdatedExtensionsQuery(query: string): boolean { - return /@outdated/i.test(query); - } - - static isDisabledExtensionsQuery(query: string): boolean { - return /@disabled/i.test(query); - } - - static isEnabledExtensionsQuery(query: string): boolean { - return /@enabled/i.test(query); - } - static isRecommendedExtensionsQuery(query: string): boolean { return /^@recommended$/i.test(query.trim()); } @@ -650,31 +622,12 @@ export class ExtensionsListView extends ViewletPanel { } } -export class InstalledExtensionsView extends ExtensionsListView { - - public static isInstalledExtensionsQuery(query: string): boolean { - return ExtensionsListView.isInstalledExtensionsQuery(query) - || ExtensionsListView.isOutdatedExtensionsQuery(query) - || ExtensionsListView.isDisabledExtensionsQuery(query) - || ExtensionsListView.isEnabledExtensionsQuery(query); - } - - async show(query: string): Promise<IPagedModel<IExtension>> { - if (InstalledExtensionsView.isInstalledExtensionsQuery(query)) { - return super.show(query); - } - let searchInstalledQuery = '@installed'; - searchInstalledQuery = query ? searchInstalledQuery + ' ' + query : searchInstalledQuery; - return super.show(searchInstalledQuery); - } -} - export class GroupByServerExtensionsView extends ExtensionsListView { async show(query: string): Promise<IPagedModel<IExtension>> { query = query.replace(/@group:server/g, '').trim(); query = query ? query : '@installed'; - if (!InstalledExtensionsView.isInstalledExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) { + if (!ExtensionsListView.isInstalledExtensionsQuery(query) && !ExtensionsListView.isBuiltInExtensionsQuery(query)) { query = query += ' @installed'; } return super.show(query.trim()); @@ -682,42 +635,41 @@ export class GroupByServerExtensionsView extends ExtensionsListView { } export class EnabledExtensionsView extends ExtensionsListView { + private readonly enabledExtensionsQuery = '@enabled'; async show(query: string): Promise<IPagedModel<IExtension>> { - 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<IPagedModel<IExtension>> { - 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<IPagedModel<IExtension>> { - 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<IPagedModel<IExtension>> { - 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<IPagedModel<IExtension>> { - 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); @@ -728,13 +680,21 @@ export class DefaultRecommendedExtensionsView extends ExtensionsListView { } async show(query: string): Promise<IPagedModel<IExtension>> { - return super.show('@recommended:all'); + if (query && query.trim() !== this.recommendedExtensionsQuery) { + return this.showEmptyModel(); + } + const model = await super.show(this.recommendedExtensionsQuery); + if (!this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User)) { + // This is part of popular extensions view. Collapse if no installed extensions. + this.setExpanded(model.length > 0); + } + return model; } } export class RecommendedExtensionsView extends ExtensionsListView { - + private readonly recommendedExtensionsQuery = '@recommended'; renderBody(container: HTMLElement): void { super.renderBody(container); @@ -745,12 +705,12 @@ export class RecommendedExtensionsView extends ExtensionsListView { } async show(query: string): Promise<IPagedModel<IExtension>> { - 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 { @@ -784,14 +744,15 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { this.disposables.push(...[this.installAllAction, configureWorkspaceFolderAction, actionbar]); } - async show(): Promise<IPagedModel<IExtension>> { - let model = await super.show('@recommended:workspace'); + async show(query: string): Promise<IPagedModel<IExtension>> { + 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 98391d7599e..e8e11338689 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,32 +202,6 @@ 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, @@ -246,4 +225,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 a492f570789..f35a103ec82 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -18,7 +18,7 @@ import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest, - InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionTipsService, ExtensionRecommendationSource, IExtensionRecommendation, IExtensionManagementServerService + InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -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<T> { (extension: Extension): T; @@ -48,14 +48,14 @@ class Extension implements IExtension { public get local(): ILocalExtension { return this.locals[0]; } public enablementState: EnablementState = EnablementState.Enabled; - public recommendationSources: ExtensionRecommendationSource[]; constructor( private galleryService: IExtensionGalleryService, private stateProvider: IExtensionStateProvider<ExtensionState>, public locals: ILocalExtension[], public gallery: IGalleryExtension, - private telemetryService: ITelemetryService + private telemetryService: ITelemetryService, + private logService: ILogService ) { } get type(): LocalExtensionType { @@ -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; } @@ -211,8 +211,8 @@ class Extension implements IExtension { if (this.gallery.assets.manifest) { return this.galleryService.getManifest(this.gallery); } - this.telemetryService.publicLog('extensions:NotFoundManifest', this.telemetryData); - return TPromise.wrapError<IExtensionManifest>(new Error('not available')); + this.logService.error(nls.localize('Manifest is not found', "Manifest is not found"), this.id); + return TPromise.as(undefined); } return TPromise.as(this.local.manifest); @@ -393,7 +393,6 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, @ILogService private logService: ILogService, @IProgressService2 private progressService: IProgressService2, @IExtensionService private runtimeExtensionService: IExtensionService, - @IExtensionTipsService private extensionTipsService: IExtensionTipsService, @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService ) { this.stateProvider = ext => this.getExtensionState(ext); @@ -415,6 +414,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)); @@ -429,8 +433,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } queryLocal(): TPromise<IExtension[]> { - return TPromise.join([this.extensionService.getInstalled(), this.extensionTipsService.getAllRecommendations()]) - .then(([installed, allRecommendations]) => this.getDistinctInstalledExtensions(installed) + return this.extensionService.getInstalled() + .then(installed => this.getDistinctInstalledExtensions(installed) .then(distinctInstalled => { const installedById = index(this.installed, e => e.local.identifier.id); const groupById = groupBy(installed, i => getGalleryExtensionIdFromLocal(i)); @@ -438,13 +442,9 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, const locals = groupById[getGalleryExtensionIdFromLocal(local)]; locals.splice(locals.indexOf(local), 1); locals.splice(0, 0, local); - const extension = installedById[local.identifier.id] || new Extension(this.galleryService, this.stateProvider, locals, null, this.telemetryService); + const extension = installedById[local.identifier.id] || new Extension(this.galleryService, this.stateProvider, locals, null, this.telemetryService, this.logService); extension.locals = locals; extension.enablementState = this.extensionEnablementService.getEnablementState(local); - const recommendation = allRecommendations.filter(r => areSameExtensions({ id: r.extensionId }, { id: extension.id }))[0]; - if (recommendation) { - extension.recommendationSources = recommendation.sources || []; - } return extension; }); @@ -454,12 +454,12 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } queryGallery(options: IQueryOptions = {}): TPromise<IPager<IExtension>> { - return TPromise.join([this.extensionTipsService.getAllRecommendations(), this.extensionService.getExtensionsReport()]) - .then(([allRecommendations, report]) => { + return this.extensionService.getExtensionsReport() + .then(report => { const maliciousSet = getMaliciousExtensionsSet(report); return this.galleryService.query(options) - .then(result => mapPager(result, gallery => this.fromGallery(gallery, maliciousSet, allRecommendations))) + .then(result => mapPager(result, gallery => this.fromGallery(gallery, maliciousSet))) .then(null, err => { if (/No extension gallery service configured/.test(err.message)) { return TPromise.as(singlePagePager([])); @@ -475,12 +475,12 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return TPromise.wrap<IExtensionDependencies>(null); } - return TPromise.join([this.extensionTipsService.getAllRecommendations(), this.extensionService.getExtensionsReport()]) - .then(([allRecommendations, report]) => { + return this.extensionService.getExtensionsReport() + .then(report => { const maliciousSet = getMaliciousExtensionsSet(report); return this.galleryService.loadAllDependencies((<Extension>extension).dependencies.map(id => ({ id }))) - .then(galleryExtensions => galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension, maliciousSet, allRecommendations))) + .then(galleryExtensions => galleryExtensions.map(galleryExtension => this.fromGallery(galleryExtension, maliciousSet))) .then(extensions => [...this.local, ...extensions]) .then(extensions => { const map = new Map<string, IExtension>(); @@ -548,7 +548,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return false; } - private fromGallery(gallery: IGalleryExtension, maliciousExtensionSet: Set<string>, allRecommendations: IExtensionRecommendation[]): Extension { + private fromGallery(gallery: IGalleryExtension, maliciousExtensionSet: Set<string>): Extension { let result = this.getInstalledExtensionMatchingGallery(gallery); if (result) { @@ -561,18 +561,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this.syncLocalWithGalleryExtension(result, gallery); } } else { - result = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService); + result = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService); } if (maliciousExtensionSet.has(result.id)) { result.isMalicious = true; } - const recommendation = allRecommendations.filter(r => areSameExtensions({ id: r.extensionId }, { id: result.id }))[0]; - if (recommendation) { - result.recommendationSources = recommendation.sources || []; - } - return result; } @@ -610,8 +605,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) @@ -673,11 +673,11 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, location: ProgressLocation.Extensions, title: nls.localize('installingVSIXExtension', 'Installing extension from VSIX...'), source: `${extension}` - }, () => this.extensionService.install(extension).then(() => null)); + }, () => this.extensionService.install(URI.file(extension)).then(() => null)); } if (!(extension instanceof Extension)) { - return undefined; + return TPromise.as(undefined); } if (extension.isMalicious) { @@ -742,38 +742,45 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } private promptAndSetEnablement(extensions: IExtension[], enablementState: EnablementState): TPromise<any> { - 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<void> { - 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<void> { + 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<void>(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<any> { - const allExtensions = [...extensions, ...dependencies]; + private checkAndSetEnablement(extensions: IExtension[], otherExtensions: IExtension[], enablementState: EnablementState): TPromise<any> { + const allExtensions = [...extensions, ...otherExtensions]; const enable = enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled; if (!enable) { for (const extension of extensions) { @@ -786,7 +793,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) { @@ -799,11 +806,15 @@ 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 i.type === LocalExtensionType.User + && (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; } @@ -891,7 +902,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, let extension = this.installed.filter(e => areSameExtensions(e, gallery.identifier))[0]; if (!extension) { - extension = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService); + extension = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService); } extension.gallery = gallery; @@ -904,14 +915,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private onDidInstallExtension(event: DidInstallExtensionEvent): void { const { local, zipPath, error, gallery } = event; const installingExtension = gallery ? this.installing.filter(e => areSameExtensions(e, gallery.identifier))[0] : null; - const extension: Extension = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, [local], null, this.telemetryService) : null; + const extension: Extension = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, [local], null, this.telemetryService, this.logService) : null; if (extension) { this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing; if (!error) { const installed = this.installed.filter(e => e.id === extension.id)[0]; if (installed) { const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); - const existingLocal = installed.locals.filter(l => this.extensionManagementServerService.getExtensionManagementServer(l.location).location.toString() === server.location.toString())[0]; + const existingLocal = installed.locals.filter(l => this.extensionManagementServerService.getExtensionManagementServer(l.location).authority === server.authority)[0]; if (existingLocal) { const locals = [...installed.locals]; locals.splice(installed.locals.indexOf(existingLocal), 1, local); 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..f107018c53d 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.suggestions('@sort:in').some(x => x === '@sort:installs '); + Query.suggestions('@sort:installs').every(x => x !== '@sort:rating '); + + Query.suggestions('@category:blah').some(x => x === '@category:"extension packs" '); + Query.suggestions('@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 1c34aeeffa7..5b724133ed5 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 @@ -36,7 +36,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { URLService } from 'vs/platform/url/common/urlService'; import URI from 'vs/base/common/uri'; import { SingleServerExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; -import { Schemas } from 'vs/base/common/network'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; suite('ExtensionsActions Test', () => { @@ -60,7 +60,7 @@ suite('ExtensionsActions Test', () => { instantiationService.stub(IWindowService, TestWindowService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); - instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, onDidChangeConfiguration: () => { }, getConfiguration: () => ({}) }); + instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService); @@ -71,7 +71,7 @@ suite('ExtensionsActions Test', () => { instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event); instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event); - instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(SingleServerExtensionManagementServerService, <IExtensionManagementServer>{ location: URI.from({ scheme: Schemas.file }), extensionManagementService: instantiationService.get(IExtensionManagementService) })); + instantiationService.stub(IExtensionManagementServerService, instantiationService.createInstance(SingleServerExtensionManagementServerService, <IExtensionManagementServer>{ authority: 'vscode-local', extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local' })); instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts index 89cbd28c3d6..4fd3b6cc212 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -316,7 +316,7 @@ suite('ExtensionsTipsService Test', () => { }); test('ExtensionTipsService: No Prompt for valid workspace recommendations during extension development', () => { - instantiationService.stub(IEnvironmentService, { extensionDevelopmentPath: true }); + instantiationService.stub(IEnvironmentService, { extensionDevelopmentLocationURI: true }); return testNoPromptOrRecommendationsForValidRecommendations(mockTestData.validRecommendedExtensions); }); 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 704d31d312a..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,7 +11,7 @@ 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, @@ -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); @@ -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) @@ -1276,4 +1282,4 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts index 2224b8c6846..47cdf3df9ec 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedback.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedback.ts @@ -216,6 +216,7 @@ export class FeedbackDropdown extends Dropdown { dom.EventHelper.stop(event); const actionId = 'workbench.action.openIssueReporter'; this.commandService.executeCommand(actionId).done(null, errors.onUnexpectedError); + this.hide(); /* __GDPR__ "workbenchActionExecuted" : { @@ -227,8 +228,12 @@ export class FeedbackDropdown extends Dropdown { }) .appendTo($contactUsContainer); - $('div').append($('a').attr('target', '_blank').attr('href', this.requestFeatureLink).text(nls.localize("request a missing feature", "Request a missing feature")).attr('tabindex', '0')) - .appendTo($contactUsContainer); + if (!!this.requestFeatureLink) { + $('div').append($('a').attr('target', '_blank').attr('href', this.requestFeatureLink).text(nls.localize("request a missing feature", "Request a missing feature")).attr('tabindex', '0')) + .on('click', event => { this.hide(); }) + .appendTo($contactUsContainer); + } + this.remainingCharacterCount = $('span.char-counter').text(this.getCharCountText(0)); diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts index 38b00d07f94..738ee925110 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts @@ -16,8 +16,7 @@ import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { clearNode, EventHelper, addClass, removeClass } from 'vs/base/browser/dom'; -import { $ } from 'vs/base/browser/builder'; +import { clearNode, EventHelper, addClass, removeClass, addDisposableListener } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; @@ -94,7 +93,7 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { super.updateStyles(); if (this.dropdown) { - $(this.dropdown.label).style('background-color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); + this.dropdown.label.style.backgroundColor = (this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); } } @@ -102,21 +101,21 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { this.container = element; // Prevent showing dropdown on anything but left click - $(this.container).on('mousedown', (e: MouseEvent) => { + this.toDispose.push(addDisposableListener(this.container, 'mousedown', (e: MouseEvent) => { if (e.button !== 0) { EventHelper.stop(e, true); } - }, this.toDispose, true); + }, true)); // Offer context menu to hide status bar entry - $(this.container).on('contextmenu', e => { + this.toDispose.push(addDisposableListener(this.container, 'contextmenu', e => { EventHelper.stop(e, true); this.contextMenuService.showContextMenu({ getAnchor: () => this.container, getActions: () => TPromise.as([this.hideAction]) }); - }, this.toDispose); + })); return this.update(); } diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css b/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css index 3a390da8f70..3c69f00d995 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css +++ b/src/vs/workbench/parts/feedback/electron-browser/media/feedback.css @@ -48,7 +48,6 @@ padding-left: 3px; } -/* TODO @C5 review link color */ .monaco-shell .feedback-form .content .channels a { padding: 2px 0; } diff --git a/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts index 8b16509b50c..f99b69abbcc 100644 --- a/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -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'; @@ -27,7 +26,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; -import { ResourceQueue } from 'vs/base/common/async'; +import { ResourceQueue, timeout } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; export class FileEditorTracker extends Disposable implements IWorkbenchContribution { @@ -83,7 +82,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut if (configuration.workbench && configuration.workbench.editor && typeof configuration.workbench.editor.closeOnFileDelete === 'boolean') { this.closeOnFileDelete = configuration.workbench.editor.closeOnFileDelete; } else { - this.closeOnFileDelete = true; // default + this.closeOnFileDelete = false; // default } } @@ -149,7 +148,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)) { return; } @@ -157,7 +156,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); } if (!matches) { @@ -170,14 +169,14 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut // file is really gone and not just a faulty file event. // This only applies to external file events, so we need to check for the isExternal // flag. - let checkExists: TPromise<boolean>; + let checkExists: Thenable<boolean>; if (isExternal) { - checkExists = TPromise.timeout(100).then(() => this.fileService.existsFile(resource)); + checkExists = timeout(100).then(() => this.fileService.existsFile(resource)); } else { - checkExists = TPromise.as(false); + checkExists = Promise.resolve(false); } - checkExists.done(exists => { + checkExists.then(exists => { if (!exists && !editor.isDisposed()) { editor.dispose(); } else if (this.environmentService.verbose) { @@ -219,31 +218,33 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut private handleMovedFileInOpenedEditors(oldResource: URI, newResource: URI): void { this.editorGroupService.groups.forEach(group => { - group.editors.forEach(input => { - if (input instanceof FileEditorInput) { - const resource = input.getResource(); + group.editors.forEach(editor => { + if (editor instanceof FileEditorInput) { + const resource = editor.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)) { 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 - this.editorService.openEditor({ - resource: reopenFileResource, - options: { - preserveFocus: true, - pinned: group.isPinned(input), - index: group.getIndexOfEditor(input), - inactive: !group.isActive(input), - viewState: this.getViewStateFor(oldResource, group) - } - }, group); + this.editorService.replaceEditors([{ + editor: { resource }, + replacement: { + resource: reopenFileResource, + options: { + preserveFocus: true, + pinned: group.isPinned(editor), + index: group.getIndexOfEditor(editor), + inactive: !group.isActive(editor), + viewState: this.getViewStateFor(oldResource, group) + } + }, + }], group); } } }); diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index 8ec1880198a..7954c9cb400 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 { ILabelService } from 'vs/platform/label/common/label'; /** * 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, + @ILabelService private labelService: ILabelService ) { 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.labelService.getUriLabel(resources.dirname(this.resource))); } @memoize private get mediumDescription(): string { - return labels.getPathLabel(resources.dirname(this.resource), this.environmentService, this.contextService); + return this.labelService.getUriLabel(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.labelService.getUriLabel(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.labelService.getUriLabel(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.labelService.getUriLabel(this.resource); } 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..6b66d23af4f 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, startsWithIgnoreCase, startsWith, equalsIgnoreCase } 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); })); // 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,9 @@ 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)) - ) { + // For performance reasons try to do the comparison as fast as possible + if (resource && this.resource.scheme === resource.scheme && equalsIgnoreCase(this.resource.authority, resource.authority) && + (resources.hasToIgnoreCase(resource) ? startsWithIgnoreCase(resource.path, this.resource.path) : startsWith(resource.path, this.resource.path))) { 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 dbca8fb34ef..a6d11d8e009 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -25,6 +25,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import URI from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { FileDialogContext } from 'vs/platform/workbench/common/contextkeys'; // Contribute Global Actions const category = nls.localize('filesCategory', "File"); @@ -114,8 +115,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.IsFileSystemResource, copyRelativePathCommand); +appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.IsFileSystemResource); function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr, alt?: { id: string, title: string }): void { @@ -186,7 +187,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', order: 10, command: openToSideCommand, - when: ResourceContextKey.IsFile + when: ResourceContextKey.IsFileSystemResource }); const revealInOsCommand = { @@ -197,7 +198,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', order: 20, command: revealInOsCommand, - when: ResourceContextKey.Scheme.isEqualTo(Schemas.file) + when: ResourceContextKey.IsFileSystemResource }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -205,7 +206,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { order: 40, command: copyPathCommand, alt: copyRelativePathCommand, - when: ResourceContextKey.IsFile + when: ResourceContextKey.IsFileSystemResource }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -216,7 +217,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.IsFileSystemResource, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo('')) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -227,7 +228,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.IsFileSystemResource, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo('')) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -256,7 +257,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.IsFileSystemResource, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo(''), WorkbenchListDoubleSelection.toNegated()) }); const compareResourceCommand = { @@ -267,7 +268,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 20, command: compareResourceCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResourceOrUntitled, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) }); const selectForCompareCommand = { @@ -278,7 +279,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 30, command: selectForCompareCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResourceOrUntitled, WorkbenchListDoubleSelection.toNegated()) }); const compareSelectedCommand = { @@ -289,7 +290,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 30, command: compareSelectedCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection) + when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResourceOrUntitled, WorkbenchListDoubleSelection) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -372,21 +373,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 +416,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { order: 30, command: copyPathCommand, alt: copyRelativePathCommand, - when: ResourceContextKey.IsFile + when: ResourceContextKey.IsFileSystemResource }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { @@ -425,7 +426,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { id: ADD_ROOT_FOLDER_COMMAND_ID, title: ADD_ROOT_FOLDER_LABEL }, - when: ExplorerRootContext + when: ContextKeyExpr.and(ExplorerRootContext, FileDialogContext.isEqualTo('local')) }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { @@ -505,7 +506,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { id: SAVE_FILE_AS_COMMAND_ID, title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As...") }, - order: 2 + order: 2, + when: FileDialogContext.isEqualTo('local') }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -544,3 +546,14 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { }, 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 +}); diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 04b6594db4e..2b34716fc38 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -14,7 +14,6 @@ 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'; @@ -300,7 +299,7 @@ class RenameFileAction extends BaseRenameAction { public runAction(newName: string): TPromise<any> { 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); } @@ -496,7 +495,7 @@ class CreateFileAction extends BaseCreateAction { public runAction(fileName: string): TPromise<any> { 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)); @@ -523,7 +522,7 @@ class CreateFolderAction extends BaseCreateAction { public runAction(fileName: string): TPromise<any> { 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)); }); } @@ -784,9 +783,9 @@ export class AddFilesAction extends BaseFileAction { this._updateEnablement(); } - public run(resources: URI[]): TPromise<any> { + public run(resourcesToAdd: URI[]): TPromise<any> { const addPromise = TPromise.as(null).then(() => { - if (resources && resources.length > 0) { + if (resourcesToAdd && resourcesToAdd.length > 0) { // Find parent to add to let targetElement: ExplorerItem; @@ -811,8 +810,8 @@ export class AddFilesAction extends BaseFileAction { }); let overwritePromise: TPromise<IConfirmationResult> = 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?"), @@ -831,10 +830,10 @@ export class AddFilesAction extends BaseFileAction { // Run add in sequence const addPromisesFactory: ITask<TPromise<void>>[] = []; - 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 @@ -845,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)); @@ -1020,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; @@ -1049,7 +1048,7 @@ export function incrementFileName(name: string, isFolder: boolean): string { } // 1.file.txt=>2.file.txt - let prefixFileRegex = RegExp('(\\d+)(' + separators + '.*)(\\..*)$'); + let prefixFileRegex = RegExp('^(\\d+)(' + separators + '.*)(\\..*)$'); if (!isFolder && name.match(prefixFileRegex)) { return name.replace(prefixFileRegex, (match, g1?, g2?, g3?) => { let number = parseInt(g1); @@ -1060,7 +1059,7 @@ export function incrementFileName(name: string, isFolder: boolean): string { } // 1.txt=>2.txt - let prefixFileNoNameRegex = RegExp('(\\d+)(\\..*)$'); + let prefixFileNoNameRegex = RegExp('^(\\d+)(\\..*)$'); if (!isFolder && name.match(prefixFileNoNameRegex)) { return name.replace(prefixFileNoNameRegex, (match, g1?, g2?) => { let number = parseInt(g1); @@ -1545,14 +1544,14 @@ 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 = () => { this.registrationDisposal = dispose(this.registrationDisposal); }; - return always(this.editorService.openEditor({ leftResource: URI.from({ scheme: CompareWithClipboardAction.SCHEME, path: resource.fsPath }), rightResource: resource, label: editorLabel }), cleanUp); + return always(this.editorService.openEditor({ leftResource: resource.with({ scheme: CompareWithClipboardAction.SCHEME }), rightResource: resource, label: editorLabel }), cleanUp); } return TPromise.as(true); diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index 5be4ed15f03..2e4edb6e547 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; import * as paths from 'vs/base/common/paths'; import { TPromise } from 'vs/base/common/winjs.base'; -import * as labels from 'vs/base/common/labels'; import URI from 'vs/base/common/uri'; import { toResource, IEditorCommandsContext } from 'vs/workbench/common/editor'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; @@ -31,7 +30,7 @@ import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; 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 { isWindows, isMacintosh } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { sequence } from 'vs/base/common/async'; import { getResourceForCommand, getMultiSelectedResources } from 'vs/workbench/parts/files/browser/files'; @@ -42,8 +41,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { isEqual, basenameOrAuthority } from 'vs/base/common/resources'; -import { ltrim } from 'vs/base/common/strings'; +import { ILabelService } from 'vs/platform/label/common/label'; // Commands @@ -79,7 +77,6 @@ export const ResourceSelectedForCompareContext = new RawContextKey<boolean>('res export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace"); -//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.map(p => typeof p === 'string' ? URI.file(p) : p), { forceNewWindow }); @@ -289,7 +286,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const name = paths.basename(uri.fsPath); const editorLabel = nls.localize('modifiedLabel', "{0} (on disk) ↔ {1}", name, name); - return editorService.openEditor({ leftResource: URI.from({ scheme: COMPARE_WITH_SAVED_SCHEMA, path: uri.fsPath }), rightResource: uri, label: editorLabel }).then(() => void 0); + return editorService.openEditor({ leftResource: uri.with({ scheme: COMPARE_WITH_SAVED_SCHEMA }), rightResource: uri, label: editorLabel }).then(() => void 0); } return TPromise.as(true); @@ -390,28 +387,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -function resourcesToClipboard(resources: URI[], clipboardService: IClipboardService, notificationService: INotificationService, contextService?: IWorkspaceContextService): void { +function resourcesToClipboard(resources: URI[], relative: boolean, clipboardService: IClipboardService, notificationService: INotificationService, labelService: ILabelService): void { if (resources.length) { const lineDelimiter = isWindows ? '\r\n' : '\n'; - const text = resources.map(resource => { - if (contextService) { - const workspaceFolder = contextService.getWorkspaceFolder(resource); - if (workspaceFolder) { - if (isEqual(workspaceFolder.uri, resource, !isLinux)) { - return basenameOrAuthority(workspaceFolder.uri); - } - - return paths.normalize(ltrim(resource.path.substr(workspaceFolder.uri.path.length), paths.sep), true); - } - } - - if (resource.scheme === Schemas.file) { - return paths.normalize(labels.normalizeDriveLetter(resource.fsPath), true); - } - - return resource.toString(); - }).join(lineDelimiter); + const text = resources.map(resource => labelService.getUriLabel(resource, relative, true)) + .join(lineDelimiter); clipboardService.writeText(text); } else { notificationService.info(nls.localize('openFileToCopy', "Open a file first to copy its path")); @@ -428,7 +409,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: COPY_PATH_COMMAND_ID, handler: (accessor, resource: URI | object) => { const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService)); - resourcesToClipboard(resources, accessor.get(IClipboardService), accessor.get(INotificationService)); + resourcesToClipboard(resources, false, accessor.get(IClipboardService), accessor.get(INotificationService), accessor.get(ILabelService)); } }); @@ -442,7 +423,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: COPY_RELATIVE_PATH_COMMAND_ID, handler: (accessor, resource: URI | object) => { const resources = getMultiSelectedResources(resource, accessor.get(IListService), accessor.get(IEditorService)); - resourcesToClipboard(resources, accessor.get(IClipboardService), accessor.get(INotificationService), accessor.get(IWorkspaceContextService)); + resourcesToClipboard(resources, true, accessor.get(IClipboardService), accessor.get(INotificationService), accessor.get(ILabelService)); } }); @@ -455,7 +436,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const editorService = accessor.get(IEditorService); const activeInput = editorService.activeEditor; const resources = activeInput && activeInput.getResource() ? [activeInput.getResource()] : []; - resourcesToClipboard(resources, accessor.get(IClipboardService), accessor.get(INotificationService)); + resourcesToClipboard(resources, false, accessor.get(IClipboardService), accessor.get(INotificationService), accessor.get(ILabelService)); } }); 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 c684a9a1e73..13694d24cd7 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -12,7 +12,7 @@ import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/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 { ILabelService } from 'vs/platform/label/common/label'; +import { Schemas } from 'vs/base/common/network'; +import { nativeSep } from 'vs/base/common/paths'; // Viewlet Action export class OpenExplorerViewletAction extends ToggleViewletAction { @@ -50,6 +53,20 @@ export class OpenExplorerViewletAction extends ToggleViewletAction { } } +class FileUriLabelContribution implements IWorkbenchContribution { + + constructor(@ILabelService labelService: ILabelService) { + labelService.registerFormatter(Schemas.file, { + uri: { + label: '${path}', + separator: nativeSep, + tildify: !platform.isWindows, + normalizeDriveLetter: platform.isWindows + } + }); + } +} + // Register Viewlet Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( ExplorerViewlet, @@ -156,6 +173,10 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi // Register Dirty Files Tracker Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DirtyFilesTracker, LifecyclePhase.Starting); +// Register uri display for file uris +Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FileUriLabelContribution, LifecyclePhase.Starting); + + // Configuration const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration); @@ -167,7 +188,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 +213,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 +228,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 +237,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 +269,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.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. 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#` 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 +293,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 +312,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 +333,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 +353,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,16 +382,16 @@ 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 }, } diff --git a/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts b/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts index f7ab5bd7f54..3eca44e9bfa 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/emptyView.ts @@ -11,7 +11,6 @@ import * as DOM from 'vs/base/browser/dom'; import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; import { Button } from 'vs/base/browser/ui/button/button'; -import { $, Builder } from 'vs/base/browser/builder'; import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -33,8 +32,8 @@ export class EmptyView extends ViewletPanel { public static readonly NAME = nls.localize('noWorkspace', "No Folder Opened"); private button: Button; - private messageDiv: Builder; - private titleDiv: Builder; + private messageElement: HTMLElement; + private titleElement: HTMLElement; constructor( options: IViewletViewOptions, @@ -50,17 +49,26 @@ export class EmptyView extends ViewletPanel { } public renderHeader(container: HTMLElement): void { - this.titleDiv = $('span').text(name).appendTo($('div.title').appendTo(container)); + const titleContainer = document.createElement('div'); + DOM.addClass(titleContainer, 'title'); + container.appendChild(titleContainer); + + this.titleElement = document.createElement('span'); + this.titleElement.textContent = name; + titleContainer.appendChild(this.titleElement); } protected renderBody(container: HTMLElement): void { DOM.addClass(container, 'explorer-empty-view'); - this.messageDiv = $('p').appendTo($('div.section').appendTo(container)); + const messageContainer = document.createElement('div'); + DOM.addClass(messageContainer, 'section'); + container.appendChild(messageContainer); - const section = $('div.section').appendTo(container); + this.messageElement = document.createElement('p'); + messageContainer.appendChild(this.messageElement); - this.button = new Button(section.getHTMLElement()); + this.button = new Button(messageContainer); attachButtonStyler(this.button, this.themeService); this.disposables.push(this.button.onDidClick(() => { @@ -99,17 +107,17 @@ export class EmptyView extends ViewletPanel { private setLabels(): void { if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - this.messageDiv.text(nls.localize('noWorkspaceHelp', "You have not yet added a folder to the workspace.")); + this.messageElement.textContent = nls.localize('noWorkspaceHelp', "You have not yet added a folder to the workspace."); if (this.button) { this.button.label = nls.localize('addFolder', "Add Folder"); } - this.titleDiv.text(this.contextService.getWorkspace().name); + this.titleElement.textContent = EmptyView.NAME; } else { - this.messageDiv.text(nls.localize('noFolderHelp', "You have not yet opened a folder.")); + this.messageElement.textContent = nls.localize('noFolderHelp', "You have not yet opened a folder."); if (this.button) { this.button.label = nls.localize('openFolder', "Open Folder"); } - this.titleDiv.text(this.title); + this.titleElement.textContent = this.title; } } 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..175ad41be2e 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -43,6 +43,7 @@ import { Schemas } from 'vs/base/common/network'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { ILabelService } from 'vs/platform/label/common/label'; export interface IExplorerViewOptions extends IViewletViewOptions { viewletState: FileViewletState; @@ -94,7 +95,8 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService configurationService: IConfigurationService, - @IDecorationsService decorationService: IDecorationsService + @IDecorationsService decorationService: IDecorationsService, + @ILabelService private labelService: ILabelService ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); @@ -118,6 +120,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView this.decorationProvider = new ExplorerDecorationsProvider(this.model, contextService); decorationService.registerDecorationsProvider(this.decorationProvider); this.disposables.push(this.decorationProvider); + this.disposables.push(this.resourceContext); } private getFileEventsExcludes(root?: URI): glob.IExpression { @@ -146,7 +149,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } public get name(): string { - return this.contextService.getWorkspace().name; + return this.labelService.getWorkspaceLabel(this.contextService.getWorkspace()); } public get title(): string { @@ -196,6 +199,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 { @@ -797,14 +801,21 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private resolveRoots(targetsToResolve: { root: ExplorerItem, resource: URI, options: { resolveTo: any[] } }[], targetsToExpand: URI[]): TPromise<any> { // Display roots only when multi folder workspace - const input = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? this.model.roots[0] : this.model; - const errorFileStat = (resource: URI, root: ExplorerItem) => ExplorerItem.create({ - resource: resource, - name: paths.basename(resource.fsPath), - mtime: 0, - etag: undefined, - isDirectory: true - }, root, undefined, true); + let input = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? this.model.roots[0] : this.model; + + const errorRoot = (resource: URI, root: ExplorerItem) => { + if (input === this.model.roots[0]) { + input = this.model; + } + + return ExplorerItem.create({ + resource: resource, + name: paths.basename(resource.fsPath), + mtime: 0, + etag: undefined, + isDirectory: true + }, root, undefined, true); + }; const setInputAndExpand = (input: ExplorerItem | Model, statsToExpand: ExplorerItem[]) => { // Make sure to expand all folders that where expanded in the previous session @@ -825,7 +836,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView return ExplorerItem.create(result.stat, targetsToResolve[index].root, targetsToResolve[index].options.resolveTo); } - return errorFileStat(targetsToResolve[index].resource, targetsToResolve[index].root); + return errorRoot(targetsToResolve[index].resource, targetsToResolve[index].root); }); // Subsequent refresh: Merge stat into our local model and refresh tree modelStats.forEach((modelStat, index) => { @@ -848,7 +859,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView let delayer = new Delayer(100); let delayerPromise: TPromise; return TPromise.join(targetsToResolve.map((target, index) => this.fileService.resolveFile(target.resource, target.options) - .then(result => result.isDirectory ? ExplorerItem.create(result, target.root, target.options.resolveTo) : errorFileStat(target.resource, target.root), () => errorFileStat(target.resource, target.root)) + .then(result => result.isDirectory ? ExplorerItem.create(result, target.root, target.options.resolveTo) : errorRoot(target.resource, target.root), () => errorRoot(target.resource, target.root)) .then(modelStat => { // Subsequent refresh: Merge stat into our local model and refresh tree if (index < this.model.roots.length) { 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..e0204febe1f 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; } } @@ -502,7 +502,7 @@ export class FileController extends WorkbenchTreeController implements IDisposab sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey; } - this.openEditor(stat, { preserveFocus, sideBySide, pinned: isDoubleClick }); + this.openEditor(stat, { preserveFocus, sideBySide, pinned: isDoubleClick || (event && event.middleButton) }); } } @@ -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/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 52205ce5b1d..5aa65e9e21a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -10,7 +10,7 @@ import { IAction, ActionRunner } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorGroupsService, IEditorGroup, GroupChangeKind } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup, GroupChangeKind, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorInput } from 'vs/workbench/common/editor'; @@ -234,6 +234,7 @@ export class OpenEditorsView extends ViewletPanel { ExplorerFocusedContext.bindTo(this.list.contextKeyService); this.resourceContext = this.instantiationService.createInstance(ResourceContextKey); + this.disposables.push(this.resourceContext); this.groupFocusedContext = OpenEditorsGroupContext.bindTo(this.contextKeyService); this.dirtyEditorFocusedContext = DirtyEditorContext.bindTo(this.contextKeyService); @@ -326,7 +327,7 @@ export class OpenEditorsView extends ViewletPanel { private get elements(): (IEditorGroup | OpenEditor)[] { const result: (IEditorGroup | OpenEditor)[] = []; - this.editorGroupService.groups.forEach(g => { + this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(g => { if (this.showGroups) { result.push(g); } @@ -342,7 +343,7 @@ export class OpenEditorsView extends ViewletPanel { return index; } - for (let g of this.editorGroupService.groups) { + for (let g of this.editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)) { if (g.id === group.id) { return index + (!!editor ? 1 : 0); } else { @@ -389,7 +390,7 @@ export class OpenEditorsView extends ViewletPanel { } private focusActiveEditor(): void { - if (this.editorGroupService.activeGroup && this.editorGroupService.activeGroup.activeEditor /* could be empty */) { + if (this.list.length && this.editorGroupService.activeGroup) { const index = this.getIndex(this.editorGroupService.activeGroup, this.editorGroupService.activeGroup.activeEditor); this.list.setFocus([index]); this.list.setSelection([index]); 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/files/test/electron-browser/fileActions.test.ts b/src/vs/workbench/parts/files/test/electron-browser/fileActions.test.ts index 48bd1f7cc23..60120a22edd 100644 --- a/src/vs/workbench/parts/files/test/electron-browser/fileActions.test.ts +++ b/src/vs/workbench/parts/files/test/electron-browser/fileActions.test.ts @@ -106,12 +106,24 @@ suite('Files - Increment file name', () => { assert.strictEqual(result, '2.test.js'); }); + test('Increment file name with prefix version - check if starting from digit', function () { + const name = 'F1.test.js'; + const result = incrementFileName(name, false); + assert.strictEqual(result, 'F1.test.1.js'); + }); + test('Increment file name with just version in name', function () { const name = '1.js'; const result = incrementFileName(name, false); assert.strictEqual(result, '2.js'); }); + test('Increment file name with just version in name - check if starting from digit', function () { + const name = 'F1.js'; + const result = incrementFileName(name, false); + assert.strictEqual(result, 'F1.1.js'); + }); + test('Increment file name with just version in name, too big number', function () { const name = '9007199254740992.js'; const result = incrementFileName(name, false); 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..052de75c677 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts @@ -12,7 +12,7 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; import { ConfigureLocaleAction } from 'vs/workbench/parts/localizations/electron-browser/localizationsActions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; +import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { IExtensionManagementService, DidInstallExtensionEvent, LocalExtensionType, IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -98,24 +98,6 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } } - private migrateToMarketplaceLanguagePack(language: string): void { - this.isLanguageInstalled(language) - .then(installed => { - if (!installed) { - this.getLanguagePackExtension(language) - .then(extension => { - if (extension) { - this.notificationService.prompt(Severity.Warning, localize('install language pack', "In the near future, VS Code will only support language packs in the form of Marketplace extensions. Please install the '{0}' extension in order to continue to use the currently configured language. ", extension.displayName || extension.displayName), - [ - { label: localize('install', "Install"), run: () => this.installExtension(extension) }, - { label: localize('more information', "More Information..."), run: () => window.open('https://go.microsoft.com/fwlink/?linkid=872941') } - ]); - } - }); - } - }); - } - private checkAndInstall(): void { const language = platform.language; const locale = platform.locale; @@ -124,11 +106,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo if (!this.galleryService.isEnabled()) { return; } - if (language !== 'en' && language.indexOf('en-') !== -1) { - this.migrateToMarketplaceLanguagePack(language); - return; - } - if (locale === 'en' || locale.indexOf('en-') !== -1) { + if (language === 'en' || language.indexOf('en-') === 0) { return; } if (language === locale || languagePackSuggestionIgnoreList.indexOf(language) > -1) { @@ -141,19 +119,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,24 +210,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<IGalleryExtension> { - return this.localizationService.getLanguageIds(LanguageType.Core) - .then(coreLanguages => { - if (coreLanguages.some(c => c.toLowerCase() === language)) { - const extensionIdPrefix = language === 'zh-cn' ? 'zh-hans' : language === 'zh-tw' ? 'zh-hant' : language; - const extensionId = `MS-CEINTL.vscode-language-pack-${extensionIdPrefix}`; - return this.galleryService.query({ names: [extensionId], pageSize: 1 }) - .then(result => result.total === 1 ? result.firstPage[0] : null); - } - return null; - }); - } - private isLanguageInstalled(language: string): TPromise<boolean> { return this.extensionManagementService.getInstalled(LocalExtensionType.User) .then(installed => installed.some(i => i.manifest && i.manifest.contributes && i.manifest.contributes.localizations && i.manifest.contributes.localizations.length && i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language))); diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts b/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts index 7d22c8fc3a6..1e70b6279fa 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 { ILabelService } from 'vs/platform/label/common/label'; 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, + @ILabelService private labelService: ILabelService ) { 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.labelService.getUriLabel(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..9d1ae739a25 100644 --- a/src/vs/workbench/parts/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/parts/logs/electron-browser/logsActions.ts @@ -9,13 +9,14 @@ 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'; +import { ILabelService } from 'vs/platform/label/common/label'; export class OpenLogsFolderAction extends Action { @@ -40,22 +41,24 @@ 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 + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ILabelService private labelService: ILabelService ) { super(id, label); } run(): TPromise<void> { - const entries: IPickOpenEntry[] = [ - { id: Constants.rendererLogChannelId, label: this.contextService.getWorkspace().name ? nls.localize('rendererProcess', "Window ({0})", this.contextService.getWorkspace().name) : nls.localize('emptyWindow', "Window") }, + const workspaceName = this.labelService.getWorkspaceLabel(this.contextService.getWorkspace()); + const entries: IQuickPickItem[] = [ + { id: Constants.rendererLogChannelId, label: workspaceName ? nls.localize('rendererProcess', "Window ({0})", workspaceName) : 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,25 +74,27 @@ 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, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ILabelService private labelService: ILabelService ) { super(id, label); } run(): TPromise<void> { - const entries: IPickOpenEntry[] = [ - { 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") }, + const workspaceName = this.labelService.getWorkspaceLabel(this.contextService.getWorkspace()); + const entries: IQuickPickItem[] = [ + { id: URI.file(paths.join(this.environmentService.logsPath, `renderer${this.windowService.getCurrentWindowId()}.log`)).fsPath, label: workspaceName ? nls.localize('rendererProcess', "Window ({0})", workspaceName) : 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") }, { id: URI.file(paths.join(this.environmentService.logsPath, `main.log`)).fsPath, label: nls.localize('mainProcess', "Main") }, { 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 +110,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 +128,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); } @@ -142,4 +147,4 @@ export class SetLogLevelAction extends Action { } return void 0; } -} \ No newline at end of file +} 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/markersModel.ts b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts index 85e1d53e2ff..e40b6fe56c7 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts @@ -15,10 +15,10 @@ import { groupBy, isFalsyOrEmpty, flatten } from 'vs/base/common/arrays'; import { values } from 'vs/base/common/map'; import * as glob from 'vs/base/common/glob'; import * as strings from 'vs/base/common/strings'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { CodeAction } from 'vs/editor/common/modes'; import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; +import { IModelService } from 'vs/editor/common/services/modelService'; function compareUris(a: URI, b: URI) { if (a.toString() < b.toString()) { @@ -48,7 +48,7 @@ export class ResourceMarkers extends NodeWithId { constructor( readonly uri: URI, - private textModelService: ITextModelService + private modelService: IModelService ) { super(uri.toString()); } @@ -72,6 +72,10 @@ export class ResourceMarkers extends NodeWithId { } public async hasFixes(marker: Marker): Promise<boolean> { + if (!this.modelService.getModel(this.uri)) { + // Return early, If the model is not yet created + return false; + } if (!this._allFixesPromise) { this._allFixesPromise = this._getFixes(); } @@ -88,11 +92,9 @@ export class ResourceMarkers extends NodeWithId { } private async _getFixes(range?: Range): Promise<CodeAction[]> { - const modelReference = await this.textModelService.createModelReference(this.uri); - if (modelReference) { - const model = modelReference.object.textEditorModel; + const model = this.modelService.getModel(this.uri); + if (model) { const codeActions = await getCodeActions(model, range ? range : model.getFullModelRange(), { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }); - modelReference.dispose(); return codeActions; } return []; @@ -121,7 +123,8 @@ export class Marker extends NodeWithId { constructor( id: string, - readonly raw: IMarker + readonly raw: IMarker, + readonly resourceMarkers: ResourceMarkers ) { super(id); } @@ -224,7 +227,7 @@ export class MarkersModel { constructor( markers: IMarker[] = [], - @ITextModelService private textModelService: ITextModelService + @IModelService private modelService: IModelService ) { this._markersByResource = new Map<string, ResourceMarkers>(); this._filterOptions = new FilterOptions(); @@ -310,11 +313,11 @@ export class MarkersModel { private createResource(uri: URI, rawMarkers: IMarker[]): ResourceMarkers { const markers: Marker[] = []; - const resource = new ResourceMarkers(uri, this.textModelService); + const resource = new ResourceMarkers(uri, this.modelService); this.updateResource(resource); rawMarkers.forEach((rawMarker, index) => { - const marker = new Marker(uri.toString() + index, rawMarker); + const marker = new Marker(uri.toString() + index, rawMarker, resource); if (rawMarker.relatedInformation) { const groupedByResource = groupBy(rawMarker.relatedInformation, MarkersModel._compareMarkersByUri); groupedByResource.sort((a, b) => compareUris(a[0].resource, b[0].resource)); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index dde2adc6b77..76d693bd41f 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'; @@ -32,6 +32,8 @@ import { SimpleFileResourceDragAndDrop } from 'vs/workbench/browser/dnd'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Scope } from 'vs/workbench/common/memento'; import { localize } from 'vs/nls'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; export class MarkersPanel extends Panel { @@ -68,6 +70,7 @@ export class MarkersPanel extends Panel { this.delayedRefresh = new Delayer<void>(500); this.autoExpanded = new Set<string>(); this.panelSettings = this.getMemento(storageService, Scope.WORKSPACE); + this.setCurrentActiveEditor(); } public create(parent: HTMLElement): TPromise<void> { @@ -196,7 +199,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 +272,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 { @@ -302,6 +309,7 @@ export class MarkersPanel extends Panel { dom.clearNode(this.messageBoxContainer); const markersModel = this.markersWorkbenchService.markersModel; if (markersModel.hasFilteredResources()) { + this.messageBoxContainer.style.display = 'none'; const { total, filtered } = markersModel.stats(); if (filtered === total) { this.ariaLabelElement.setAttribute('aria-label', localize('No problems filtered', "Showing {0} problems", total)); @@ -310,6 +318,7 @@ export class MarkersPanel extends Panel { } this.messageBoxContainer.removeAttribute('tabIndex'); } else { + this.messageBoxContainer.style.display = 'block'; this.messageBoxContainer.setAttribute('tabIndex', '0'); if (markersModel.hasResources()) { if (markersModel.filterOptions.filter) { @@ -329,7 +338,13 @@ export class MarkersPanel extends Panel { const link = dom.append(container, dom.$('a.messageAction')); link.textContent = localize('disableFilesExclude', "Disable Files Exclude Filter."); link.setAttribute('tabIndex', '0'); - dom.addDisposableListener(link, dom.EventType.CLICK, () => this.filterInputActionItem.useFilesExclude = false); + dom.addStandardDisposableListener(link, dom.EventType.CLICK, () => this.filterInputActionItem.useFilesExclude = false); + dom.addStandardDisposableListener(link, dom.EventType.KEY_DOWN, (e: IKeyboardEvent) => { + if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { + this.filterInputActionItem.useFilesExclude = false; + e.stopPropagation(); + } + }); this.ariaLabelElement.setAttribute('aria-label', Messages.MARKERS_PANEL_NO_PROBLEMS_FILE_EXCLUSIONS_FILTER); } @@ -339,7 +354,13 @@ export class MarkersPanel extends Panel { const link = dom.append(container, dom.$('a.messageAction')); link.textContent = localize('clearFilter', "Clear Filter."); link.setAttribute('tabIndex', '0'); - dom.addDisposableListener(link, dom.EventType.CLICK, () => this.filterInputActionItem.clear()); + dom.addStandardDisposableListener(link, dom.EventType.CLICK, () => this.filterInputActionItem.clear()); + dom.addStandardDisposableListener(link, dom.EventType.KEY_DOWN, (e: IKeyboardEvent) => { + if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { + this.filterInputActionItem.clear(); + e.stopPropagation(); + } + }); this.ariaLabelElement.setAttribute('aria-label', Messages.MARKERS_PANEL_NO_PROBLEMS_FILTERS); } @@ -433,6 +454,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..d231e7b99fc 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'; @@ -23,13 +23,20 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachStylerCallback, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; 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 { IDisposable, dispose } from 'vs/base/common/lifecycle'; +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 } 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'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { isEqual } from 'vs/base/common/resources'; export class ToggleMarkersPanelAction extends TogglePanelAction { @@ -86,14 +93,12 @@ export interface IMarkersFilterActionItemOptions { export class MarkersFilterActionItem extends BaseActionItem { - private _toDispose: IDisposable[] = []; - private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>()); readonly onDidChange: Event<void> = this._onDidChange.event; private delayedFilterUpdate: Delayer<void>; private container: HTMLElement; - private element: HTMLElement; + private filterContainer: HTMLElement; private filterInputBox: HistoryInputBox; private controlsContainer: HTMLInputElement; private filterBadge: HTMLInputElement; @@ -116,7 +121,7 @@ export class MarkersFilterActionItem extends BaseActionItem { render(container: HTMLElement): void { this.container = container; DOM.addClass(this.container, 'markers-panel-action-filter-container'); - DOM.append(this.container, this.element); + DOM.append(this.container, this.filterContainer); this.adjustInputBox(); } @@ -150,9 +155,9 @@ export class MarkersFilterActionItem extends BaseActionItem { } private create(itemOptions: IMarkersFilterActionItemOptions): void { - this.element = DOM.$('.markers-panel-action-filter'); - this.createInput(this.element, itemOptions); - this.createControls(this.element, itemOptions); + this.filterContainer = DOM.$('.markers-panel-action-filter'); + this.createInput(this.filterContainer, itemOptions); + this.createControls(this.filterContainer, itemOptions); } private createInput(container: HTMLElement, itemOptions: IMarkersFilterActionItemOptions): void { @@ -264,9 +269,89 @@ export class MarkersFilterActionItem extends BaseActionItem { */ this.telemetryService.publicLog('problems.filter', data); } +} + +export class QuickFixAction extends Action { + + public static readonly ID: string = 'workbench.actions.problems.quickfix'; + + private updated: boolean = false; + private disposables: IDisposable[] = []; + + constructor( + readonly marker: Marker, + @IBulkEditService private bulkEditService: IBulkEditService, + @ICommandService private commandService: ICommandService, + @IEditorService private editorService: IEditorService, + @IModelService modelService: IModelService + ) { + super(QuickFixAction.ID, Messages.MARKERS_PANEL_ACTION_TOOLTIP_QUICKFIX, 'markers-panel-action-quickfix', false); + if (modelService.getModel(this.marker.resourceMarkers.uri)) { + this.update(); + } else { + modelService.onModelAdded(model => { + if (isEqual(model.uri, marker.resource)) { + this.update(); + } + }, this, this.disposables); + } - private _register<T extends IDisposable>(t: T): T { - this._toDispose.push(t); - return t; } + + private update(): void { + if (!this.updated) { + this.marker.resourceMarkers.hasFixes(this.marker).then(hasFixes => this.enabled = hasFixes); + this.updated = true; + } + } + + async getQuickFixActions(): Promise<IAction[]> { + const codeActions = await this.marker.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<void> { + 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); + } + + dispose(): void { + dispose(this.disposables); + super.dispose(); + } +} + +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.element); + this.contextMenuService.showContextMenu({ + getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), + getActions: () => TPromise.wrap((<QuickFixAction>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..e936b16e3f1 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts @@ -7,19 +7,16 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as mouse from 'vs/base/browser/mouseEvent'; import * as tree from 'vs/base/parts/tree/browser/tree'; -import { MarkersModel, Marker, ResourceMarkers } from 'vs/workbench/parts/markers/electron-browser/markersModel'; +import { MarkersModel, Marker } 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,13 @@ export class Controller extends WorkbenchTreeController { private async _getMenuActions(tree: WorkbenchTree, element: any): Promise<IAction[]> { const result: IAction[] = []; - if (element instanceof Marker) { - const quickFixActions = await this._getQuickFixActions(tree, element); + const quickFixAction = this.instantiationService.createInstance(QuickFixAction, element); + const quickFixActions = await quickFixAction.getQuickFixActions(); if (quickFixActions.length) { result.push(...quickFixActions); - } else { - result.push(new Action('problems.no.fixes', localize('no fixes available', "No fixes available"), void 0, false)); + result.push(new Separator()); } - result.push(new Separator()); } const menu = this.menuService.createMenu(MenuId.ProblemsPanelContext, tree.contextKeyService); @@ -104,34 +97,4 @@ export class Controller extends WorkbenchTreeController { result.pop(); // remove last separator return result; } - - private async _getQuickFixActions(tree: WorkbenchTree, element: Marker): Promise<IAction[]> { - 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<void> { - 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 4b8f13c73de..4e35e7e3ccb 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -19,9 +19,10 @@ 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 { ILabelService } from 'vs/platform/label/common/label'; +import { dirname } from 'vs/base/common/resources'; interface IResourceMarkersTemplateData { resourceLabel: ResourceLabel; @@ -31,7 +32,7 @@ interface IResourceMarkersTemplateData { interface IMarkerTemplateData { icon: HTMLElement; - lightbulb: HTMLElement; + actionBar: ActionBar; source: HighlightedLabel; description: HighlightedLabel; lnCol: HTMLElement; @@ -96,10 +97,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, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @ILabelService private labelService: ILabelService ) { } @@ -167,6 +168,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')); @@ -180,8 +184,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')); @@ -204,7 +209,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, this.contextService), resource: element.uri }, { matches: element.uriMatches }); + templateData.resourceLabel.setLabel({ name: element.name, description: this.labelService.getUriLabel(dirname(element.uri), true), resource: element.uri }, { matches: element.uriMatches }); } (<IResourceMarkersTemplateData>templateData).count.setCount(element.filteredCount); } @@ -213,25 +218,24 @@ 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 quickFixAction = this.instantiationService.createInstance(QuickFixAction, element); + 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, this.contextService); + templateData.resourceLabel.element.title = this.labelService.getUriLabel(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; @@ -258,6 +262,7 @@ export class Renderer implements IRenderer { } else if (templateId === Renderer.MARKER_TEMPLATE_ID) { (<IMarkerTemplateData>templateData).description.dispose(); (<IMarkerTemplateData>templateData).source.dispose(); + (<IMarkerTemplateData>templateData).actionBar.dispose(); } else if (templateId === Renderer.RELATED_INFO_TEMPLATE_ID) { (<IRelatedInformationTemplateData>templateData).description.dispose(); (<IRelatedInformationTemplateData>templateData).resourceLabel.dispose(); @@ -268,14 +273,13 @@ export class Renderer implements IRenderer { export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider { constructor( - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IEnvironmentService private environmentService: IEnvironmentService + @ILabelService private labelServie: ILabelService ) { } 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.labelServie.getUriLabel(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..635af71f10c 100644 --- a/src/vs/workbench/parts/markers/electron-browser/media/markers.css +++ b/src/vs/workbench/parts/markers/electron-browser/media/markers.css @@ -72,6 +72,7 @@ .markers-panel .markers-panel-container .message-box-container { line-height: 22px; padding-left: 20px; + height: 100%; } .markers-panel .markers-panel-container .message-box-container .messageAction { @@ -136,7 +137,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 +167,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/markers/test/electron-browser/markersModel.test.ts b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts index b2bceb193b3..39eae5fb72b 100644 --- a/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts +++ b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts @@ -140,19 +140,19 @@ suite('MarkersModel Test', () => { test('toString()', function () { let marker = aMarker('a/res1'); marker.code = '1234'; - assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker).toString()); + assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker, null).toString()); marker = aMarker('a/res2', MarkerSeverity.Warning); - assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker).toString()); + assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker, null).toString()); marker = aMarker('a/res2', MarkerSeverity.Info, 1, 2, 1, 8, 'Info', ''); - assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker).toString()); + assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker, null).toString()); marker = aMarker('a/res2', MarkerSeverity.Hint, 1, 2, 1, 8, 'Ignore message', 'Ignore'); - assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker).toString()); + assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path }, null, '\t'), instantiationService.createInstance(Marker, '', marker, null).toString()); marker = aMarker('a/res2', MarkerSeverity.Warning, 1, 2, 1, 8, 'Warning message', '', [{ startLineNumber: 2, startColumn: 5, endLineNumber: 2, endColumn: 10, message: 'some info', resource: URI.file('a/res3') }]); - const testObject = instantiationService.createInstance(Marker, '', marker); + const testObject = instantiationService.createInstance(Marker, '', marker, null); testObject.resourceRelatedInformation = marker.relatedInformation.map(r => new RelatedInformation('', r)); assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path, relatedInformation: marker.relatedInformation.map(r => ({ ...r, resource: r.resource.path })) }, null, '\t'), testObject.toString()); }); diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.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 c61097b03d3..4c9c93b5db4 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -13,7 +13,7 @@ import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { Action, IAction, RadioGroup } from 'vs/base/common/actions'; import { firstIndex } from 'vs/base/common/arrays'; -import { asDisposablePromise, setDisposableTimeout, createCancelablePromise } from 'vs/base/common/async'; +import { asDisposablePromise, setDisposableTimeout, createCancelablePromise, timeout } from 'vs/base/common/async'; import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { defaultGenerator } from 'vs/base/common/idGenerator'; @@ -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; } @@ -511,7 +496,7 @@ export class OutlinePanel extends ViewletPanel { let oldRatio = oldSize / oldLength; if (newRatio <= oldRatio * 0.5 || newRatio >= oldRatio * 1.5) { if (!await asDisposablePromise( - TPromise.timeout(2000).then(_ => true), + timeout(2000).then(_ => true), false, this._editorDisposables).promise ) { 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/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index 32fc13e7979..52c5d9c1f1f 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as paths from 'vs/base/common/paths'; -import * as strings from 'vs/base/common/strings'; import * as extfs from 'vs/base/node/extfs'; import * as fs from 'fs'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -17,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IOutputChannelIdentifier, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, MAX_OUTPUT_LENGTH, LOG_SCHEME, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT } from 'vs/workbench/parts/output/common/output'; +import { IOutputChannelIdentifier, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, LOG_SCHEME, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT } from 'vs/workbench/parts/output/common/output'; import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -37,7 +36,6 @@ import { RotatingLogger } from 'spdlog'; import { toLocalISOString } from 'vs/base/common/date'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { ILogService } from 'vs/platform/log/common/log'; -import { binarySearch } from 'vs/base/common/arrays'; import { Schemas } from 'vs/base/common/network'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -288,7 +286,7 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out } private loadFile(): TPromise<string> { - return this.fileService.resolveContent(this.file, { position: this.startOffset }) + return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) .then(content => this.appendedMessage ? content.value + this.appendedMessage : content.value); } @@ -374,7 +372,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann } loadModel(): TPromise<ITextModel> { - return this.fileService.resolveContent(this.file, { position: this.startOffset }) + return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) .then(content => { this.endOffset = this.startOffset + Buffer.from(content.value).byteLength; return this.createModel(content.value); @@ -387,7 +385,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann protected updateModel(): void { if (this.model) { - this.fileService.resolveContent(this.file, { position: this.endOffset }) + this.fileService.resolveContent(this.file, { position: this.endOffset, encoding: 'utf8' }) .then(content => { if (content.value) { this.endOffset = this.endOffset + Buffer.from(content.value).byteLength; @@ -584,13 +582,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo if (channelData && channelData.file) { return this.instantiationService.createInstance(FileOutputChannel, channelData, uri); } - try { - return this.instantiationService.createInstance(OutputChannelBackedByFile, { id, label: channelData ? channelData.label : '' }, this.outputDir, uri); - } catch (e) { - // Do not crash if spdlog rotating logger cannot be loaded (workaround for https://github.com/Microsoft/vscode/issues/47883) - this.logService.error(e); - return this.instantiationService.createInstance(BufferredOutputChannel, { id, label: channelData ? channelData.label : '' }); - } + return this.instantiationService.createInstance(OutputChannelBackedByFile, { id, label: channelData ? channelData.label : '' }, this.outputDir, uri); } private doShowChannel(channel: IOutputChannel, preserveFocus: boolean): Thenable<void> { @@ -660,144 +652,4 @@ export class LogContentProvider { } return channel; } -} -// Remove this channel when https://github.com/Microsoft/vscode/issues/47883 is fixed -class BufferredOutputChannel extends Disposable implements OutputChannel { - - readonly id: string; - readonly label: string; - readonly file: URI = null; - scrollLock: boolean = false; - - protected _onDidAppendedContent: Emitter<void> = new Emitter<void>(); - readonly onDidAppendedContent: Event<void> = this._onDidAppendedContent.event; - - private readonly _onDispose: Emitter<void> = new Emitter<void>(); - readonly onDispose: Event<void> = this._onDispose.event; - - private modelUpdater: RunOnceScheduler; - private model: ITextModel; - private readonly bufferredContent: BufferedContent; - private lastReadId: number = void 0; - - constructor( - protected readonly outputChannelIdentifier: IOutputChannelIdentifier, - @IModelService private modelService: IModelService, - @IModeService private modeService: IModeService - ) { - super(); - - this.id = outputChannelIdentifier.id; - this.label = outputChannelIdentifier.label; - - this.modelUpdater = new RunOnceScheduler(() => this.updateModel(), 300); - this._register(toDisposable(() => this.modelUpdater.cancel())); - - this.bufferredContent = new BufferedContent(); - this._register(toDisposable(() => this.bufferredContent.clear())); - } - - append(output: string) { - this.bufferredContent.append(output); - if (!this.modelUpdater.isScheduled()) { - this.modelUpdater.schedule(); - } - } - - clear(): void { - if (this.modelUpdater.isScheduled()) { - this.modelUpdater.cancel(); - } - if (this.model) { - this.model.setValue(''); - } - this.bufferredContent.clear(); - this.lastReadId = void 0; - } - - loadModel(): TPromise<ITextModel> { - const { value, id } = this.bufferredContent.getDelta(this.lastReadId); - if (this.model) { - this.model.setValue(value); - } else { - this.model = this.createModel(value); - } - this.lastReadId = id; - return TPromise.as(this.model); - } - - private createModel(content: string): ITextModel { - const model = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), URI.from({ scheme: OUTPUT_SCHEME, path: this.id })); - const disposables: IDisposable[] = []; - disposables.push(model.onWillDispose(() => { - this.model = null; - dispose(disposables); - })); - return model; - } - - private updateModel(): void { - if (this.model) { - const { value, id } = this.bufferredContent.getDelta(this.lastReadId); - this.lastReadId = id; - const lastLine = this.model.getLineCount(); - const lastLineMaxColumn = this.model.getLineMaxColumn(lastLine); - this.model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), value)]); - this._onDidAppendedContent.fire(); - } - } - - dispose(): void { - this._onDispose.fire(); - super.dispose(); - } -} - -class BufferedContent { - - private data: string[] = []; - private dataIds: number[] = []; - private idPool = 0; - private length = 0; - - public append(content: string): void { - this.data.push(content); - this.dataIds.push(++this.idPool); - this.length += content.length; - this.trim(); - } - - public clear(): void { - this.data.length = 0; - this.dataIds.length = 0; - this.length = 0; - } - - private trim(): void { - if (this.length < MAX_OUTPUT_LENGTH * 1.2) { - return; - } - - while (this.length > MAX_OUTPUT_LENGTH) { - this.dataIds.shift(); - const removed = this.data.shift(); - this.length -= removed.length; - } - } - - public getDelta(previousId?: number): { value: string, id: number } { - let idx = -1; - if (previousId !== void 0) { - idx = binarySearch(this.dataIds, previousId, (a, b) => a - b); - } - - const id = this.idPool; - if (idx >= 0) { - const value = strings.removeAnsiEscapeCodes(this.data.slice(idx + 1).join('')); - return { value, id }; - } else { - const value = strings.removeAnsiEscapeCodes(this.data.join('')); - return { value, id }; - } - } -} +} \ No newline at end of file diff --git a/src/vs/workbench/parts/performance/electron-browser/actions.ts b/src/vs/workbench/parts/performance/electron-browser/actions.ts new file mode 100644 index 00000000000..178a2e15d7e --- /dev/null +++ b/src/vs/workbench/parts/performance/electron-browser/actions.ts @@ -0,0 +1,309 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TPromise } from 'vs/base/common/winjs.base'; +import { Action } from 'vs/base/common/actions'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import * as nls from 'vs/nls'; +import product from 'vs/platform/node/product'; +import pkg from 'vs/platform/node/package'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; +import { ITimerService, IStartupMetrics } from 'vs/workbench/services/timer/electron-browser/timerService'; +import * as os from 'os'; +import { IExtensionService, ActivationTimes } from 'vs/workbench/services/extensions/common/extensions'; +import { getEntries } from 'vs/base/common/performance'; +import { timeout } from 'vs/base/common/async'; +import { StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { forEach } from 'vs/base/common/collections'; +import { mergeSort } from 'vs/base/common/arrays'; + + +class Info { + + static getTimerInfo(metrics: IStartupMetrics, nodeModuleLoadTime?: number): { [name: string]: Info } { + const table: { [name: string]: Info } = Object.create(null); + table['start => app.isReady'] = new Info(metrics.timers.ellapsedAppReady, '[main]', metrics.initialStartup); + table['nls:start => nls:end'] = new Info(metrics.timers.ellapsedNlsGeneration, '[main]', metrics.initialStartup); + table['app.isReady => window.loadUrl()'] = new Info(metrics.timers.ellapsedWindowLoad, '[main]', metrics.initialStartup); + + table['window.loadUrl() => begin to require(workbench.main.js)'] = new Info(metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKind[metrics.windowKind]); + table['require(workbench.main.js)'] = new Info(metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${nodeModuleLoadTime ? `, node_modules took ${nodeModuleLoadTime}ms` : ''}`); + + table['register extensions & spawn extension host'] = new Info(metrics.timers.ellapsedExtensions, '[renderer]'); + table['restore viewlet'] = new Info(metrics.timers.ellapsedViewletRestore, '[renderer]', metrics.viewletId); + table['restore panel'] = new Info(metrics.timers.ellapsedPanelRestore, '[renderer]', metrics.panelId); + table['restore editors'] = new Info(metrics.timers.ellapsedEditorRestore, '[renderer]', `${metrics.editorIds.length}: ${metrics.editorIds.join(', ')}`); + table['overall workbench load'] = new Info(metrics.timers.ellapsedWorkbench, '[renderer]'); + + table['workbench ready'] = new Info(metrics.ellapsed, '[main->renderer]'); + table['extensions registered'] = new Info(metrics.timers.ellapsedExtensionsReady, '[renderer]'); + + return table; + } + + private constructor(readonly duration: number, readonly process: string, readonly info: string | boolean = '') { } +} + +class LoaderStat { + + static getLoaderStats() { + + let seq = 1; + const amdLoad = new Map<string, LoaderStat>(); + const amdInvoke = new Map<string, LoaderStat>(); + const nodeRequire = new Map<string, LoaderStat>(); + const nodeEval = new Map<string, LoaderStat>(); + + function mark(map: Map<string, LoaderStat>, stat: LoaderEvent) { + if (map.has(stat.detail)) { + // console.warn('BAD events, DOUBLE start', stat); + // map.delete(stat.detail); + return; + } + map.set(stat.detail, new LoaderStat(-stat.timestamp, seq++)); + } + + function diff(map: Map<string, LoaderStat>, stat: LoaderEvent) { + let obj = map.get(stat.detail); + if (!obj) { + // console.warn('BAD events, end WITHOUT start', stat); + // map.delete(stat.detail); + return; + } + if (obj.duration >= 0) { + // console.warn('BAD events, DOUBLE end', stat); + // map.delete(stat.detail); + return; + } + obj.duration = (obj.duration + stat.timestamp); + } + + const stats = mergeSort(require.getStats().slice(0), (a, b) => a.timestamp - b.timestamp); + + for (const stat of stats) { + switch (stat.type) { + case LoaderEventType.BeginLoadingScript: + mark(amdLoad, stat); + break; + case LoaderEventType.EndLoadingScriptOK: + case LoaderEventType.EndLoadingScriptError: + diff(amdLoad, stat); + break; + + case LoaderEventType.BeginInvokeFactory: + mark(amdInvoke, stat); + break; + case LoaderEventType.EndInvokeFactory: + diff(amdInvoke, stat); + break; + + case LoaderEventType.NodeBeginNativeRequire: + mark(nodeRequire, stat); + break; + case LoaderEventType.NodeEndNativeRequire: + diff(nodeRequire, stat); + break; + + case LoaderEventType.NodeBeginEvaluatingScript: + mark(nodeEval, stat); + break; + case LoaderEventType.NodeEndEvaluatingScript: + diff(nodeEval, stat); + break; + } + } + + function toObject(map: Map<string, any>): { [name: string]: any } { + const result = Object.create(null); + map.forEach((value, index) => result[index] = value); + return result; + } + + let nodeRequireTotal = 0; + nodeRequire.forEach(value => nodeRequireTotal += value.duration); + + return { + amdLoad: toObject(amdLoad), + amdInvoke: toObject(amdInvoke), + nodeRequire: toObject(nodeRequire), + nodeEval: toObject(nodeEval), + nodeRequireTotal + }; + } + + constructor(public duration: number, public seq: number) { } +} + +export class ShowStartupPerformance extends Action { + + static readonly ID = 'workbench.action.appPerf'; + static readonly LABEL = nls.localize('appPerf', "Startup Performance"); + + constructor( + id: string, + label: string, + @IWindowService private windowService: IWindowService, + @ITimerService private timerService: ITimerService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IExtensionService private extensionService: IExtensionService + ) { + super(id, label); + } + + run(): TPromise<boolean> { + + // Show dev tools + this.windowService.openDevTools(); + + Promise.all([ + timeout(1000), // needed to print a table + this.timerService.startupMetrics + ]).then(([, metrics]) => { + + console.group('Startup Performance Measurement'); + console.log(`OS: ${metrics.platform}(${metrics.release})`); + console.log(`CPUs: ${metrics.cpus.model}(${metrics.cpus.count} x ${metrics.cpus.speed})`); + console.log(`Memory(System): ${(metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free)`); + console.log(`Memory(Process): ${(metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared)`); + console.log(`VM(likelyhood): ${metrics.isVMLikelyhood}% `); + + console.log(`Initial Startup: ${metrics.initialStartup} `); + console.log(`Has ${metrics.windowCount - 1} other windows`); + console.log(`Screen Reader Active: ${metrics.hasAccessibilitySupport} `); + console.log(`Empty Workspace: ${metrics.emptyWorkbench} `); + + + const loaderStats = this.environmentService.performance && LoaderStat.getLoaderStats(); + + console.table(Info.getTimerInfo(metrics, loaderStats && loaderStats.nodeRequireTotal)); + + if (loaderStats) { + for (const key in loaderStats) { + console.groupCollapsed(`Loader: ${key} `); + console.table(loaderStats[key]); + console.groupEnd(); + } + } + + console.groupEnd(); + + console.group('Extension Activation Stats'); + let extensionsActivationTimes: { [id: string]: ActivationTimes; } = {}; + let extensionsStatus = this.extensionService.getExtensionsStatus(); + for (let id in extensionsStatus) { + const status = extensionsStatus[id]; + if (status.activationTimes) { + extensionsActivationTimes[id] = status.activationTimes; + } + } + console.table(extensionsActivationTimes); + console.groupEnd(); + + console.group('Raw Startup Timers (CSV)'); + let value = `Name\tStart\n`; + let entries = getEntries('mark'); + for (const entry of entries) { + value += `${entry.name} \t${entry.startTime} \n`; + } + console.log(value); + console.groupEnd(); + }); + + return TPromise.as(true); + } +} + + +// NOTE: This is still used when running --prof-startup, which already opens a dialog, so the reporter is not used. +export class ReportPerformanceIssueAction extends Action { + + static readonly ID = 'workbench.action.reportPerformanceIssue'; + static readonly LABEL = nls.localize('reportPerformanceIssue', "Report Performance Issue"); + + constructor( + id: string, + label: string, + @IIntegrityService private integrityService: IIntegrityService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ITimerService private timerService: ITimerService + ) { + super(id, label); + } + + run(appendix?: string): TPromise<boolean> { + Promise.all([ + this.timerService.startupMetrics, + this.integrityService.isPure() + ]).then(([metrics, integrity]) => { + const issueUrl = this.generatePerformanceIssueUrl(metrics, product.reportIssueUrl, pkg.name, pkg.version, product.commit, product.date, integrity.isPure, appendix); + + window.open(issueUrl); + }); + + return TPromise.wrap(true); + } + + private generatePerformanceIssueUrl(metrics: IStartupMetrics, baseUrl: string, name: string, version: string, _commit: string, _date: string, isPure: boolean, appendix?: string): string { + + if (!appendix) { + appendix = `Additional Steps to Reproduce(if any): + + 1. +2.`; + } + + let nodeModuleLoadTime: number; + if (this.environmentService.performance) { + nodeModuleLoadTime = LoaderStat.getLoaderStats().nodeRequireTotal; + } + + + const osVersion = `${os.type()} ${os.arch()} ${os.release()} `; + const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; + const body = encodeURIComponent( + `- VSCode Version: <code>${name} ${version} ${isPure ? '' : ' **[Unsupported]**'} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})</code> + - OS Version: <code>${ osVersion} </code> + - CPUs: <code>${ metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed})</code> + - Memory(System): <code>${ (metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free) < /code> + - Memory(Process): <code>${ (metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared) < /code> + - Load(avg): <code>${ metrics.loadavg.map(l => Math.round(l)).join(', ')} </code> + - VM: <code>${ metrics.isVMLikelyhood}% </code> + - Initial Startup: <code>${ metrics.initialStartup ? 'yes' : 'no'} </code> + - Screen Reader: <code>${ metrics.hasAccessibilitySupport ? 'yes' : 'no'} </code> + - Empty Workspace: <code>${ metrics.emptyWorkbench ? 'yes' : 'no'} </code> + - Timings: + +${ this.generatePerformanceTable(metrics, nodeModuleLoadTime)} + +--- + + ${ appendix} ` + ); + + return `${baseUrl} ${queryStringPrefix} body = ${body} `; + } + + private generatePerformanceTable(metrics: IStartupMetrics, nodeModuleLoadTime?: number): string { + let tableHeader = `| Component | Task | Duration(ms) | Info | +| ---| ---| ---| ---| `; + + let table = ''; + forEach(Info.getTimerInfo(metrics, nodeModuleLoadTime), e => { + table += `| ${e.value.process}| ${e.key}| ${e.value.duration}| ${e.value.info}|\n`; + }); + + return `${tableHeader} \n${table} `; + } +} + +Registry + .as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(ShowStartupPerformance, ShowStartupPerformance.ID, ShowStartupPerformance.LABEL), 'Developer: Startup Performance', nls.localize('developer', "Developer")); diff --git a/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts index 1b8c90733cd..09fab3522c3 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts @@ -12,13 +12,15 @@ import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/ import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ReportPerformanceIssueAction } from 'vs/workbench/electron-browser/actions'; + import { TPromise } from 'vs/base/common/winjs.base'; import { join, dirname } from 'path'; import { localize } from 'vs/nls'; import { readdir, del, readFile } from 'vs/base/node/pfs'; import { basename } from 'vs/base/common/paths'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { timeout } from 'vs/base/common/async'; +import { ReportPerformanceIssueAction } from 'vs/workbench/parts/performance/electron-browser/actions'; class StartupProfiler implements IWorkbenchContribution { @@ -52,7 +54,7 @@ class StartupProfiler implements IWorkbenchContribution { const removeArgs: string[] = ['--prof-startup']; const markerFile = readFile(profileFilenamePrefix).then(value => removeArgs.push(...value.toString().split('|'))) .then(() => del(profileFilenamePrefix)) - .then(() => TPromise.timeout(1000)); + .then(() => timeout(1000)); markerFile.then(() => { return readdir(dir).then(files => files.filter(value => value.indexOf(prefix) === 0)); diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts index 047c038d75b..d4a974c0c38 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts @@ -5,23 +5,20 @@ 'use strict'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ILifecycleService, LifecyclePhase, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ITimerService } from 'vs/workbench/services/timer/common/timerService'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ILifecycleService, LifecyclePhase, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { Extensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import * as files from 'vs/workbench/parts/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { ILogService } from 'vs/platform/log/common/log'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IUpdateService } from 'vs/platform/update/common/update'; +import { ITimerService, didUseCachedData } from 'vs/workbench/services/timer/electron-browser/timerService'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; class StartupTimings implements IWorkbenchContribution { @@ -34,7 +31,6 @@ class StartupTimings implements IWorkbenchContribution { @IPanelService private readonly _panelService: IPanelService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @IExtensionService private readonly _extensionService: IExtensionService, @IUpdateService private readonly _updateService: IUpdateService, ) { @@ -43,10 +39,6 @@ class StartupTimings implements IWorkbenchContribution { } private async _reportVariedStartupTimes(): Promise<void> { - await Promise.all([ - this._extensionService.whenInstalledExtensionsRegistered(), - this._lifecycleService.when(LifecyclePhase.Eventually) - ]); /* __GDPR__ "startupTimeVaried" : { "${include}": [ @@ -54,7 +46,7 @@ class StartupTimings implements IWorkbenchContribution { ] } */ - this._telemetryService.publicLog('startupTimeVaried', this._timerService.startupMetrics); + this._telemetryService.publicLog('startupTimeVaried', await this._timerService.startupMetrics); } private async _reportStandardStartupTimes(): Promise<void> { @@ -85,7 +77,7 @@ class StartupTimings implements IWorkbenchContribution { this._logService.info('no standard startup: panel is active'); return; } - if (!this._didUseCachedData()) { + if (!didUseCachedData()) { this._logService.info('no standard startup: not using cached data'); return; } @@ -93,11 +85,6 @@ class StartupTimings implements IWorkbenchContribution { this._logService.info('no standard startup: not running latest version'); return; } - // wait only know so that can check the restored state as soon as possible - await TPromise.join([ - this._extensionService.whenInstalledExtensionsRegistered(), - this._lifecycleService.when(LifecyclePhase.Eventually) - ]); /* __GDPR__ "startupTime" : { @@ -106,27 +93,11 @@ class StartupTimings implements IWorkbenchContribution { ] } */ - this._telemetryService.publicLog('startupTime', this._timerService.startupMetrics); - this._logService.info('standard startup', this._timerService.startupMetrics); - } - - private _didUseCachedData(): boolean { - // We surely don't use cached data when we don't tell the loader to do so - if (!Boolean((<any>global).require.getConfig().nodeCachedDataDir)) { - return false; - } - // whenever cached data is produced or rejected a onNodeCachedData-callback is invoked. That callback - // stores data in the `MonacoEnvironment.onNodeCachedData` global. See: - // https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/electron-browser/bootstrap/index.js#L219 - if (!isFalsyOrEmpty(MonacoEnvironment.onNodeCachedData)) { - return false; - } - return true; + const metrics = await this._timerService.startupMetrics; + this._telemetryService.publicLog('startupTime', metrics); + this._logService.info('standard startup', metrics); } } -declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }]; -declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] }; - const registry = Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench); registry.registerWorkbenchContribution(StartupTimings, LifecyclePhase.Running); diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts index 2822fbb1890..676b7c3e4f3 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts @@ -5,15 +5,14 @@ 'use strict'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ITimerService } from 'vs/workbench/services/timer/common/timerService'; +import { ITimerService, didUseCachedData } from 'vs/workbench/services/timer/electron-browser/timerService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { nfcall } from 'vs/base/common/async'; -import { appendFile } from 'fs'; +import { nfcall, timeout } from 'vs/base/common/async'; +import { appendFile, } from 'fs'; import product from 'vs/platform/node/product'; class StartupTimingsAppender implements IWorkbenchContribution { @@ -22,21 +21,20 @@ class StartupTimingsAppender implements IWorkbenchContribution { @ITimerService timerService: ITimerService, @IWindowsService windowsService: IWindowsService, @ILifecycleService lifecycleService: ILifecycleService, - @IExtensionService extensionService: IExtensionService, @IEnvironmentService environmentService: IEnvironmentService, ) { let appendTo = environmentService.args['prof-append-timers']; if (!appendTo) { + // nothing to do return; } Promise.all([ - lifecycleService.when(LifecyclePhase.Eventually), - extensionService.whenInstalledExtensionsRegistered() - ]).then(() => { - const { startupMetrics } = timerService; - return nfcall(appendFile, appendTo, `${product.nameShort}\t${product.commit || '0000000'}\t${Date.now()}\t${startupMetrics.ellapsed}\n`); + timerService.startupMetrics, + this._waitWhenNoCachedData(), + ]).then(([startupMetrics]) => { + return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameLong}\t${product.commit || '0000000'}\n`); }).then(() => { windowsService.quit(); }).catch(err => { @@ -44,7 +42,14 @@ class StartupTimingsAppender implements IWorkbenchContribution { windowsService.quit(); }); } + + private _waitWhenNoCachedData(): Promise<void> { + // wait 15s for cached data to be produced + return !didUseCachedData() + ? timeout(15000) + : Promise.resolve(); + } } const registry = Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench); -registry.registerWorkbenchContribution(StartupTimingsAppender, LifecyclePhase.Running); +registry.registerWorkbenchContribution(StartupTimingsAppender, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index c071be384ec..0f25d0a34a6 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -439,7 +439,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor private getAriaLabel(keybindingsEntries: IKeybindingItemEntry[]): string { if (this.sortByPrecedence.checked) { - return localize('show sorted keybindings', "Showing {0} Keybindings in precendence order", keybindingsEntries.length); + return localize('show sorted keybindings', "Showing {0} Keybindings in precedence order", keybindingsEntries.length); } else { return localize('show keybindings', "Showing {0} Keybindings in alphabetical order", keybindingsEntries.length); } 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 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#e8e8e8" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg> \ 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 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg> \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/configure-inverse.svg b/src/vs/workbench/parts/preferences/browser/media/configure-inverse.svg index 61baaea2b8b..bbfbd366eb9 100644 --- a/src/vs/workbench/parts/preferences/browser/media/configure-inverse.svg +++ b/src/vs/workbench/parts/preferences/browser/media/configure-inverse.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#C5C5C5"><path d="M12.714 9.603c-.07.207-.15.407-.246.601l1.017 2.139c-.335.424-.718.807-1.142 1.143l-2.14-1.018c-.193.097-.394.176-.601.247l-.795 2.235c-.265.03-.534.05-.807.05-.272 0-.541-.02-.806-.05l-.795-2.235c-.207-.071-.408-.15-.602-.247l-2.14 1.017c-.424-.336-.807-.719-1.143-1.143l1.017-2.139c-.094-.193-.175-.393-.245-.6l-2.236-.796c-.03-.265-.05-.534-.05-.807s.02-.542.05-.807l2.236-.795c.07-.207.15-.407.246-.601l-1.016-2.139c.336-.423.719-.807 1.143-1.142l2.14 1.017c.193-.096.394-.176.602-.247l.793-2.236c.265-.03.534-.05.806-.05.273 0 .542.02.808.05l.795 2.236c.207.07.407.15.601.246l2.14-1.017c.424.335.807.719 1.142 1.142l-1.017 2.139c.096.194.176.394.246.601l2.236.795c.029.266.049.535.049.808s-.02.542-.05.807l-2.236.796zm-4.714-4.603c-1.657 0-3 1.343-3 3s1.343 3 3 3 3-1.343 3-3-1.343-3-3-3z"/><circle cx="8" cy="8" r="1.5"/></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>configure \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/configure.svg b/src/vs/workbench/parts/preferences/browser/media/configure.svg index 3dec2ba50fd..c97bb48bdcc 100644 --- a/src/vs/workbench/parts/preferences/browser/media/configure.svg +++ b/src/vs/workbench/parts/preferences/browser/media/configure.svg @@ -1 +1 @@ - \ No newline at end of file +configure \ 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 bdffa1c3f9e..67c8790f01a 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,12 +26,13 @@ .settings-editor > .settings-header > .settings-preview-header .settings-preview-label { opacity: .7; + 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; @@ -44,28 +43,49 @@ position: relative; } -.settings-editor > .settings-header .search-container > .settings-search-input { - vertical-align: middle; +.vs .settings-editor > .settings-header > .search-container > .suggest-input-container { + border: 1px solid #ddd; } -.settings-editor > .settings-header .search-container > .settings-search-input > .monaco-inputbox { - height: 30px; - width: 100%; -} - -.settings-editor > .settings-header .search-container > .settings-search-input > .monaco-inputbox .input { - font-size: 14px; - padding-left: 10px; +.settings-editor > .settings-header > .search-container > .settings-count-widget { + margin: 6px 0px; + padding: 0px 8px; + border-radius: 2px; + position: absolute; + right: 10px; + top: 0; } .settings-editor > .settings-header > .settings-header-controls { - height: 27px; + height: 32px; display: flex; - border-bottom: solid #3c3c3c 1px; + 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 { @@ -75,7 +95,7 @@ .settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right { margin-left: auto; - padding-top: 3px; + padding-top: 4px; display: flex; } @@ -85,33 +105,7 @@ height: 22px; background-position: center; background-repeat: no-repeat; -} - -.vs .settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more { - background-image: url('configure.svg'); -} - -.vs-dark .settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more { - background-image: url('configure-inverse.svg'); -} - -.vs .settings-editor.showing-modified-only > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more::before { - border-color : #fff; -} - -.vs-dark .settings-editor.showing-modified-only > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more::before { - border-color : #000; -} - -.settings-editor.showing-modified-only > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more::before { - content: ""; - width: 6px; - height: 6px; - position: absolute; - top: 3px; - right: 3px; - border-radius: 10px; - border: 1px solid; + background-size: 16px; } .settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget > .monaco-action-bar .action-item { @@ -122,8 +116,8 @@ text-transform: none; font-size: 13px; - padding-bottom: 3px; - padding-top: 6px; + padding-bottom: 7px; + padding-top: 7px; padding-left: 8px; padding-right: 8px; } @@ -131,40 +125,91 @@ .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 > .settings-body > .no-results { + margin-top: 20px; + display: none; +} + +.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: 188px; + padding-left: 20px; } +.settings-editor > .settings-body > .settings-tree-container .setting-measure-container { + /* 20 from monaco-tree-wrapper + 20 from monaco-tree-row */ + padding-left: 40px; +} + +.settings-editor > .settings-body .settings-tree-container .monaco-tree-rows { + width: calc(100% - 20px); +} + +.settings-editor > .settings-body .settings-tree-container .monaco-tree-row > .content::before { + /* Hide twisties */ + display: none !important; +} + +.settings-editor > .settings-body .settings-tree-container .monaco-toolbar { + display: none; + position: absolute; + left: -23px; + top: 11px; +} + +.settings-editor > .settings-body .settings-tree-container .monaco-tree-row:hover .monaco-toolbar { + display: flex; +} + +.settings-editor > .settings-body .settings-tree-container .monaco-tree-row .setting-item.focused .monaco-toolbar { + display: flex; +} + +.settings-editor > .settings-body .settings-tree-container .monaco-toolbar .toolbar-toggle-more { + display: block; + width: 22px; + height: 22px; + background-position: center; + background-repeat: no-repeat; + background-size: 16px; +} + +.vs .settings-editor > .settings-body .settings-tree-container .monaco-toolbar .toolbar-toggle-more { + background-image: url('configure.svg'); +} + +.vs-dark .settings-editor > .settings-body .settings-tree-container .monaco-toolbar .toolbar-toggle-more { + background-image: url('configure-inverse.svg'); +} .settings-editor > .settings-body .settings-toc-container { - width: 175px; - margin-right: 5px; - padding-top: 8px; - box-sizing: border-box; -} - -.settings-editor > .settings-body .settings-toc-container.hidden { - display: none; + position: absolute; + width: 160px; + margin-top: 16px; + padding-left: 5px; } .settings-editor.search-mode .settings-toc-container { display: none; } -.settings-editor.search-mode > .settings-body .settings-tree-container { - max-width: 1100px; -} - .settings-editor.narrow > .settings-body .settings-toc-container { display: none; } @@ -173,30 +218,57 @@ 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 */ - padding-top: 8px; - box-sizing: border-box; + margin-top: 14px; border-spacing: 0; border-collapse: separate; position: relative; } +.settings-editor > .settings-body > .settings-tree-container .monaco-tree-row { + overflow: visible; /* so validation messages dont get clipped */ +} + .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; @@ -209,10 +281,25 @@ text-overflow: ellipsis; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-is-configured-label { - font-style: italic; - opacity: 0.8; - margin-right: 7px; + +.settings-editor > .settings-body > .settings-tree-container .setting-item > .setting-item-modified-indicator { + display: none; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.is-configured > .setting-item-modified-indicator { + display: block; + content: ' '; + position: absolute; + width: 6px; + border-left-width: 2px; + border-left-style: solid; + left: 0px; + top: 15px; + bottom: 16px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item-bool.is-configured > .setting-item-modified-indicator { + bottom: 23px; } .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-overrides { @@ -224,27 +311,73 @@ margin-right: 7px; } +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-cat-label-container { + float: left; +} + .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; + user-select: text; + cursor: text; } .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-deprecation-message, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { margin-top: 3px; - overflow: hidden; - text-overflow: ellipsis; - height: 18px; + user-select: text; + cursor: text; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description * { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { + position: absolute; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { + display: none; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.invalid-input .setting-item-validation-message { + display: block; + position: absolute; + padding: 5px; + box-sizing: border-box; + margin-top: -1px; + z-index: 1; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-text .setting-item-validation-message { + width: 500px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-number .setting-item-validation-message { + width: 200px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-number input[type=number]::-webkit-inner-spin-button { + /* Hide arrow button that shows in type=number fields */ + -webkit-appearance: none !important; +} + +.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; + text-decoration: underline; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover { + text-decoration: underline; +} + +.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"; } @@ -254,9 +387,18 @@ visibility: hidden; } -.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; +.settings-editor.search-mode > .settings-body > .settings-tree-container .setting-measure-container.measure-bool-description .setting-item-description { + /* Allocate space for the checkbox control */ + margin-left: 27px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-enumDescription { + display: none; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .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 { @@ -264,27 +406,28 @@ } .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 { background: url('check.svg') center center no-repeat; } -.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.checked { +.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.checked, +.hc-black .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.checked { background: url('check-inverse.svg') center center no-repeat; } .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value { - margin-top: 7px; + margin-top: 9px; display: flex; } @@ -292,14 +435,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, @@ -316,10 +462,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 { @@ -328,7 +484,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 { @@ -337,7 +493,7 @@ } .settings-editor > .settings-body > .settings-tree-container .settings-group-level-2 { - padding-top: 27px; + padding-top: 32px; font-size: 20px; } 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..deb7ff3d83c --- /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 .setting-exclude-row .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 70beb50fe4f..5ae5ee293a8 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -10,11 +10,14 @@ 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'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class OpenRawDefaultSettingsAction extends Action { @@ -37,18 +40,18 @@ export class OpenRawDefaultSettingsAction extends Action { export class OpenSettings2Action extends Action { public static readonly ID = 'workbench.action.openSettings2'; - public static readonly LABEL = nls.localize('openSettings2', "Open Settings (Preview)"); + public static readonly LABEL = nls.localize('openSettings2', "Open Settings (UI)"); constructor( id: string, label: string, - @IPreferencesService private preferencesService2: IPreferencesService + @IPreferencesService private preferencesService: IPreferencesService ) { super(id, label); } public run(event?: any): TPromise { - return this.preferencesService2.openSettings2(); + return this.preferencesService.openSettings(false); } } @@ -58,6 +61,26 @@ export class OpenSettingsAction extends Action { public static readonly ID = 'workbench.action.openSettings'; public static readonly LABEL = nls.localize('openSettings', "Open Settings"); + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService, + @IConfigurationService private configurationService: IConfigurationService, + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const jsonEditorPreferred = this.configurationService.getValue('workbench.settings.editor') === 'json'; + return this.preferencesService.openSettings(jsonEditorPreferred); + } +} + +export class OpenSettingsJsonAction extends Action { + + public static readonly ID = 'workbench.action.openSettingsJson'; + public static readonly LABEL = nls.localize('openSettingsJson', "Open Settings (JSON)"); + constructor( id: string, label: string, @@ -67,7 +90,7 @@ export class OpenSettingsAction extends Action { } public run(event?: any): TPromise { - return this.preferencesService.openSettings(); + return this.preferencesService.openSettings(true); } } @@ -79,13 +102,15 @@ export class OpenGlobalSettingsAction extends Action { constructor( id: string, label: string, - @IPreferencesService private preferencesService: IPreferencesService + @IPreferencesService private preferencesService: IPreferencesService, + @IConfigurationService private configurationService: IConfigurationService ) { super(id, label); } public run(event?: any): TPromise { - return this.preferencesService.openGlobalSettings(); + const jsonEditorPreferred = this.configurationService.getValue('workbench.settings.editor') === 'json'; + return this.preferencesService.openGlobalSettings(jsonEditorPreferred); } } @@ -154,6 +179,7 @@ export class OpenWorkspaceSettingsAction extends Action { id: string, label: string, @IPreferencesService private preferencesService: IPreferencesService, + @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService ) { super(id, label); @@ -166,7 +192,8 @@ export class OpenWorkspaceSettingsAction extends Action { } public run(event?: any): TPromise { - return this.preferencesService.openWorkspaceSettings(); + const jsonEditorPreferred = this.configurationService.getValue('workbench.settings.editor') === 'json'; + return this.preferencesService.openWorkspaceSettings(jsonEditorPreferred); } public dispose(): void { @@ -189,6 +216,8 @@ export class OpenFolderSettingsAction extends Action { id: string, label: string, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService, + @IPreferencesService private preferencesService: IPreferencesService, @ICommandService private commandService: ICommandService ) { super(id, label); @@ -205,8 +234,10 @@ export class OpenFolderSettingsAction extends Action { return this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID) .then(workspaceFolder => { if (workspaceFolder) { - return this.commandService.executeCommand(OPEN_FOLDER_SETTINGS_COMMAND, workspaceFolder.uri); + const jsonEditorPreferred = this.configurationService.getValue('workbench.settings.editor') === 'json'; + return this.preferencesService.openFolderSettings(workspaceFolder.uri, jsonEditorPreferred); } + return null; }); } @@ -225,8 +256,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); @@ -234,7 +266,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; @@ -247,14 +279,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..5d9feab4b09 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -8,7 +8,7 @@ import { Button } from 'vs/base/browser/ui/button/button'; import { Widget } from 'vs/base/browser/ui/widget'; import * as arrays from 'vs/base/common/arrays'; import { Delayer, ThrottledDelayer } from 'vs/base/common/async'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IStringDictionary } from 'vs/base/common/collections'; import { getErrorMessage, isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -120,7 +120,7 @@ export class PreferencesEditor extends BaseEditor { }); openSettings2Button.label = nls.localize('openSettings2Label', "new settings editor"); openSettings2Button.element.classList.add('open-settings2-button'); - this._register(openSettings2Button.onDidClick(() => this.preferencesService.openSettings2())); + this._register(openSettings2Button.onDidClick(() => this.preferencesService.openSettings(false))); this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.headerContainer, { ariaLabel: nls.localize('SearchSettingsWidget.AriaLabel', "Search settings"), @@ -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 { @@ -263,11 +263,11 @@ export class PreferencesEditor extends BaseEditor { const promise = this.input && this.input.isDirty() ? this.input.save() : TPromise.as(true); promise.done(value => { if (target === ConfigurationTarget.USER) { - this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource); + this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource, true); } else if (target === ConfigurationTarget.WORKSPACE) { - this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource); + this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource, true); } else if (target instanceof URI) { - this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target); + this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target, true); } }); } @@ -372,7 +372,7 @@ class PreferencesRenderersController extends Disposable { private _editablePreferencesRendererDisposables: IDisposable[] = []; private _settingsNavigator: SettingsNavigator; - private _remoteFilterInProgress: TPromise; + private _remoteFilterCancelToken: CancellationTokenSource; private _prefsModelsForSearch = new Map(); private _currentLocalSearchProvider: ISearchProvider; @@ -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,17 +448,25 @@ class PreferencesRenderersController extends Disposable { } remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise { - if (this._remoteFilterInProgress && this._remoteFilterInProgress.cancel) { - // Resolved/rejected promises have no .cancel() - this._remoteFilterInProgress.cancel(); + if (this.lastFilterResult && this.lastFilterResult.exactMatch) { + // Skip and clear remote search + query = ''; + } + + if (this._remoteFilterCancelToken) { + this._remoteFilterCancelToken.cancel(); + this._remoteFilterCancelToken.dispose(); + this._remoteFilterCancelToken = null; } this._currentRemoteSearchProvider = (updateCurrentResults && this._currentRemoteSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query); - this._remoteFilterInProgress = this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1, updateCurrentResults); - - return this._remoteFilterInProgress.then(() => { - this._remoteFilterInProgress = null; + this._remoteFilterCancelToken = new CancellationTokenSource(); + return this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1, this._remoteFilterCancelToken.token, updateCurrentResults).then(() => { + if (this._remoteFilterCancelToken) { + this._remoteFilterCancelToken.dispose(); + this._remoteFilterCancelToken = null; + } }, err => { if (isPromiseCanceledError(err)) { return null; @@ -466,23 +476,24 @@ class PreferencesRenderersController extends Disposable { }); } - localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise { + localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise { if (this._settingsNavigator) { this._settingsNavigator.reset(); } this._currentLocalSearchProvider = (updateCurrentResults && this._currentLocalSearchProvider) || this.preferencesSearchService.getLocalSearchProvider(query); - return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0, updateCurrentResults); + return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0, undefined, updateCurrentResults); } - private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, editableContentOnly?: boolean): TPromise { + private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken, 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, token)]; if (!editableContentOnly) { filterPs.push( - this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder)); - filterPs.push(this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder)); + this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)); + filterPs.push( + this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder, token).then(() => null)); } return TPromise.join(filterPs).then(results => { @@ -494,25 +505,27 @@ class PreferencesRenderersController extends Disposable { this.consolidateAndUpdate(defaultFilterResult, editableFilterResult); this._lastFilterResult = defaultFilterResult; + + return defaultFilterResult && defaultFilterResult.exactMatch; }); } - private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise { + private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { const searchPs = [ - this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.WORKSPACE, groupId, groupLabel, groupOrder), - this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER, groupId, groupLabel, groupOrder) + this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.WORKSPACE, groupId, groupLabel, groupOrder, token), + this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER, groupId, groupLabel, groupOrder, token) ]; for (const folder of this.workspaceContextService.getWorkspace().folders) { const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri); - searchPs.push(this.searchSettingsTarget(query, searchProvider, folderSettingsResource, groupId, groupLabel, groupOrder)); + searchPs.push(this.searchSettingsTarget(query, searchProvider, folderSettingsResource, groupId, groupLabel, groupOrder, token)); } return TPromise.join(searchPs).then(() => { }); } - private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget, groupId: string, groupLabel: string, groupOrder: number): Promise { + private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { if (!query) { // Don't open the other settings targets when query is empty this._onDidFilterResultsCountChange.fire({ target, count: 0 }); @@ -520,7 +533,7 @@ class PreferencesRenderersController extends Disposable { } return this.getPreferencesEditorModel(target).then(model => { - return model && this._filterOrSearchPreferencesModel('', model, provider, groupId, groupLabel, groupOrder); + return model && this._filterOrSearchPreferencesModel('', model, provider, groupId, groupLabel, groupOrder, token); }).then(result => { const count = result ? this._flatten(result.filteredGroups).length : 0; this._onDidFilterResultsCountChange.fire({ target, count }); @@ -578,20 +591,20 @@ class PreferencesRenderersController extends Disposable { } } - private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise { + private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { if (!preferencesRenderer) { return TPromise.wrap(null); } const model = preferencesRenderer.preferencesModel; - return this._filterOrSearchPreferencesModel(filter, model, provider, groupId, groupLabel, groupOrder).then(filterResult => { + return this._filterOrSearchPreferencesModel(filter, model, provider, groupId, groupLabel, groupOrder, token).then(filterResult => { preferencesRenderer.filterPreferences(filterResult); return filterResult; }); } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise { - const searchP = provider ? provider.searchModel(model) : TPromise.wrap(null); + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { + const searchP = provider ? provider.searchModel(model, token) : TPromise.wrap(null); return searchP .then(null, err => { if (isPromiseCanceledError(err)) { @@ -613,6 +626,10 @@ class PreferencesRenderersController extends Disposable { } }) .then(searchResult => { + if (token && token.isCancellationRequested) { + searchResult = null; + } + const filterResult = searchResult ? model.updateResultGroup(groupId, { id: groupId, @@ -624,6 +641,7 @@ 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/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 3c57b2f5fcb..642fc76e4e4 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import URI from 'vs/base/common/uri'; -import { $ } from 'vs/base/browser/builder'; import * as DOM from 'vs/base/browser/dom'; import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -318,7 +317,7 @@ export class FolderSettingsActionItem extends BaseActionItem { } public render(container: HTMLElement): void { - this.builder = $(container); + this.element = container; this.container = container; this.labelElement = DOM.$('.action-title'); diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index 7fe7a471b59..d891a63aabc 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ 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 { CancellationToken, CancellationTokenSource } 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'; @@ -24,48 +24,57 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c 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 { INotificationService } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; 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 { badgeBackground, badgeForeground, contrastBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { attachButtonStyler, attachStylerCallback } 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 { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; +import { SuggestEnabledInput } from 'vs/workbench/parts/codeEditor/browser/suggestEnabledInput'; +import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; -import { 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 { resolveExtensionsSettings, resolveSettingsTree, SettingsRenderer, SettingsTree, SimplePagedDataSource, SettingsDataSource } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { ISettingsEditorViewState, MODIFIED_SETTING_TAG, ONLINE_SERVICES_SETTING_TAG, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, countSettingGroupChildrenWithPredicate, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +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 { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; 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'; + private static NUM_INSTANCES: number = 0; + + private static readonly SUGGESTIONS: string[] = [ + '@modified', '@tag:usesOnlineServices' + ]; private defaultSettingsEditorModel: DefaultSettingsEditorModel; private rootElement: HTMLElement; private headerContainer: HTMLElement; - private searchWidget: SearchWidget; + private searchWidget: SuggestEnabledInput; + private countElement: HTMLElement; private settingsTargetsWidget: SettingsTargetsWidget; private toolbar: ToolBar; private settingsTreeContainer: HTMLElement; - private settingsTree: WorkbenchTree; - private treeDataSource: SettingsDataSource; + private settingsTree: Tree; + private settingsTreeRenderer: SettingsRenderer; + private settingsTreeDataSource: SimplePagedDataSource; private tocTreeModel: TOCTreeModel; private settingsTreeModel: SettingsTreeModel; + private noResultsMessage: HTMLElement; private tocTreeContainer: HTMLElement; private tocTree: WorkbenchTree; @@ -73,22 +82,25 @@ export class SettingsEditor2 extends BaseEditor { private delayedFilterLogging: Delayer; private localSearchDelayer: Delayer; private remoteSearchThrottle: ThrottledDelayer; - private searchInProgress: TPromise; + private searchInProgress: CancellationTokenSource; + + private delayRefreshOnLayout: Delayer; + private lastLayedoutWidth: number; 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 scheduledRefreshes: Map; + + private tagRegex = /(^|\s)@tag:("([^"]*)"|[^"]\S*)/g; + /** Don't spam warnings */ private hasWarnedMissingSettings: boolean; @@ -102,44 +114,51 @@ export class SettingsEditor2 extends BaseEditor { @ILogService private logService: ILogService, @IEnvironmentService private environmentService: IEnvironmentService, @IContextKeyService contextKeyService: IContextKeyService, - @IContextMenuService private contextMenuService: IContextMenuService + @IContextMenuService private contextMenuService: IContextMenuService, + @IStorageService private storageService: IStorageService, + @INotificationService private notificationService: INotificationService ) { super(SettingsEditor2.ID, telemetryService, themeService); this.delayedFilterLogging = new Delayer(1000); - this.localSearchDelayer = new Delayer(100); + this.localSearchDelayer = new Delayer(300); this.remoteSearchThrottle = new ThrottledDelayer(200); this.viewState = { settingsTarget: ConfigurationTarget.USER }; + this.delayRefreshOnLayout = new Delayer(100); - this.settingUpdateDelayer = new Delayer(500); + this.settingUpdateDelayer = new Delayer(200); 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 => { - this.onConfigUpdate(); + this.scheduledRefreshes = new Map(); - if (e.affectsConfiguration('workbench.settings.tocVisible')) { - this.updateTOCVisible(); - } + this._register(configurationService.onDidChangeConfiguration(e => { + this.onConfigUpdate(e.affectedKeys); })); } + private get currentSettingsModel() { + return this.searchResultModel || this.settingsTreeModel; + } + createEditor(parent: HTMLElement): void { parent.setAttribute('tabindex', '-1'); this.rootElement = DOM.append(parent, $('.settings-editor')); this.createHeader(this.rootElement); this.createBody(this.rootElement); + this.updateStyles(); } 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)) .then(() => { - this.render(token); + // Init TOC selection + this.updateTreeScrollSync(); }); } @@ -149,10 +168,19 @@ export class SettingsEditor2 extends BaseEditor { } layout(dimension: DOM.Dimension): void { - this.searchWidget.layout(dimension); this.layoutTrees(dimension); + let innerWidth = dimension.width - 24 * 2; // 24px padding on left and right + let monacoWidth = (innerWidth > 1000 ? 1000 : innerWidth) - 10; + this.searchWidget.layout({ height: 20, width: monacoWidth }); + DOM.toggleClass(this.rootElement, 'narrow', dimension.width < 600); + + // #56185 + if (dimension.width !== this.lastLayedoutWidth) { + this.lastLayedoutWidth = dimension.width; + this.delayRefreshOnLayout.trigger(() => this.renderTree()); + } } focus(): void { @@ -160,30 +188,42 @@ 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(); + } + } + + showContextMenu(): void { + const settingDOMElement = this.settingsTreeRenderer.getSettingDOMElementForDOMElement(document.activeElement); + if (!settingDOMElement) { + return; } - this.settingsTree.domFocus(); + const focusedKey = this.settingsTreeRenderer.getKeyForDOMElementInSetting(settingDOMElement); + if (!focusedKey) { + return; + } + + const elements = this.currentSettingsModel.getElementsByName(focusedKey); + if (elements && elements[0]) { + this.settingsTreeRenderer.showContextMenu(elements[0], settingDOMElement); + } } 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.setValue(''); } - clearSearchResults(): void { - this.searchWidget.clear(); + search(text: string): void { + if (this.searchWidget) { + this.searchWidget.focus(); + this.searchWidget.setValue(text); + } } private createHeader(parent: HTMLElement): void { @@ -198,24 +238,47 @@ export class SettingsEditor2 extends BaseEditor { previewTextLabel.textContent = localize('previewLabel', "This is a preview of our new settings editor"); const searchContainer = DOM.append(this.headerContainer, $('.search-container')); - this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, searchContainer, { - ariaLabel: localize('SearchSettings.AriaLabel', "Search settings"), - placeholder: localize('SearchSettings.Placeholder', "Search settings"), - focusKey: this.searchFocusContextKey, - ariaLive: 'assertive' + + let searchBoxLabel = localize('SearchSettings.AriaLabel', "Search settings"); + this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, { + triggerCharacters: ['@'], + provideResults: (query: string) => { + return SettingsEditor2.SUGGESTIONS.filter(tag => query.indexOf(tag) === -1).map(tag => tag + ' '); + } + }, searchBoxLabel, 'settingseditor:searchinput' + SettingsEditor2.NUM_INSTANCES++, { + placeholderText: searchBoxLabel, + focusContextKey: this.searchFocusContextKey, + // TODO: Aria-live + })); + + this.countElement = DOM.append(searchContainer, DOM.$('.settings-count-widget')); + this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder, badgeForeground }, colors => { + const background = colors.badgeBackground ? colors.badgeBackground.toString() : null; + const border = colors.contrastBorder ? colors.contrastBorder.toString() : null; + + this.countElement.style.backgroundColor = background; + this.countElement.style.color = colors.badgeForeground.toString(); + + this.countElement.style.borderWidth = border ? '1px' : null; + this.countElement.style.borderStyle = border ? 'solid' : null; + this.countElement.style.borderColor = border; })); - this._register(this.searchWidget.onDidChange(() => this.onSearchInputChanged())); + + this._register(this.searchWidget.onInputDidChange(() => this.onSearchInputChanged())); 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 = this.settingsTargetsWidget.settingsTarget; - - this.settingsTreeModel.update(); - this.refreshTreeAndMaintainFocus(); + this.settingsTargetsWidget.onDidTargetChange(target => { + this.viewState.settingsTarget = target; + if (target === ConfigurationTarget.USER) { + this.preferencesService.openGlobalSettings(); + } else if (target === ConfigurationTarget.WORKSPACE) { + this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource); + } else if (target instanceof URI) { + this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target); + } }); this.createHeaderControls(headerControlsContainer); @@ -224,26 +287,51 @@ export class SettingsEditor2 extends BaseEditor { private createHeaderControls(parent: HTMLElement): void { const headerControlsContainerRight = DOM.append(parent, $('.settings-header-controls-right')); - this.toolbar = new ToolBar(headerControlsContainerRight, this.contextMenuService, { + this.toolbar = this._register(new ToolBar(headerControlsContainerRight, this.contextMenuService, { ariaLabel: localize('settingsToolbarLabel', "Settings Editor Actions"), actionRunner: this.actionRunner - }); + })); - const actions = [ - this.instantiationService.createInstance(ToggleShowModifiedOnlyAction, this, this.viewState), - this.instantiationService.createInstance(OpenSettingsAction) + const actions: Action[] = [ + this.instantiationService.createInstance(FilterByTagAction, + localize('filterModifiedLabel', "Show modified settings"), + MODIFIED_SETTING_TAG, + this) ]; + if (this.environmentService.appQuality !== 'stable') { + actions.push( + this.instantiationService.createInstance( + FilterByTagAction, + localize('filterOnlineServicesLabel', "Show settings for online services"), + ONLINE_SERVICES_SETTING_TAG, + this)); + actions.push(new Separator()); + } + actions.push(new Action('settings.openSettingsJson', localize('openSettingsJsonLabel', "Open settings.json"), undefined, undefined, () => { + return this.openSettingsFile().then(editor => { + const currentSearch = this.searchWidget.getValue(); + if (editor instanceof PreferencesEditor && currentSearch) { + editor.focusSearch(currentSearch); + } + }); + })); + this.toolbar.setActions([], actions)(); - this.toolbar.context = this.settingsTargetsWidget.settingsTarget; + 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(); + private revealSettingByKey(settingKey: string): void { + const elements = this.currentSettingsModel.getElementsByName(settingKey); + if (elements && elements[0]) { + this.settingsTree.reveal(elements[0]); + + const domElements = this.settingsTreeRenderer.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), settingKey); + if (domElements && domElements[0]) { + const control = domElements[0].querySelector(SettingsRenderer.CONTROL_SELECTOR); + if (control) { + (control).focus(); + } + } } } @@ -251,57 +339,108 @@ export class SettingsEditor2 extends BaseEditor { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; if (currentSettingsTarget === ConfigurationTarget.USER) { - return this.preferencesService.openGlobalSettings(); + return this.preferencesService.openGlobalSettings(true); } else if (currentSettingsTarget === ConfigurationTarget.WORKSPACE) { - return this.preferencesService.openWorkspaceSettings(); + return this.preferencesService.openWorkspaceSettings(true); } else { - return this.preferencesService.openFolderSettings(currentSettingsTarget); + return this.preferencesService.openFolderSettings(currentSettingsTarget, true); } } private createBody(parent: HTMLElement): void { const bodyContainer = DOM.append(parent, $('.settings-body')); - this.createTOC(bodyContainer); + this.noResultsMessage = DOM.append(bodyContainer, $('.no-results')); + this.noResultsMessage.innerText = localize('noResults', "No Settings Found"); + this._register(attachStylerCallback(this.themeService, { editorForeground }, colors => { + this.noResultsMessage.style.color = colors.editorForeground ? colors.editorForeground.toString() : null; + })); + + 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; + } + } + + 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.viewState); 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; + const element: SettingsTreeGroupElement = 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); - } + this.renderTree(); } + if (element && (!e.payload || !e.payload.fromScroll)) { + let refreshP = TPromise.wrap(null); + if (this.settingsTreeDataSource.pageTo(element.index)) { + refreshP = this.renderTree(); + } + + refreshP.then(() => this.settingsTree.reveal(element, 0)); + } })); this._register(this.tocTree.onDidFocus(() => { @@ -311,115 +450,36 @@ export class SettingsEditor2 extends BaseEditor { this._register(this.tocTree.onDidBlur(() => { this.tocRowFocused.set(false); })); - - this.updateTOCVisible(); - } - - private updateTOCVisible(): void { - const visible = !!this.configurationService.getValue('workbench.settings.tocVisible'); - DOM.toggleClass(this.tocTreeContainer, 'hidden', !visible); } 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.revealSettingByKey(settingName))); + this._register(this.settingsTreeRenderer.onDidFocusSetting(element => { + this.settingsTree.reveal(element); + })); + + this.settingsTreeDataSource = this.instantiationService.createInstance(SimplePagedDataSource, + this.instantiationService.createInstance(SettingsDataSource, this.viewState)); + + this.settingsTree = this._register(this.instantiationService.createInstance(SettingsTree, + this.settingsTreeContainer, + this.viewState, + { + renderer: this.settingsTreeRenderer, + dataSource: this.settingsTreeDataSource + })); + this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex'); this._register(this.settingsTree.onDidScroll(() => { this.updateTreeScrollSync(); @@ -438,16 +498,16 @@ export class SettingsEditor2 extends BaseEditor { })); } - toggleShowModifiedOnly(): TPromise { - this.viewState.showConfiguredOnly = !this.viewState.showConfiguredOnly; - DOM.toggleClass(this.rootElement, 'showing-modified-only', this.viewState.showConfiguredOnly); - return this.refreshTreeAndMaintainFocus().then(() => { - this.settingsTree.setScrollPosition(0); - this.expandAll(this.settingsTree); - }); + public notifyNoSaveNeeded(force: boolean = true) { + if (force || !this.storageService.getBoolean('hasNotifiedOfSettingsAutosave', StorageScope.GLOBAL, false)) { + this.storageService.store('hasNotifiedOfSettingsAutosave', true, StorageScope.GLOBAL); + this.notificationService.info(localize('settingsNoSaveNeeded', "Your changes are automatically saved as you edit.")); + } } private onDidChangeSetting(key: string, value: any): void { + this.notifyNoSaveNeeded(false); + if (this.pendingSettingUpdate && this.pendingSettingUpdate.key !== key) { this.updateChangedSetting(key, value); } @@ -457,6 +517,7 @@ export class SettingsEditor2 extends BaseEditor { } private updateTreeScrollSync(): void { + this.settingsTreeRenderer.cancelSuggesters(); if (this.searchResultModel) { return; } @@ -465,25 +526,21 @@ 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; - } - } + this.updateTreePagingByScroll(); + 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]); @@ -491,12 +548,19 @@ export class SettingsEditor2 extends BaseEditor { } } + private updateTreePagingByScroll(): void { + const lastVisibleElement = this.settingsTree.getLastVisibleElement(); + if (lastVisibleElement && this.settingsTreeDataSource.pageTo(lastVisibleElement.index)) { + this.renderTree(); + } + } + private updateChangedSetting(key: string, value: any): TPromise { // ConfigurationService displays the error if this fails. // Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change const settingsTarget = this.settingsTargetsWidget.settingsTarget; const resource = URI.isUri(settingsTarget) ? settingsTarget : undefined; - const configurationTarget = (resource ? undefined : settingsTarget); + const configurationTarget = (resource ? ConfigurationTarget.WORKSPACE_FOLDER : settingsTarget); const overrides: IConfigurationOverrides = { resource }; // If the user is changing the value back to the default, do a 'reset' instead @@ -506,14 +570,14 @@ export class SettingsEditor2 extends BaseEditor { } return this.configurationService.updateValue(key, value, overrides, configurationTarget) - .then(() => this.refreshTreeAndMaintainFocus()) + .then(() => this.renderTree(key)) .then(() => { const reportModifiedProps = { key, 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 }; @@ -583,13 +647,16 @@ export class SettingsEditor2 extends BaseEditor { private render(token: CancellationToken): TPromise { if (this.input) { return this.input.resolve() - .then((model: DefaultSettingsEditorModel) => { + .then(model => { if (token.isCancellationRequested) { return void 0; } - this.defaultSettingsEditorModel = model; - this.onConfigUpdate(); + return this.preferencesService.createPreferencesEditorModel((model).textEditorModel.uri); + }).then((defaultSettingsEditorModel: DefaultSettingsEditorModel) => { + this._register(defaultSettingsEditorModel.onDidChangeGroups(() => this.onConfigUpdate())); + this.defaultSettingsEditorModel = defaultSettingsEditorModel; + return this.onConfigUpdate(); }); } return TPromise.as(null); @@ -602,7 +669,30 @@ export class SettingsEditor2 extends BaseEditor { } } - private onConfigUpdate(): TPromise { + private scheduleRefresh(element: HTMLElement, key = ''): void { + if (key && this.scheduledRefreshes.has(key)) { + return; + } + + if (!key) { + this.scheduledRefreshes.forEach(r => r.dispose()); + this.scheduledRefreshes.clear(); + } + + const scheduledRefreshTracker = DOM.trackFocus(element); + this.scheduledRefreshes.set(key, scheduledRefreshTracker); + scheduledRefreshTracker.onDidBlur(() => { + scheduledRefreshTracker.dispose(); + this.scheduledRefreshes.delete(key); + this.onConfigUpdate([key]); + }); + } + + private onConfigUpdate(keys?: string[]): TPromise { + if (keys) { + return this.updateElementsByKey(keys); + } + const groups = this.defaultSettingsEditorModel.settingsGroups.slice(1); // Without commonlyUsed const dividedGroups = collections.groupBy(groups, g => g.contributedByExtension ? 'extension' : 'core'); const settingsResult = resolveSettingsTree(tocData, dividedGroups.core); @@ -630,8 +720,10 @@ export class SettingsEditor2 extends BaseEditor { if (this.settingsTreeModel) { this.settingsTreeModel.update(resolvedSettingsRoot); + return this.renderTree(); } else { - this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState, resolvedSettingsRoot); + this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState); + this.settingsTreeModel.update(resolvedSettingsRoot); this.settingsTree.setInput(this.settingsTreeModel.root); this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement; @@ -642,81 +734,154 @@ export class SettingsEditor2 extends BaseEditor { } } - return this.refreshTreeAndMaintainFocus(); + return TPromise.wrap(null); } - private refreshTreeAndMaintainFocus(): TPromise { - // Sort of a hack to maintain focus on the focused control across a refresh - const focusedRowItem = DOM.findParentWithClass(document.activeElement, 'setting-item'); - const focusedRowId = focusedRowItem && focusedRowItem.id; - const selection = focusedRowId && document.activeElement.tagName.toLowerCase() === 'input' ? - (document.activeElement).selectionStart : - null; + private updateElementsByKey(keys: string[]): TPromise { + if (keys.length) { + keys.forEach(key => this.currentSettingsModel.updateElementsByName(key)); + return TPromise.join( + keys.map(key => this.renderTree(key))) + .then(() => { }); + } else { + return this.renderTree(); + } + } - return this.settingsTree.refresh() - .then(() => { - if (focusedRowId) { - this.focusEditControlForRow(focusedRowId, selection); + private renderTree(key?: string): TPromise { + if (key && this.scheduledRefreshes.has(key)) { + this.updateModifiedLabelForKey(key); + return TPromise.wrap(null); + } + + // If a setting control is currently focused, schedule a refresh for later + const focusedSetting = this.settingsTreeRenderer.getSettingDOMElementForDOMElement(document.activeElement); + if (focusedSetting) { + // If a single setting is being refreshed, it's ok to refresh now if that is not the focused setting + if (key) { + const focusedKey = focusedSetting.getAttribute(SettingsRenderer.SETTING_KEY_ATTR); + if (focusedKey === key && + !DOM.hasClass(focusedSetting, 'setting-item-exclude')) { // update `exclude`s live, as they have a separate "submit edit" step built in before this + + this.updateModifiedLabelForKey(key); + this.scheduleRefresh(focusedSetting, key); + return TPromise.wrap(null); } - }) - .then(() => { - return this.tocTree.refresh(); - }); + } else { + this.scheduleRefresh(focusedSetting); + return TPromise.wrap(null); + } + } + + let refreshP: TPromise; + if (key) { + const elements = this.currentSettingsModel.getElementsByName(key); + if (elements && elements.length) { + refreshP = TPromise.join(elements.map(e => this.settingsTree.refresh(e))); + } else { + // Refresh requested for a key that we don't know about + return TPromise.wrap(null); + } + } else { + refreshP = this.settingsTree.refresh(); + } + + return refreshP.then(() => { + this.tocTreeModel.update(); + return this.tocTree.refresh(); + }).then(() => { }); } - 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`); - if (inputElementToFocus) { - inputElementToFocus.focus(); - if (typeof selection === 'number') { - (inputElementToFocus).setSelectionRange(selection, selection); - } + private updateModifiedLabelForKey(key: string): void { + const dataElements = this.currentSettingsModel.getElementsByName(key); + const isModified = dataElements && dataElements[0] && dataElements[0].isConfigured; // all elements are either configured or not + const elements = this.settingsTreeRenderer.getDOMElementsForSettingKey(this.settingsTree.getHTMLElement(), key); + if (elements && elements[0]) { + DOM.toggleClass(elements[0], 'is-configured', isModified); } } private onSearchInputChanged(): void { const query = this.searchWidget.getValue().trim(); this.delayedFilterLogging.cancel(); - this.triggerSearch(query).then(() => { + this.triggerSearch(query.replace(/›/g, ' ')).then(() => { if (query && this.searchResultModel) { this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(query, this.searchResultModel.getUniqueResults())); } }); } + private parseSettingFromJSON(query: string): string { + const match = query.match(/"([a-zA-Z.]+)": /); + return match && match[1]; + } + private triggerSearch(query: string): TPromise { + this.viewState.tagFilters = new Set(); if (query) { - return this.searchInProgress = TPromise.join([ - this.localSearchDelayer.trigger(() => this.localFilterPreferences(query)), - this.remoteSearchThrottle.trigger(() => this.remoteSearchPreferences(query), 500) - ]).then(() => { - this.searchInProgress = null; + 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 && query !== '@') { + query = this.parseSettingFromJSON(query) || query; + return this.triggerFilterPreferences(query); } else { - this.localSearchDelayer.cancel(); - this.remoteSearchThrottle.cancel(); - if (this.searchInProgress && this.searchInProgress.cancel) { - this.searchInProgress.cancel(); + if (this.viewState.tagFilters && this.viewState.tagFilters.size) { + this.searchResultModel = this.createFilterModel(); + } else { + this.searchResultModel = null; + } + + this.localSearchDelayer.cancel(); + this.remoteSearchThrottle.cancel(); + if (this.searchInProgress) { + this.searchInProgress.cancel(); + this.searchInProgress.dispose(); + this.searchInProgress = null; } - this.searchResultModel = null; - this.tocTreeModel.currentSearchModel = null; this.viewState.filterToCategory = null; + this.tocTreeModel.currentSearchModel = this.searchResultModel; this.tocTree.refresh(); this.toggleSearchMode(); - this.settingsTree.setInput(this.settingsTreeModel.root); + collapseAll(this.tocTree); - return TPromise.wrap(null); + if (this.searchResultModel) { + return this.settingsTree.setInput(this.searchResultModel.root).then(() => this.renderResultCountMessages()); + } else { + return this.settingsTree.setInput(this.settingsTreeModel.root).then(() => this.renderResultCountMessages()); + } } } - private expandAll(tree: ITree): void { - const nav = tree.getNavigator(); - let cur; - while (cur = nav.next()) { - tree.expand(cur); + /** + * Return a fake SearchResultModel which can hold a flat list of all settings, to be filtered (@modified etc) + */ + private createFilterModel(): SearchResultModel { + const filterModel = this.instantiationService.createInstance(SearchResultModel, this.viewState); + + const fullResult: ISearchResult = { + filterMatches: [] + }; + for (let g of this.defaultSettingsEditorModel.settingsGroups.slice(1)) { + for (let sect of g.sections) { + for (let setting of sect.settings) { + fullResult.filterMatches.push({ setting, matches: [], score: 0 }); + } + } } + + filterModel.setResult(0, fullResult); + + return filterModel; } private reportFilteringUsed(query: string, results: ISearchResult[]): void { @@ -758,48 +923,86 @@ export class SettingsEditor2 extends BaseEditor { this.telemetryService.publicLog('settingsEditor.filter', data); } - private localFilterPreferences(query: string): TPromise { - const localSearchProvider = this.preferencesSearchService.getLocalSearchProvider(query); - return this.filterOrSearchPreferences(query, SearchResultIdx.Local, localSearchProvider); - } + private triggerFilterPreferences(query: string): TPromise { + if (this.searchInProgress) { + this.searchInProgress.cancel(); + this.searchInProgress = null; + } - private remoteSearchPreferences(query: string): TPromise { - const remoteSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query); - return this.filterOrSearchPreferences(query, SearchResultIdx.Remote, remoteSearchProvider); - } - - 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 => { - 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); - this.tocTreeModel.currentSearchModel = this.searchResultModel; - this.toggleSearchMode(); - this.settingsTree.setInput(this.searchResultModel); - } else { - this.searchResultModel.setResult(type, result); - } - - this.tocTreeModel.update(); - resolve(this.refreshTreeAndMaintainFocus()); - }); - }, () => { - isCanceled = true; + // Trigger the local search. If it didn't find an exact match, trigger the remote search. + const searchInProgress = this.searchInProgress = new CancellationTokenSource(); + return this.localSearchDelayer.trigger(() => { + if (searchInProgress && !searchInProgress.token.isCancellationRequested) { + return this.localFilterPreferences(query).then(result => { + if (!result.exactMatch) { + this.remoteSearchThrottle.trigger(() => { + return searchInProgress && !searchInProgress.token.isCancellationRequested ? + this.remoteSearchPreferences(query, this.searchInProgress.token) : + TPromise.wrap(null); + }); + } + }).then(() => this.renderResultCountMessages()); + } else { + return TPromise.wrap(null); + } }); } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider): TPromise { - const searchP = provider ? provider.searchModel(model) : TPromise.wrap(null); + private localFilterPreferences(query: string, token?: CancellationToken): TPromise { + const localSearchProvider = this.preferencesSearchService.getLocalSearchProvider(query); + return this.filterOrSearchPreferences(query, SearchResultIdx.Local, localSearchProvider, token); + } + + private remoteSearchPreferences(query: string, token?: CancellationToken): TPromise { + const remoteSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query); + const newExtSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query, true); + + return TPromise.join([ + this.filterOrSearchPreferences(query, SearchResultIdx.Remote, remoteSearchProvider, token), + this.filterOrSearchPreferences(query, SearchResultIdx.NewExtensions, newExtSearchProvider, token) + ]).then(() => { }); + } + + private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider, token?: CancellationToken): TPromise { + return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token).then(result => { + if (token && token.isCancellationRequested) { + // Handle cancellation like this because cancellation is lost inside the search provider due to async/await + return null; + } + + if (!this.searchResultModel) { + this.searchResultModel = this.instantiationService.createInstance(SearchResultModel, this.viewState); + this.searchResultModel.setResult(type, result); + this.tocTreeModel.currentSearchModel = this.searchResultModel; + this.toggleSearchMode(); + this.settingsTree.setInput(this.searchResultModel.root); + } else { + this.tocTreeModel.update(); + this.searchResultModel.setResult(type, result); + } + + + this.tocTree.setSelection([]); + expandAll(this.tocTree); + + return this.renderTree().then(() => result); + }); + } + + private renderResultCountMessages() { + let count = countSettingGroupChildrenWithPredicate(this.settingsTree.getInput() as SettingsTreeGroupElement, element => element.matchesAllTags(this.viewState.tagFilters)); + switch (count) { + case 0: this.countElement.innerText = localize('noResults', "No Settings Found"); break; + case 1: this.countElement.innerText = localize('oneResult', "1 Setting Found"); break; + default: this.countElement.innerText = localize('moreThanOneResult', "{0} Settings Found", count); + } + + this.countElement.style.display = 'block'; + this.noResultsMessage.style.display = count === 0 ? 'block' : 'none'; + } + + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, token?: CancellationToken): TPromise { + const searchP = provider ? provider.searchModel(model, token) : TPromise.wrap(null); return searchP .then(null, err => { if (isPromiseCanceledError(err)) { @@ -823,65 +1026,50 @@ export class SettingsEditor2 extends BaseEditor { } private layoutTrees(dimension: DOM.Dimension): void { - const listHeight = dimension.height - (DOM.getDomNodePagePosition(this.headerContainer).height + 11 /*padding*/); - this.settingsTreeContainer.style.height = `${listHeight}px`; - this.settingsTree.layout(listHeight, 800); + const listHeight = dimension.height - (97 + 11 /* header height + 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); + + this.settingsTreeRenderer.updateWidth(dimension.width); + } + + public updateStyles(): void { + super.updateStyles(); + this.searchWidget.updateStyles(); + } + + setVisible(visible: boolean, group?: IEditorGroup): TPromise { + if (visible) { + this.searchWidget.focus(); + this.searchWidget.selectAll(); } - this.tocTreeContainer.style.height = `${listHeight}px`; - this.tocTree.layout(listHeight, 175); + return TPromise.as(super.setVisible(visible, group)); } } -class OpenSettingsAction extends Action { - static readonly ID = 'settings.openSettingsJson'; - static readonly LABEL = localize('openSettingsJsonLabel', "Open settings.json for advanced customizations"); - - constructor( - @IPreferencesService private readonly preferencesService: IPreferencesService, - ) { - super(OpenSettingsAction.ID, OpenSettingsAction.LABEL, 'open-settings-json'); - } - - - run(context?: SettingsTarget): TPromise { - return this._run(context) - .then(() => { }); - } - - private _run(context?: SettingsTarget): TPromise { - if (context === ConfigurationTarget.USER) { - return this.preferencesService.openGlobalSettings(); - } else if (context === ConfigurationTarget.WORKSPACE) { - return this.preferencesService.openWorkspaceSettings(); - } else if (URI.isUri(context)) { - return this.preferencesService.openFolderSettings(context); - } - - return TPromise.wrap(null); - } +interface ISettingsToolbarContext { + target: SettingsTarget; } -class ToggleShowModifiedOnlyAction extends Action { - static readonly ID = 'settings.toggleShowModifiedOnly'; - static readonly LABEL = localize('showModifiedOnlyLabel', "Show modified settings only"); - - get checked(): boolean { - return this.viewState.showConfiguredOnly; - } +class FilterByTagAction extends Action { + static readonly ID = 'settings.filterByTag'; constructor( - private settingsEditor: SettingsEditor2, - private viewState: ISettingsEditorViewState + label: string, + private tag: string, + private settingsEditor: SettingsEditor2 ) { - super(ToggleShowModifiedOnlyAction.ID, ToggleShowModifiedOnlyAction.LABEL, 'show-modified-only'); + super(FilterByTagAction.ID, label, 'toggle-filter-tag'); } run(): TPromise { - return this.settingsEditor.toggleShowModifiedOnly(); + 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 ef3cccf1611..5f1c8d11a0e 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts @@ -27,6 +27,7 @@ export const tocData: ITOCEntry = { { id: 'editor', label: localize('textEditor', "Text Editor"), + settings: ['editor.*'], children: [ { id: 'editor/cursor', @@ -45,7 +46,7 @@ export const tocData: ITOCEntry = { }, { id: 'editor/format', - label: localize('format', "Format"), + label: localize('formatting', "Formatting"), settings: ['editor.format*'] }, { @@ -67,17 +68,13 @@ export const tocData: ITOCEntry = { id: 'editor/files', label: localize('files', "Files"), settings: ['files.*'] - }, - { - id: 'editor/editor', - label: localize('textEditor', "Text Editor"), - settings: ['editor.*'] } ] }, { id: 'workbench', label: localize('workbench', "Workbench"), + settings: ['workbench.*'], children: [ { id: 'workbench/appearance', @@ -103,27 +100,18 @@ export const tocData: ITOCEntry = { id: 'workbench/zenmode', label: localize('zenMode', "Zen Mode"), settings: ['zenmode.*'] - }, - { - id: 'workbench/workbench', - label: localize('workbench', "Workbench"), - settings: ['workbench.*'] } ] }, { id: 'window', label: localize('window', "Window"), + settings: ['window.*'], children: [ { id: 'window/newWindow', label: localize('newWindow', "New Window"), settings: ['window.*newwindow*'] - }, - { - id: 'window/window', - label: localize('window', "Window"), - settings: ['window.*'] } ] }, @@ -197,3 +185,16 @@ export const tocData: ITOCEntry = { } ] }; + +export const knownAcronyms = new Set(); +[ + 'css', + 'html', + 'scss', + 'less', + 'json', + 'js', + 'ts', + 'ie', + 'id', +].forEach(str => knownAcronyms.add(str)); diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index a43b504ff51..e1816a4ca4e 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -7,235 +7,61 @@ import * as DOM from 'vs/base/browser/dom'; import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { alert as ariaAlert } from 'vs/base/browser/ui/aria/aria'; import { Button } from 'vs/base/browser/ui/button/button'; 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 { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { Action, IAction } from 'vs/base/common/actions'; 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'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -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, ITree, IRenderer } 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 { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { 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, attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder } 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 { Color } from 'vs/base/common/color'; +import { ISettingsEditorViewState, isExcludeSetting, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, settingKeyToDisplayFormat, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +import { ExcludeSettingWidget, IExcludeDataItem, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectListBorder, settingsSelectForeground, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; +import { 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.")); +function getExcludeDisplayValue(element: SettingsTreeSettingElement): IExcludeDataItem[] { + const data = element.isConfigured ? + { ...element.defaultValue, ...element.scopeValue } : + element.defaultValue; -// 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.")); + return Object.keys(data) + .filter(key => !!data[key]) + .map(key => { + const value = data[key]; + const sibling = typeof value === 'boolean' ? undefined : value.when; -// 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}; }`); - collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more::before { background-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 abstract class SettingsTreeElement { - id: string; - parent: any; // SearchResultModel or group element... TODO search should be more similar to the normal case -} - -export class SettingsTreeGroupElement extends SettingsTreeElement { - children: (SettingsTreeGroupElement | SettingsTreeSettingElement)[]; - label: string; - level: number; - isFirstGroup: boolean; -} - -export class SettingsTreeSettingElement extends SettingsTreeElement { - setting: ISetting; - - displayCategory: string; - displayLabel: string; - value: any; - isConfigured: boolean; - overriddenScopeList: string[]; - description: string; - valueType: 'enum' | 'string' | 'integer' | 'number' | 'boolean' | 'complex'; -} - -export interface ITOCEntry { - id: string; - label: string; - children?: ITOCEntry[]; - settings?: (string | ISetting)[]; -} - -export class SettingsTreeModel { - private _root: SettingsTreeGroupElement; - private _treeElementsById = new Map(); - private _treeElementsBySettingName = new Map(); - - constructor( - private _viewState: ISettingsEditorViewState, - private _tocRoot: ITOCEntry, - @IConfigurationService private _configurationService: IConfigurationService - ) { - this.update(this._tocRoot); - } - - get root(): SettingsTreeGroupElement { - return this._root; - } - - update(newTocRoot = this._tocRoot): void { - const newRoot = this.createSettingsTreeGroupElement(newTocRoot); - (newRoot.children[0]).isFirstGroup = true; - - if (this._root) { - this._root.children = newRoot.children; - } else { - this._root = newRoot; - } - } - - getElementById(id: string): SettingsTreeElement { - return this._treeElementsById.get(id); - } - - getElementByName(name: string): SettingsTreeElement { - return this._treeElementsBySettingName.get(name); - } - - private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { - const element = new SettingsTreeGroupElement(); - element.id = tocEntry.id; - element.label = tocEntry.label; - element.parent = parent; - element.level = this.getDepth(element); - - 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)); - } - - this._treeElementsById.set(element.id, element); - return element; - } - - private getDepth(element: SettingsTreeElement): number { - if (element.parent) { - return 1 + this.getDepth(element.parent); - } else { - return 0; - } - } - - private createSettingsTreeSettingElement(setting: ISetting, parent: SettingsTreeGroupElement): SettingsTreeSettingElement { - const element = createSettingsTreeSettingElement(setting, parent, this._viewState.settingsTarget, this._configurationService); - this._treeElementsById.set(element.id, element); - this._treeElementsBySettingName.set(setting.key, element); - return element; - } -} - -function sanitizeId(id: string): string { - return id.replace(/[\.\/]/, '_'); -} - -function createSettingsTreeSettingElement(setting: ISetting, parent: any, settingsTarget: SettingsTarget, configurationService: IConfigurationService): SettingsTreeSettingElement { - const element = new SettingsTreeSettingElement(); - element.id = sanitizeId(parent.id + '_' + setting.key); - element.parent = parent; - - const { isConfigured, inspected, targetSelector } = inspectSetting(setting.key, settingsTarget, configurationService); - - const displayValue = isConfigured ? inspected[targetSelector] : inspected.default; - const overriddenScopeList = []; - if (targetSelector === 'user' && typeof inspected.workspace !== 'undefined') { - overriddenScopeList.push(localize('workspace', "Workspace")); - } - - if (targetSelector === 'workspace' && typeof inspected.user !== 'undefined') { - overriddenScopeList.push(localize('user', "User")); - } - - const displayKeyFormat = settingKeyToDisplayFormat(setting.key, parent.id); - element.setting = setting; - element.displayLabel = displayKeyFormat.label; - element.displayCategory = displayKeyFormat.category; - - element.value = displayValue; - element.isConfigured = isConfigured; - 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'; - - return element; -} - -function inspectSetting(key: string, target: SettingsTarget, configurationService: IConfigurationService): { isConfigured: boolean, inspected: any, targetSelector: string } { - const inspectOverrides = URI.isUri(target) ? { resource: target } : undefined; - const inspected = configurationService.inspect(key, inspectOverrides); - const targetSelector = target === ConfigurationTarget.USER ? 'user' : - target === ConfigurationTarget.WORKSPACE ? 'workspace' : - 'workspaceFolder'; - const isConfigured = typeof inspected[targetSelector] !== 'undefined'; - - return { isConfigured, inspected, targetSelector }; + return { + id: key, + pattern: key, + sibling + }; + }); } export function resolveSettingsTree(tocData: ITOCEntry, coreSettingsGroups: ISettingsGroup[]): { tree: ITOCEntry, leftoverSettings: Set } { @@ -270,23 +96,28 @@ export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { } function _resolveSettingsTree(tocData: ITOCEntry, allSettings: Set): ITOCEntry { - if (tocData.settings) { - return { - id: tocData.id, - label: tocData.label, - settings: arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern))) - }; - } else if (tocData.children) { - return { - id: tocData.id, - label: tocData.label, - children: tocData.children - .map(child => _resolveSettingsTree(child, allSettings)) - .filter(child => (child.children && child.children.length) || (child.settings && child.settings.length)) - }; + let children: ITOCEntry[]; + if (tocData.children) { + children = tocData.children + .map(child => _resolveSettingsTree(child, allSettings)) + .filter(child => (child.children && child.children.length) || (child.settings && child.settings.length)); } - return null; + let settings: ISetting[]; + if (tocData.settings) { + settings = arrays.flatten(tocData.settings.map(pattern => getMatchingSettings(allSettings, pattern))); + } + + if (!children && !settings) { + return null; + } + + return { + id: tocData.id, + label: tocData.label, + children, + settings + }; } function getMatchingSettings(allSettings: Set, pattern: string): ISetting[] { @@ -303,12 +134,23 @@ function getMatchingSettings(allSettings: Set, pattern: string): ISett return result.sort((a, b) => a.key.localeCompare(b.key)); } -function settingMatches(s: ISetting, pattern: string): boolean { +const settingPatternCache = new Map(); + +function createSettingMatchRegExp(pattern: string): RegExp { pattern = escapeRegExpCharacters(pattern) .replace(/\\\*/g, '.*'); - const regexp = new RegExp(`^${pattern}`, 'i'); - return regexp.test(s.key); + return new RegExp(`^${pattern}`, 'i'); +} + +function settingMatches(s: ISetting, pattern: string): boolean { + let regExp = settingPatternCache.get(pattern); + if (!regExp) { + regExp = createSettingMatchRegExp(pattern); + settingPatternCache.set(pattern, regExp); + } + + return regExp.test(s.key); } function getFlatSettings(settingsGroups: ISettingsGroup[]) { @@ -335,10 +177,6 @@ export class SettingsDataSource implements IDataSource { } hasChildren(tree: ITree, element: SettingsTreeElement): boolean { - if (element instanceof SearchResultModel) { - return true; - } - if (element instanceof SettingsTreeGroupElement) { return true; } @@ -346,14 +184,12 @@ 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)); } private _getChildren(element: SettingsTreeElement): SettingsTreeElement[] { - if (element instanceof SearchResultModel) { - return element.getChildren(); - } else if (element instanceof SettingsTreeGroupElement) { + if (element instanceof SettingsTreeGroupElement) { return element.children; } else { // No children... @@ -361,8 +197,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 { @@ -370,63 +206,56 @@ export class SettingsDataSource implements IDataSource { } } -export function settingKeyToDisplayFormat(key: string, groupId = ''): { category: string, label: string } { - let label = wordifyKey(key); - const lastDotIdx = label.lastIndexOf('.'); - let category = ''; - if (lastDotIdx >= 0) { - category = label.substr(0, lastDotIdx); - label = label.substr(lastDotIdx + 1); +export class SimplePagedDataSource implements IDataSource { + private static readonly SETTINGS_PER_PAGE = 30; + + private loadedToIndex: number; + + constructor(private realDataSource: IDataSource) { + this.loadedToIndex = SimplePagedDataSource.SETTINGS_PER_PAGE * 2; } - groupId = wordifyKey(groupId.replace(/\//g, '.')); - category = trimCategoryForGroup(category, groupId); - - return { category, label }; -} - -function wordifyKey(key: string): string { - return key - .replace(/\.([a-z])/g, (match, p1) => `.${p1.toUpperCase()}`) - .replace(/([a-z])([A-Z])/g, '$1 $2') // fooBar => foo Bar - .replace(/^[a-z]/g, match => match.toUpperCase()); // foo => Foo -} - -function trimCategoryForGroup(category: string, groupId: string): string { - const doTrim = forward => { - const parts = groupId.split('.'); - while (parts.length) { - const reg = new RegExp(`^${parts.join('\\.')}(\\.|$)`, 'i'); - if (reg.test(category)) { - return category.replace(reg, ''); - } - - if (forward) { - parts.pop(); - } else { - parts.shift(); - } + pageTo(index: number): boolean { + if (index > this.loadedToIndex - SimplePagedDataSource.SETTINGS_PER_PAGE) { + this.loadedToIndex = (Math.ceil(index / SimplePagedDataSource.SETTINGS_PER_PAGE) + 1) * SimplePagedDataSource.SETTINGS_PER_PAGE; + return true; + } else { + return false; } - - return null; - }; - - let trimmed = doTrim(true); - if (trimmed === null) { - trimmed = doTrim(false); } - if (trimmed === null) { - trimmed = category; + getId(tree: ITree, element: any): string { + return this.realDataSource.getId(tree, element); } - return trimmed; -} + hasChildren(tree: ITree, element: any): boolean { + return this.realDataSource.hasChildren(tree, element); + } -export interface ISettingsEditorViewState { - settingsTarget: SettingsTarget; - showConfiguredOnly?: boolean; - filterToCategory?: SettingsTreeGroupElement; + getChildren(tree: ITree, element: SettingsTreeGroupElement): TPromise { + return this.realDataSource.getChildren(tree, element).then(realChildren => { + return this._getChildren(realChildren); + }); + } + + _getChildren(realChildren: SettingsTreeElement[]): any[] { + const lastChild = realChildren[realChildren.length - 1]; + if (lastChild && lastChild.index > this.loadedToIndex) { + return realChildren.filter(child => { + return child.index < this.loadedToIndex; + }); + } else { + return realChildren; + } + } + + getParent(tree: ITree, element: any): TPromise { + return this.realDataSource.getParent(tree, element); + } + + shouldAutoexpand(tree: ITree, element: any): boolean { + return this.realDataSource.shouldAutoexpand(tree, element); + } } interface IDisposableTemplate { @@ -436,13 +265,15 @@ interface IDisposableTemplate { interface ISettingItemTemplate extends IDisposableTemplate { onChange?: (value: T) => void; + context?: SettingsTreeSettingElement; containerElement: HTMLElement; categoryElement: HTMLElement; labelElement: HTMLElement; descriptionElement: HTMLElement; controlElement: HTMLElement; - isConfiguredElement: HTMLElement; + deprecationWarningElement: HTMLElement; otherOverridesElement: HTMLElement; + toolbar: ToolBar; } interface ISettingBoolItemTemplate extends ISettingItemTemplate { @@ -451,18 +282,29 @@ interface ISettingBoolItemTemplate extends ISettingItemTemplate { interface ISettingTextItemTemplate extends ISettingItemTemplate { inputBox: InputBox; + validationErrorMessageElement: HTMLElement; } 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; +} + interface IGroupTitleTemplate extends IDisposableTemplate { context?: SettingsTreeGroupElement; parent: HTMLElement; @@ -472,7 +314,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 { @@ -480,33 +324,95 @@ 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; + public static readonly CONTROL_CLASS = 'setting-control-focus-target'; + public static readonly CONTROL_SELECTOR = '.' + SettingsRenderer.CONTROL_CLASS; + + public static readonly SETTING_KEY_ATTR = 'data-key'; + 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 measureContainer: HTMLElement; + private readonly _onDidFocusSetting: Emitter = new Emitter(); + public readonly onDidFocusSetting: Event = this._onDidFocusSetting.event; + + private descriptionMeasureContainer: HTMLElement; + private longestSingleLineDescription = 0; + + private rowHeightCache = new Map(); + private lastRenderedWidth: number; + + private settingActions: IAction[]; 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, + @IContextMenuService private contextMenuService: IContextMenuService ) { - this.measureContainer = DOM.append(_measureContainer, $('.setting-measure-container.monaco-tree-row')); + this.descriptionMeasureContainer = $('.setting-item-description'); + DOM.append(_measureContainer, + $('.setting-measure-container.monaco-tree-row', undefined, + $('.setting-item', undefined, + this.descriptionMeasureContainer))); + + this.settingActions = [ + new Action('settings.resetSetting', localize('resetSettingLabel', "Reset Setting"), undefined, undefined, (context: SettingsTreeSettingElement) => { + if (context) { + this._onDidChangeSetting.fire({ key: context.setting.key, value: undefined }); + } + + return TPromise.wrap(null); + }), + new Separator(), + this.instantiationService.createInstance(CopySettingIdAction), + this.instantiationService.createInstance(CopySettingAsJSONAction), + ]; + } + + showContextMenu(element: SettingsTreeSettingElement, settingDOMElement: HTMLElement): void { + const toolbarElement: HTMLElement = settingDOMElement.querySelector('.toolbar-toggle-more'); + if (toolbarElement) { + this.contextMenuService.showContextMenu({ + getActions: () => TPromise.wrap(this.settingActions), + getAnchor: () => toolbarElement, + getActionsContext: () => element + }); + } + } + + updateWidth(width: number): void { + if (this.lastRenderedWidth !== width) { + this.rowHeightCache = new Map(); + } + this.longestSingleLineDescription = 0; + + this.lastRenderedWidth = width; } getHeight(tree: ITree, element: SettingsTreeElement): number { + if (this.rowHeightCache.has(element.id) && !(element instanceof SettingsTreeSettingElement && isExcludeSetting(element.setting))) { + 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 31; @@ -516,39 +422,64 @@ export class SettingsRenderer implements IRenderer { } 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 + 66 + this.measureSettingDescription(element); } private measureSettingElementHeight(tree: ITree, element: SettingsTreeSettingElement): number { - const measureHelper = DOM.append(this.measureContainer, $('.setting-measure-helper')); + let heightExcludingDescription = 86; - const templateId = this.getTemplateId(tree, element); - const template = this.renderTemplate(tree, templateId, measureHelper); - this.renderElement(tree, element, templateId, template); + if (element.valueType === 'boolean') { + heightExcludingDescription = 60; + } - const height = this.measureContainer.offsetHeight; - this.measureContainer.removeChild(this.measureContainer.firstChild); - return Math.max(height, this._getUnexpandedSettingHeight(element)); + return heightExcludingDescription + this.measureSettingDescription(element); + } + + private measureSettingDescription(element: SettingsTreeSettingElement): number { + if (element.description.length < this.longestSingleLineDescription * .8) { + // Most setting descriptions are one short line, so try to avoid measuring them. + // If the description is less than 80% of the longest single line description, assume this will also render to be one line. + return 18; + } + + const boolMeasureClass = 'measure-bool-description'; + if (element.valueType === 'boolean') { + this.descriptionMeasureContainer.classList.add(boolMeasureClass); + } else if (this.descriptionMeasureContainer.classList.contains(boolMeasureClass)) { + this.descriptionMeasureContainer.classList.remove(boolMeasureClass); + } + + // Remove markdown links and setting links + const measureText = element.description + .replace(/\[(.*)\]\(.*\)/g, '$1') + .replace(/`#(.*)#`/g, '$1'); + + this.descriptionMeasureContainer.innerText = measureText; + const h = this.descriptionMeasureContainer.offsetHeight; + if (h < 20 && measureText.length > this.longestSingleLineDescription) { + this.longestSingleLineDescription = measureText.length; + } + + return h; } getTemplateId(tree: ITree, element: SettingsTreeElement): string { - if (element instanceof SettingsTreeGroupElement) { return SETTINGS_GROUP_ELEMENT_TEMPLATE_ID; } @@ -558,7 +489,7 @@ export class SettingsRenderer implements IRenderer { return SETTINGS_BOOL_TEMPLATE_ID; } - if (element.valueType === 'integer' || element.valueType === 'number') { + if (element.valueType === 'integer' || element.valueType === 'number' || element.valueType === 'nullable-integer' || element.valueType === 'nullable-number') { return SETTINGS_NUMBER_TEMPLATE_ID; } @@ -570,9 +501,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 ''; } @@ -597,10 +536,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; } @@ -619,18 +566,24 @@ 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')); - const isConfiguredElement = DOM.append(titleElement, $('span.setting-item-is-configured-label')); + const labelCategoryContainer = DOM.append(titleElement, $('.setting-item-cat-label-container')); + const categoryElement = DOM.append(labelCategoryContainer, $('span.setting-item-category')); + const labelElement = DOM.append(labelCategoryContainer, $('span.setting-item-label')); const otherOverridesElement = DOM.append(titleElement, $('span.setting-item-overrides')); const descriptionElement = DOM.append(container, $('.setting-item-description')); + const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); + modifiedIndicatorElement.title = localize('modified', "Modified"); const valueElement = DOM.append(container, $('.setting-item-value')); const controlElement = DOM.append(valueElement, $('div.setting-item-control')); + const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); + const toDispose = []; + + const toolbar = this.renderSettingToolbar(container); + const template: ISettingItemTemplate = { toDispose, @@ -639,8 +592,9 @@ export class SettingsRenderer implements IRenderer { labelElement, descriptionElement, controlElement, - isConfiguredElement, - otherOverridesElement + deprecationWarningElement, + otherOverridesElement, + toolbar }; // Prevent clicks from being handled by list @@ -656,8 +610,27 @@ export class SettingsRenderer implements IRenderer { return template; } + private addSettingElementFocusHandler(template: ISettingItemTemplate): void { + const focusTracker = DOM.trackFocus(template.containerElement); + template.toDispose.push(focusTracker); + focusTracker.onDidBlur(() => { + if (template.containerElement.classList.contains('focused')) { + template.containerElement.classList.remove('focused'); + } + }); + + focusTracker.onDidFocus(() => { + template.containerElement.classList.add('focused'); + + if (template.context) { + this._onDidFocusSetting.fire(template.context); + } + }); + } + private renderSettingTextTemplate(tree: ITree, container: HTMLElement, type = 'text'): ISettingTextItemTemplate { const common = this.renderCommonTemplate(tree, container, 'text'); + const validationErrorMessageElement = DOM.append(container, $('.setting-item-validation-message')); const inputBox = new InputBox(common.controlElement, this.contextViewService); common.toDispose.push(inputBox); @@ -673,19 +646,24 @@ export class SettingsRenderer implements IRenderer { } })); common.toDispose.push(inputBox); + inputBox.inputElement.classList.add(SettingsRenderer.CONTROL_CLASS); const template: ISettingTextItemTemplate = { ...common, - inputBox + inputBox, + validationErrorMessageElement }; + this.addSettingElementFocusHandler(template); + return template; } private renderSettingNumberTemplate(tree: ITree, container: HTMLElement): ISettingNumberItemTemplate { const common = this.renderCommonTemplate(tree, container, 'number'); + const validationErrorMessageElement = DOM.append(container, $('.setting-item-validation-message')); - const inputBox = new InputBox(common.controlElement, this.contextViewService); + const inputBox = new InputBox(common.controlElement, this.contextViewService, { type: 'number' }); common.toDispose.push(inputBox); common.toDispose.push(attachInputBoxStyler(inputBox, this.themeService, { inputBackground: settingsNumberInputBackground, @@ -699,15 +677,30 @@ export class SettingsRenderer implements IRenderer { } })); common.toDispose.push(inputBox); + inputBox.inputElement.classList.add(SettingsRenderer.CONTROL_CLASS); const template: ISettingNumberItemTemplate = { ...common, - inputBox + inputBox, + validationErrorMessageElement }; + this.addSettingElementFocusHandler(template); + return template; } + private renderSettingToolbar(container: HTMLElement): ToolBar { + const toolbar = new ToolBar(container, this.contextMenuService, {}); + toolbar.setActions([], this.settingActions)(); + const button = container.querySelector('.toolbar-toggle-more'); + if (button) { + (button).tabIndex = -1; + } + + return toolbar; + } + private renderSettingBoolTemplate(tree: ITree, container: HTMLElement): ISettingBoolItemTemplate { DOM.addClass(container, 'setting-item'); DOM.addClass(container, 'setting-item-bool'); @@ -715,12 +708,16 @@ export class SettingsRenderer implements IRenderer { 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')); - const isConfiguredElement = DOM.append(titleElement, $('span.setting-item-is-configured-label')); const otherOverridesElement = DOM.append(titleElement, $('span.setting-item-overrides')); const descriptionAndValueElement = DOM.append(container, $('.setting-item-value-description')); const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control')); const descriptionElement = DOM.append(descriptionAndValueElement, $('.setting-item-description')); + const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); + modifiedIndicatorElement.title = localize('modified', "Modified"); + + + const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); const toDispose = []; const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: null }); @@ -732,6 +729,25 @@ export class SettingsRenderer implements IRenderer { } })); + // Need to listen for mouse clicks on description and toggle checkbox - use target ID for safety + // Also have to ignore embedded links - too buried to stop propagation + toDispose.push(DOM.addDisposableListener(descriptionElement, DOM.EventType.MOUSE_DOWN, (e) => { + const targetElement = e.toElement; + const targetId = descriptionElement.getAttribute('checkbox-label-target-id'); + + // Make sure we are not a link and the target ID matches + // Toggle target checkbox + if (targetElement.tagName.toLowerCase() !== 'a' && targetId === template.checkbox.domNode.id) { + template.checkbox.checked = template.checkbox.checked ? false : true; + template.onChange(checkbox.checked); + } + DOM.EventHelper.stop(e); + })); + + checkbox.domNode.classList.add(SettingsRenderer.CONTROL_CLASS); + const toolbar = this.renderSettingToolbar(container); + toDispose.push(toolbar); + const template: ISettingBoolItemTemplate = { toDispose, @@ -741,10 +757,13 @@ export class SettingsRenderer implements IRenderer { controlElement, checkbox, descriptionElement, - isConfiguredElement, - otherOverridesElement + deprecationWarningElement, + otherOverridesElement, + toolbar }; + this.addSettingElementFocusHandler(template); + // Prevent clicks from being handled by list toDispose.push(DOM.addDisposableListener(controlElement, 'mousedown', (e: IMouseEvent) => e.stopPropagation())); @@ -758,17 +777,38 @@ export class SettingsRenderer implements IRenderer { return template; } + public cancelSuggesters() { + this.contextViewService.hideContextView(); + } + private renderSettingEnumTemplate(tree: ITree, container: HTMLElement): ISettingEnumItemTemplate { const common = this.renderCommonTemplate(tree, container, 'enum'); - const selectBox = new SelectBox([], undefined, this.contextViewService); + const selectBox = new SelectBox([], undefined, this.contextViewService, undefined, { + hasDetails: true, markdownActionHandler: { + callback: (content: string) => { + if (startsWith(content, '#')) { + this._onDidClickSettingLink.fire(content.substr(1)); + } else { + this.openerService.open(URI.parse(content)).then(void 0, onUnexpectedError); + } + }, + disposeables: common.toDispose + } + }); + common.toDispose.push(selectBox); common.toDispose.push(attachSelectBoxStyler(selectBox, this.themeService, { selectBackground: settingsSelectBackground, selectForeground: settingsSelectForeground, - selectBorder: settingsSelectBorder + selectBorder: settingsSelectBorder, + selectListBorder: settingsSelectListBorder })); selectBox.render(common.controlElement); + const selectElement = common.controlElement.querySelector('select'); + if (selectElement) { + selectElement.classList.add(SettingsRenderer.CONTROL_CLASS); + } common.toDispose.push( selectBox.onDidSelect(e => { @@ -777,11 +817,76 @@ 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) { + let newValue = { ...template.context.scopeValue }; + + // first delete the existing entry, if present + if (e.originalPattern) { + if (e.originalPattern in template.context.defaultValue) { + // delete a default by overriding it + newValue[e.originalPattern] = false; + } else { + delete newValue[e.originalPattern]; + } + } + + // then add the new or updated entry, if present + if (e.pattern) { + if (e.pattern in template.context.defaultValue && !e.sibling) { + // add a default by deleting its override + delete newValue[e.pattern]; + } else { + newValue[e.pattern] = e.sibling ? { when: e.sibling } : true; + } + } + + const sortKeys = (obj) => { + const keyArray = Object.keys(obj) + .map(key => ({ key, val: obj[key] })) + .sort((a, b) => a.key.localeCompare(b.key)); + + const retVal = {}; + keyArray.forEach(pair => { + retVal[pair.key] = pair.val; + }); + return retVal; + }; + + this._onDidChangeSetting.fire({ + key: template.context.setting.key, + value: Object.keys(newValue).length === 0 ? undefined : sortKeys(newValue) + }); + } + })); + return template; } @@ -790,7 +895,7 @@ export class SettingsRenderer implements IRenderer { const openSettingsButton = new Button(common.controlElement, { title: true, buttonBackground: null, buttonHoverBackground: null }); common.toDispose.push(openSettingsButton); - common.toDispose.push(openSettingsButton.onDidClick(() => this._onDidOpenSettings.fire())); + common.toDispose.push(openSettingsButton.onDidClick(() => template.onChange(null))); openSettingsButton.label = localize('editInSettingsJson', "Edit in settings.json"); openSettingsButton.element.classList.add('edit-in-settings-button'); @@ -805,6 +910,34 @@ export class SettingsRenderer implements IRenderer { 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; } @@ -813,6 +946,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); } @@ -827,63 +964,59 @@ 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; + } + + public getSettingDOMElementForDOMElement(domElement: HTMLElement): HTMLElement { + const parent = DOM.findParentWithClass(domElement, 'setting-item'); + if (parent) { + return parent; + } + + return null; + } + + public getDOMElementsForSettingKey(treeContainer: HTMLElement, key: string): NodeListOf { + return treeContainer.querySelectorAll(`[${SettingsRenderer.SETTING_KEY_ATTR}="${key}"]`); + } + + public getKeyForDOMElementInSetting(element: HTMLElement): string { + const settingElement = this.getSettingDOMElementForDOMElement(element); + return settingElement && settingElement.getAttribute(SettingsRenderer.SETTING_KEY_ATTR); } private renderSettingElement(tree: ITree, element: SettingsTreeSettingElement, templateId: string, template: ISettingItemTemplate | ISettingBoolItemTemplate): void { - const isSelected = !!this.elementIsSelected(tree, element); + template.context = element; + template.toolbar.context = element; + const setting = element.setting; DOM.toggleClass(template.containerElement, 'is-configured', element.isConfigured); - DOM.toggleClass(template.containerElement, 'is-expanded', isSelected); - template.containerElement.id = element.id.replace(/\./g, '_'); + DOM.toggleClass(template.containerElement, 'is-expanded', true); + template.containerElement.setAttribute(SettingsRenderer.SETTING_KEY_ATTR, element.setting.key); - const titleTooltip = setting.key; + const titleTooltip = setting.key + (element.isConfigured ? ' - Modified' : ''); template.categoryElement.textContent = element.displayCategory && (element.displayCategory + ': '); template.categoryElement.title = titleTooltip; template.labelElement.textContent = element.displayLabel; template.labelElement.title = titleTooltip; - let enumDescriptionText = ''; - if (element.valueType === 'string' && element.setting.enumDescriptions && element.setting.enum && element.setting.enum.length < SettingsRenderer.MAX_ENUM_DESCRIPTIONS) { - enumDescriptionText = '\n' + element.setting.enumDescriptions - .map((desc, i) => desc ? - ` - \`${element.setting.enum[i]}\` : - ${desc}` : ` - \`${element.setting.enum[i]}\``) - .filter(desc => !!desc) - .join('\n'); + this.renderValue(element, templateId, template); + template.descriptionElement.innerHTML = ''; + if (element.setting.descriptionIsMarkdown) { + const renderedDescription = this.renderDescriptionMarkdown(element.description, template.toDispose); + template.descriptionElement.appendChild(renderedDescription); + } else { + template.descriptionElement.innerText = element.description; } - // Rewrite `#editor.fontSize#` to link format - const descriptionText = (element.description + enumDescriptionText) - .replace(/`#(.*)#`/g, (match, settingName) => `[\`${settingName}\`](#${settingName})`); - - 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'); - template.descriptionElement.innerHTML = ''; - template.descriptionElement.appendChild(renderedDescription); - (renderedDescription.querySelectorAll('a')).forEach(aElement => { - aElement.tabIndex = isSelected ? 0 : -1; - }); - - this.renderValue(element, isSelected, templateId, template); - - template.isConfiguredElement.textContent = element.isConfigured ? localize('configured', "Modified") : ''; + if (templateId === SETTINGS_BOOL_TEMPLATE_ID) { + // Add checkbox target to description clickable and able to toggle checkbox + const checkbox_id = (element.displayCategory + '_' + element.displayLabel).replace(/ /g, '_') + '_Item'; + template.descriptionElement.setAttribute('checkbox-label-target-id', checkbox_id); + } if (element.overriddenScopeList.length) { let otherOverridesLabel = element.isConfigured ? @@ -896,35 +1029,86 @@ 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 }); + template.deprecationWarningElement.innerText = element.setting.deprecationMessage || ''; 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 modifiedText = dataElement.isConfigured ? 'Modified' : ''; + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' checkbox ' + (dataElement.value ? 'checked ' : 'unchecked ') + modifiedText; + + // 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 { + private renderEnum(dataElement: SettingsTreeSettingElement, template: ISettingEnumItemTemplate, onChange: (value: string) => void): void { const displayOptions = getDisplayEnumOptions(dataElement.setting); template.selectBox.setOptions(displayOptions); + const descriptions = dataElement.setting.enumDescriptions; + const descriptionsAreMarkdown = dataElement.setting.descriptionIsMarkdown; + template.selectBox.setDetailsProvider(index => + ({ + details: descriptions && descriptions[index] && (descriptionsAreMarkdown ? fixSettingLinks(descriptions[index]) : descriptions[index]), + isMarkdown: descriptionsAreMarkdown + })); + + const modifiedText = dataElement.isConfigured ? 'Modified' : ''; + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' combobox ' + modifiedText; - const label = dataElement.displayCategory + ' ' + dataElement.displayLabel; template.selectBox.setAriaLabel(label); const idx = dataElement.setting.enum.indexOf(dataElement.value); @@ -933,31 +1117,80 @@ export class SettingsRenderer implements IRenderer { 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 = ''; } - private renderText(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingTextItemTemplate, onChange: (value: string) => void): void { + private renderText(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: string) => void): void { + const modifiedText = dataElement.isConfigured ? 'Modified' : ''; + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' ' + modifiedText; template.onChange = null; template.inputBox.value = dataElement.value; - template.onChange = value => onChange(value); - template.inputBox.inputElement.tabIndex = isSelected ? 0 : -1; + template.onChange = value => { renderValidations(dataElement, template, false, label); onChange(value); }; + + // 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, '_'); + + // 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); + + renderValidations(dataElement, template, true, label); } - private renderNumber(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingTextItemTemplate, onChange: (value: number) => void): void { + + private renderNumber(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: number) => void): void { + const modifiedText = dataElement.isConfigured ? 'Modified' : ''; + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' number ' + modifiedText; + const numParseFn = (dataElement.valueType === 'integer' || dataElement.valueType === 'nullable-integer') + ? parseInt : parseFloat; + + const nullNumParseFn = (dataElement.valueType === 'nullable-integer' || dataElement.valueType === 'nullable-number') + ? (v => v === '' ? null : numParseFn(v)) : numParseFn; + template.onChange = null; template.inputBox.value = dataElement.value; - template.onChange = value => onChange(parseFn(value)); - template.inputBox.inputElement.tabIndex = isSelected ? 0 : -1; + template.onChange = value => { renderValidations(dataElement, template, false, label); onChange(nullNumParseFn(value)); }; - 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, '_'); + + // 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); + + renderValidations(dataElement, template, true, label); } - 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 { @@ -965,6 +1198,44 @@ export class SettingsRenderer implements IRenderer { } } +function renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, calledOnStartup: boolean, originalAriaLabel: string) { + if (dataElement.setting.validator) { + let errMsg = dataElement.setting.validator(template.inputBox.value); + if (errMsg) { + DOM.addClass(template.containerElement, 'invalid-input'); + template.validationErrorMessageElement.innerText = errMsg; + let validationError = localize('validationError', "Validation Error."); + template.inputBox.inputElement.parentElement.setAttribute('aria-label', [originalAriaLabel, validationError, errMsg].join(' ')); + if (!calledOnStartup) { ariaAlert(validationError + ' ' + errMsg); } + return; + } else { + template.inputBox.inputElement.parentElement.setAttribute('aria-label', originalAriaLabel); + } + } + DOM.removeClass(template.containerElement, 'invalid-input'); +} + +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) => { + const targetDisplayFormat = settingKeyToDisplayFormat(settingName); + const targetName = `${targetDisplayFormat.category}: ${targetDisplayFormat.label}`; + return `[${targetName}](#${settingName})`; + }); +} + function getDisplayEnumOptions(setting: ISetting): string[] { if (setting.enum.length > SettingsRenderer.MAX_ENUM_DESCRIPTIONS && setting.enumDescriptions) { return setting.enum @@ -976,7 +1247,9 @@ function getDisplayEnumOptions(setting: ISetting): string[] { }); } - return setting.enum.map(escapeInvisibleChars); + return setting.enum + .map(String) + .map(escapeInvisibleChars); } function escapeInvisibleChars(enumValue: string): string { @@ -991,18 +1264,29 @@ export class SettingsTreeFilter implements IFilter { ) { } isVisible(tree: ITree, element: SettingsTreeElement): boolean { + // Filter during search if (this.viewState.filterToCategory && element instanceof SettingsTreeSettingElement) { if (!this.settingContainedInGroup(element.setting, this.viewState.filterToCategory)) { return false; } } - if (element instanceof SettingsTreeSettingElement && this.viewState.showConfiguredOnly) { - return element.isConfigured; + if (element instanceof SettingsTreeSettingElement && this.viewState.tagFilters) { + return element.matchesAllTags(this.viewState.tagFilters); } - if (element instanceof SettingsTreeGroupElement && this.viewState.showConfiguredOnly) { - return this.groupHasConfiguredSetting(element); + if (element instanceof SettingsTreeGroupElement) { + if (typeof element.count === 'number') { + return element.count > 0; + } + + return element.children.some(child => this.isVisible(tree, child)); + } + + if (element instanceof SettingsTreeNewExtensionsElement) { + if ((this.viewState.tagFilters && this.viewState.tagFilters.size) || this.viewState.filterToCategory) { + return false; + } } return true; @@ -1019,22 +1303,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 { @@ -1048,15 +1316,11 @@ export class SettingsTreeController extends WorkbenchTreeController { const isLink = eventish.target.tagName.toLowerCase() === 'a' || eventish.target.parentElement.tagName.toLowerCase() === 'a'; // inside - if (isLink && DOM.findParentWithClass(eventish.target, 'setting-item-description-markdown', tree.getHTMLElement())) { + if (isLink && (DOM.findParentWithClass(eventish.target, 'setting-item-description-markdown', tree.getHTMLElement()) || DOM.findParentWithClass(eventish.target, 'select-box-description-markdown'))) { return true; } - // Without this, clicking on the setting description causes the tree to lose focus. I don't know why. - // The superclass does not always call it because of DND which is not used here. - eventish.preventDefault(); - - return super.onLeftClick(tree, element, eventish, origin); + return false; } } @@ -1078,91 +1342,206 @@ export class SettingsAccessibilityProvider implements IAccessibilityProvider { } } -export enum SearchResultIdx { - Local = 0, - Remote = 1 +class NonExpandableOrSelectableTree extends Tree { + expand(): TPromise { + return TPromise.wrap(null); + } + + 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; + } + + public focusParent(eventPayload?: any): void { + return; + } + + public focusFirstChild(eventPayload?: any): void { + return; + } + + public focusFirst(eventPayload?: any, from?: any): void { + return; + } + + public focusNth(index: number, eventPayload?: any): void { + return; + } + + public focusLast(eventPayload?: any, from?: any): void { + return; + } + + public focusNextPage(eventPayload?: any): void { + return; + } + + public focusPreviousPage(eventPayload?: any): void { + return; + } + + public select(element: any, eventPayload?: any): void { + return; + } + + public selectRange(fromElement: any, toElement: any, eventPayload?: any): void { + return; + } + + public selectAll(elements: any[], eventPayload?: any): void { + return; + } + + public setSelection(elements: any[], eventPayload?: any): void { + return; + } + + public toggleSelection(element: any, eventPayload?: any): void { + return; + } } -export class SearchResultModel { - private rawSearchResults: ISearchResult[]; - private cachedUniqueSearchResults: ISearchResult[]; - private children: SettingsTreeSettingElement[]; - - readonly id = 'searchResultModel'; +export class SettingsTree extends NonExpandableOrSelectableTree { + protected disposables: IDisposable[]; constructor( - private _viewState: ISettingsEditorViewState, - @IConfigurationService private _configurationService: IConfigurationService - ) { } + container: HTMLElement, + viewState: ISettingsEditorViewState, + configuration: Partial, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService + ) { + const treeClass = 'settings-editor-tree'; - getChildren(): SettingsTreeSettingElement[] { - return this.children; - } + const controller = instantiationService.createInstance(SettingsTreeController); + const fullConfiguration = { + controller, + accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), + filter: instantiationService.createInstance(SettingsTreeFilter, viewState), + styler: new DefaultTreestyler(DOM.createStyleSheet(container), treeClass), - getUniqueResults(): ISearchResult[] { - if (this.cachedUniqueSearchResults) { - return this.cachedUniqueSearchResults; - } + ...configuration + }; - if (!this.rawSearchResults) { - return []; - } + const options = { + ariaLabel: localize('treeAriaLabel', "Settings"), + showLoading: false, + indentPixels: 0, + twistiePixels: 20, // Actually for gear button + }; - const localMatchKeys = new Set(); - const localResult = objects.deepClone(this.rawSearchResults[SearchResultIdx.Local]); - if (localResult) { - localResult.filterMatches.forEach(m => localMatchKeys.add(m.setting.key)); - } + super(container, + fullConfiguration, + options); - const remoteResult = objects.deepClone(this.rawSearchResults[SearchResultIdx.Remote]); - if (remoteResult) { - remoteResult.filterMatches = remoteResult.filterMatches.filter(m => !localMatchKeys.has(m.setting.key)); - } + this.disposables = []; + this.disposables.push(controller); - this.cachedUniqueSearchResults = [localResult, remoteResult]; - return this.cachedUniqueSearchResults; - } + 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; }`); + } - getRawResults(): ISearchResult[] { - return this.rawSearchResults; - } + 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}; }`); + } - setResult(type: SearchResultIdx, result: ISearchResult): void { - this.cachedUniqueSearchResults = null; - this.rawSearchResults = this.rawSearchResults || []; - if (!result) { - delete this.rawSearchResults[type]; - return; - } + const errorColor = theme.getColor(errorForeground); + if (errorColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { color: ${errorColor}; }`); + } - this.rawSearchResults[type] = result; - this.updateChildren(); - } + const invalidInputBackground = theme.getColor(inputValidationErrorBackground); + if (invalidInputBackground) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { background-color: ${invalidInputBackground}; }`); + } - updateChildren(): void { - this.children = this.getFlatSettings() - .map(s => createSettingsTreeSettingElement(s, this, this._viewState.settingsTarget, this._configurationService)); - } + const invalidInputBorder = theme.getColor(inputValidationErrorBorder); + if (invalidInputBorder) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.invalid-input .setting-item-control .monaco-inputbox.idle { outline-width: 0; border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); + } - private getFlatSettings(): ISetting[] { - const flatSettings: ISetting[] = []; - this.getUniqueResults() - .filter(r => !!r) - .forEach(r => { - flatSettings.push( - ...r.filterMatches.map(m => m.setting)); - }); + const headerForegroundColor = theme.getColor(settingsHeaderForeground); + if (headerForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label { color: ${headerForegroundColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-label { color: ${headerForegroundColor}; }`); + } + })); - return flatSettings; + 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, + listHoverOutline: editorBackground, + listFocusOutline: editorBackground, + listInactiveSelectionBackground: editorBackground, + listInactiveSelectionForeground: foreground + }, colors => { + this.style(colors); + })); } } -export class NonExpandableTree extends WorkbenchTree { - expand(): TPromise { - return TPromise.wrap(null); +class CopySettingIdAction extends Action { + static readonly ID = 'settings.copySettingId'; + static readonly LABEL = localize('copySettingIdLabel', "Copy Setting ID"); + + constructor( + @IClipboardService private clipboardService: IClipboardService + ) { + super(CopySettingIdAction.ID, CopySettingIdAction.LABEL); } - collapse(): TPromise { - return TPromise.wrap(null); + run(context: SettingsTreeSettingElement): TPromise { + if (context) { + this.clipboardService.writeText(context.setting.key); + } + + return TPromise.as(null); } } + +class CopySettingAsJSONAction extends Action { + static readonly ID = 'settings.copySettingAsJSON'; + static readonly LABEL = localize('copySettingAsJSONLabel', "Copy Setting as JSON"); + + constructor( + @IClipboardService private clipboardService: IClipboardService + ) { + super(CopySettingAsJSONAction.ID, CopySettingAsJSONAction.LABEL); + } + + run(context: SettingsTreeSettingElement): TPromise { + if (context) { + const jsonResult = `"${context.setting.key}": ${JSON.stringify(context.value, undefined, ' ')}`; + this.clipboardService.writeText(jsonResult); + } + + return TPromise.as(null); + } +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts new file mode 100644 index 00000000000..9626898d470 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts @@ -0,0 +1,486 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as arrays from 'vs/base/common/arrays'; +import * as objects from 'vs/base/common/objects'; +import URI from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { ITOCEntry, knownAcronyms } from 'vs/workbench/parts/preferences/browser/settingsLayout'; +import { IExtensionSetting, ISearchResult, ISetting } from 'vs/workbench/services/preferences/common/preferences'; +import { isArray } from 'vs/base/common/types'; + +export const MODIFIED_SETTING_TAG = 'modified'; +export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices'; + +export interface ISettingsEditorViewState { + settingsTarget: SettingsTarget; + tagFilters?: Set; + filterToCategory?: SettingsTreeGroupElement; +} + +export abstract class SettingsTreeElement { + id: string; + parent: SettingsTreeGroupElement; + + /** + * Index assigned in display order, used for paging. + */ + index: number; +} + +export class SettingsTreeGroupElement extends SettingsTreeElement { + children: (SettingsTreeGroupElement | SettingsTreeSettingElement | SettingsTreeNewExtensionsElement)[]; + count?: number; + label: string; + level: number; + isFirstGroup: boolean; +} + +export class SettingsTreeNewExtensionsElement extends SettingsTreeElement { + extensionIds: string[]; +} + +export class SettingsTreeSettingElement extends SettingsTreeElement { + setting: ISetting; + + private _displayCategory: string; + private _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' | 'exclude' | 'complex' | 'nullable-integer' | 'nullable-number'; + + constructor(setting: ISetting, parent: SettingsTreeGroupElement, index: number, inspectResult: IInspectResult) { + super(); + this.index = index; + this.setting = setting; + this.parent = parent; + this.id = sanitizeId(parent.id + '_' + setting.key); + + this.update(inspectResult); + } + + get displayCategory(): string { + if (!this._displayCategory) { + this.initLabel(); + } + + return this._displayCategory; + } + + get displayLabel(): string { + if (!this._displayLabel) { + this.initLabel(); + } + + return this._displayLabel; + } + + private initLabel(): void { + const displayKeyFormat = settingKeyToDisplayFormat(this.setting.key, this.parent.id); + this._displayLabel = displayKeyFormat.label; + this._displayCategory = displayKeyFormat.category; + } + + update(inspectResult: IInspectResult): void { + const { isConfigured, inspected, targetSelector } = inspectResult; + + const displayValue = isConfigured ? inspected[targetSelector] : inspected.default; + const overriddenScopeList = []; + if (targetSelector === 'user' && typeof inspected.workspace !== 'undefined') { + overriddenScopeList.push(localize('workspace', "Workspace")); + } + + if (targetSelector === 'workspace' && typeof inspected.user !== 'undefined') { + overriddenScopeList.push(localize('user', "User")); + } + + this.value = displayValue; + this.scopeValue = isConfigured && inspected[targetSelector]; + this.defaultValue = inspected.default; + + this.isConfigured = isConfigured; + if (isConfigured || this.setting.tags) { + this.tags = new Set(); + if (isConfigured) { + this.tags.add(MODIFIED_SETTING_TAG); + } + + if (this.setting.tags) { + this.setting.tags.forEach(tag => this.tags.add(tag)); + } + } + + this.overriddenScopeList = overriddenScopeList; + this.description = this.setting.description.join('\n'); + + if (this.setting.enum && (!this.setting.type || settingTypeEnumRenderable(this.setting.type))) { + this.valueType = 'enum'; + } else if (this.setting.type === 'string') { + this.valueType = 'string'; + } else if (isExcludeSetting(this.setting)) { + this.valueType = 'exclude'; + } else if (this.setting.type === 'integer') { + this.valueType = 'integer'; + } else if (this.setting.type === 'number') { + this.valueType = 'number'; + } else if (this.setting.type === 'boolean') { + this.valueType = 'boolean'; + } else if (isArray(this.setting.type) && this.setting.type.indexOf('null') > -1 && this.setting.type.length === 2) { + if (this.setting.type.indexOf('integer') > -1) { + this.valueType = 'nullable-integer'; + } else if (this.setting.type.indexOf('number') > -1) { + this.valueType = 'nullable-number'; + } else { + this.valueType = 'complex'; + } + } else { + this.valueType = 'complex'; + } + } + + matchesAllTags(tagFilters?: Set): boolean { + if (!tagFilters || !tagFilters.size) { + return true; + } + + if (this.tags) { + let hasFilteredTag = true; + tagFilters.forEach(tag => { + hasFilteredTag = hasFilteredTag && this.tags.has(tag); + }); + return hasFilteredTag; + } else { + return false; + } + } +} + +export function countSettingGroupChildrenWithPredicate(tree: SettingsTreeGroupElement, predicate: (setting: SettingsTreeSettingElement) => boolean): number { + const recursiveCounter: (root: SettingsTreeGroupElement | SettingsTreeSettingElement | SettingsTreeNewExtensionsElement) => number = + root => { + if (root instanceof SettingsTreeGroupElement) { + return root.children.map(child => recursiveCounter(child)).reduce((a, b) => a + b, 0); + } else if (root instanceof SettingsTreeSettingElement) { + return +predicate(root); + } else if (root instanceof SettingsTreeNewExtensionsElement) { + return 0; + } + + throw new Error('Argument to settingsTreeChildrenCount should only have group, setting, or extension elements'); + }; + return recursiveCounter(tree); +} + +export class SettingsTreeModel { + protected _root: SettingsTreeGroupElement; + protected _treeElementsById = new Map(); + private _treeElementsBySettingName = new Map(); + private _tocRoot: ITOCEntry; + + constructor( + private _viewState: ISettingsEditorViewState, + @IConfigurationService private _configurationService: IConfigurationService + ) { } + + get root(): SettingsTreeGroupElement { + return this._root; + } + + update(newTocRoot = this._tocRoot): void { + this._treeElementsById.clear(); + this._treeElementsBySettingName.clear(); + + const newRoot = this.createSettingsTreeGroupElement(newTocRoot); + if (newRoot.children[0] instanceof SettingsTreeGroupElement) { + (newRoot.children[0]).isFirstGroup = true; // TODO + } + + if (this._root) { + this._root.children = newRoot.children; + } else { + this._root = newRoot; + } + } + + getElementById(id: string): SettingsTreeElement { + return this._treeElementsById.get(id); + } + + getElementsByName(name: string): SettingsTreeSettingElement[] { + return this._treeElementsBySettingName.get(name); + } + + updateElementsByName(name: string): void { + if (!this._treeElementsBySettingName.has(name)) { + return; + } + + this._treeElementsBySettingName.get(name).forEach(element => { + const inspectResult = inspectSetting(element.setting.key, this._viewState.settingsTarget, this._configurationService); + element.update(inspectResult); + }); + } + + private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { + const element = new SettingsTreeGroupElement(); + const index = this._treeElementsById.size; + element.index = index; + element.id = tocEntry.id; + element.label = tocEntry.label; + element.parent = parent; + element.level = this.getDepth(element); + + element.children = []; + if (tocEntry.settings) { + const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) + .filter(el => el.setting.deprecationMessage ? el.isConfigured : true); + element.children.push(...settingChildren); + } + + if (tocEntry.children) { + const groupChildren = tocEntry.children.map(child => this.createSettingsTreeGroupElement(child, element)); + element.children.push(...groupChildren); + } + + this._treeElementsById.set(element.id, element); + return element; + } + + private getDepth(element: SettingsTreeElement): number { + if (element.parent) { + return 1 + this.getDepth(element.parent); + } else { + return 0; + } + } + + private createSettingsTreeSettingElement(setting: ISetting, parent: SettingsTreeGroupElement): SettingsTreeSettingElement { + const index = this._treeElementsById.size; + const inspectResult = inspectSetting(setting.key, this._viewState.settingsTarget, this._configurationService); + const element = new SettingsTreeSettingElement(setting, parent, index, inspectResult); + this._treeElementsById.set(element.id, element); + + const nameElements = this._treeElementsBySettingName.get(setting.key) || []; + nameElements.push(element); + this._treeElementsBySettingName.set(setting.key, nameElements); + return element; + } +} + +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' : + target === ConfigurationTarget.WORKSPACE ? 'workspace' : + 'workspaceFolder'; + const isConfigured = typeof inspected[targetSelector] !== 'undefined'; + + return { isConfigured, inspected, targetSelector }; +} + +function sanitizeId(id: string): string { + return id.replace(/[\.\/]/, '_'); +} + +export function settingKeyToDisplayFormat(key: string, groupId = ''): { category: string, label: string } { + const lastDotIdx = key.lastIndexOf('.'); + let category = ''; + if (lastDotIdx >= 0) { + category = key.substr(0, lastDotIdx); + key = key.substr(lastDotIdx + 1); + } + + groupId = groupId.replace(/\//g, '.'); + category = trimCategoryForGroup(category, groupId); + category = wordifyKey(category); + + const label = wordifyKey(key); + return { category, label }; +} + +function wordifyKey(key: string): string { + return key + .replace(/\.([a-z])/g, (match, p1) => ` › ${p1.toUpperCase()}`) + .replace(/([a-z])([A-Z])/g, '$1 $2') // fooBar => foo Bar + .replace(/^[a-z]/g, match => match.toUpperCase()) // foo => Foo + .replace(/\b\w+\b/g, match => { + return knownAcronyms.has(match.toLowerCase()) ? + match.toUpperCase() : + match; + }); +} + +function trimCategoryForGroup(category: string, groupId: string): string { + const doTrim = forward => { + const parts = groupId.split('.'); + while (parts.length) { + const reg = new RegExp(`^${parts.join('\\.')}(\\.|$)`, 'i'); + if (reg.test(category)) { + return category.replace(reg, ''); + } + + if (forward) { + parts.pop(); + } else { + parts.shift(); + } + } + + return null; + }; + + let trimmed = doTrim(true); + if (trimmed === null) { + trimmed = doTrim(false); + } + + if (trimmed === null) { + trimmed = category; + } + + return trimmed; +} + +export function isExcludeSetting(setting: ISetting): boolean { + return setting.key === 'files.exclude' || + setting.key === 'search.exclude'; +} + +function settingTypeEnumRenderable(_type: string | string[]) { + const enumRenderableSettingTypes = ['string', 'boolean', 'null', 'integer', 'number']; + let type = isArray(_type) ? _type : [_type]; + return type.every(type => enumRenderableSettingTypes.indexOf(type) > -1); +} + +export enum SearchResultIdx { + Local = 0, + Remote = 1, + NewExtensions = 2 +} + +export class SearchResultModel extends SettingsTreeModel { + private rawSearchResults: ISearchResult[]; + private cachedUniqueSearchResults: ISearchResult[]; + private newExtensionSearchResults: ISearchResult; + + readonly id = 'searchResultModel'; + + constructor( + viewState: ISettingsEditorViewState, + @IConfigurationService configurationService: IConfigurationService + ) { + super(viewState, configurationService); + this.update({ id: 'searchResultModel', label: '' }); + } + + getUniqueResults(): ISearchResult[] { + if (this.cachedUniqueSearchResults) { + return this.cachedUniqueSearchResults; + } + + if (!this.rawSearchResults) { + return []; + } + + const localMatchKeys = new Set(); + const localResult = objects.deepClone(this.rawSearchResults[SearchResultIdx.Local]); + if (localResult) { + localResult.filterMatches.forEach(m => localMatchKeys.add(m.setting.key)); + } + + const remoteResult = objects.deepClone(this.rawSearchResults[SearchResultIdx.Remote]); + if (remoteResult) { + remoteResult.filterMatches = remoteResult.filterMatches.filter(m => !localMatchKeys.has(m.setting.key)); + } + + if (remoteResult) { + this.newExtensionSearchResults = objects.deepClone(this.rawSearchResults[SearchResultIdx.NewExtensions]); + } + + this.cachedUniqueSearchResults = [localResult, remoteResult]; + return this.cachedUniqueSearchResults; + } + + getRawResults(): ISearchResult[] { + return this.rawSearchResults; + } + + setResult(order: SearchResultIdx, result: ISearchResult): void { + this.cachedUniqueSearchResults = null; + this.rawSearchResults = this.rawSearchResults || []; + if (!result) { + delete this.rawSearchResults[order]; + return; + } + + this.rawSearchResults[order] = result; + this.updateChildren(); + } + + updateChildren(): void { + this.update({ + id: 'searchResultModel', + label: 'searchResultModel', + settings: this.getFlatSettings() + }); + + if (this.newExtensionSearchResults && this.newExtensionSearchResults.filterMatches.length) { + const newExtElement = new SettingsTreeNewExtensionsElement(); + newExtElement.index = this._treeElementsById.size; + newExtElement.parent = this._root; + newExtElement.id = 'newExtensions'; + this._treeElementsById.set(newExtElement.id, newExtElement); + + 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._root.children.push(newExtElement); + } + } + + private getFlatSettings(): ISetting[] { + const flatSettings: ISetting[] = []; + this.getUniqueResults() + .filter(r => !!r) + .forEach(r => { + flatSettings.push( + ...r.filterMatches.map(m => m.setting)); + }); + + return flatSettings; + } +} \ No newline at end of file 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..ae0ad8a2d15 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts @@ -0,0 +1,482 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Color, RGBA } from 'vs/base/common/color'; +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, editorWidgetBorder } 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 modifiedItemIndicator = registerColor('settings.modifiedItemIndicator', { + light: new Color(new RGBA(102, 175, 224)), + dark: new Color(new RGBA(12, 125, 157)), + hc: new Color(new RGBA(0, 73, 122)) +}, localize('modifiedItemForeground', "(For settings editor preview) The color of 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.")); +export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, localize('settingsDropdownListBorder', "(For settings editor preview) Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); + +// 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 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}; }`); + collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a { color: ${link}; }`); + collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-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} }`); + collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown code { color: ${codeTextForegroundColor} }`); + + } + + const modifiedItemIndicatorColor = theme.getColor(modifiedItemIndicator); + if (modifiedItemIndicatorColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item > .setting-item-modified-indicator { border-color: ${modifiedItemIndicatorColor}; }`); + } +}); + +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, DOM.EventType.CLICK, e => this.onListClick(e))); + this._register(DOM.addDisposableListener(this.listElement, DOM.EventType.DBLCLICK, e => this.onListDoubleClick(e))); + + 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 onListClick(e: MouseEvent): void { + const targetIdx = this.getClickedItemIndex(e); + if (targetIdx < 0) { + return; + } + + if (this.model.getSelected() === targetIdx) { + return; + } + + this.model.select(targetIdx); + this.renderList(); + e.preventDefault(); + e.stopPropagation(); + } + + private onListDoubleClick(e: MouseEvent): void { + const targetIdx = this.getClickedItemIndex(e); + if (targetIdx < 0) { + return; + } + + const item = this.model.items[targetIdx]; + if (item) { + this.editSetting(item.pattern); + e.preventDefault(); + e.stopPropagation(); + } + } + + private getClickedItemIndex(e: MouseEvent): number { + if (!e.target) { + return -1; + } + + const actionbar = DOM.findParentWithClass(e.target, 'monaco-action-bar'); + if (actionbar) { + // Don't handle doubleclicks inside the action bar + return -1; + } + + const element = DOM.findParentWithClass((e.target), 'setting-exclude-row'); + if (!element) { + return -1; + } + + const targetIdxStr = element.getAttribute('data-index'); + if (!targetIdxStr) { + return -1; + } + + const targetIdx = parseInt(targetIdxStr); + return targetIdx; + } + + 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.editSetting(key); + } + }; + } + + private editSetting(key: string): void { + 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() + }); + } + 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..817cff659c1 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -5,10 +5,20 @@ 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, ITreeOptions } 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 { SettingsAccessibilityProvider, SettingsTreeFilter } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { ISettingsEditorViewState, SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +import { settingsHeaderForeground } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; +import { ISetting } from 'vs/workbench/services/preferences/common/preferences'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; const $ = DOM.$; @@ -17,6 +27,10 @@ export class TOCTreeModel { private _currentSearchModel: SearchResultModel; private _settingsTreeRoot: SettingsTreeGroupElement; + constructor(private viewState: ISettingsEditorViewState) { + + } + public set settingsTreeRoot(value: SettingsTreeGroupElement) { this._settingsTreeRoot = value; this.update(); @@ -36,29 +50,33 @@ export class TOCTreeModel { } private updateGroupCount(group: SettingsTreeGroupElement): void { - (group).count = this._currentSearchModel ? - this.getSearchResultChildrenCount(group) : - undefined; - group.children.forEach(child => { if (child instanceof SettingsTreeGroupElement) { this.updateGroupCount(child); } }); + + if (this._currentSearchModel) { + const childCount = group.children + .filter(child => child instanceof SettingsTreeGroupElement) + .reduce((acc, cur) => acc + (cur).count, 0); + + group.count = childCount + this.getSearchResultChildrenCount(group); + } else { + group.count = undefined; + } } private getSearchResultChildrenCount(group: SettingsTreeGroupElement): number { - return this._currentSearchModel.getChildren().filter(child => { - return this.groupContainsSetting(group, child.setting); + return this._currentSearchModel.root.children.filter(child => { + return child instanceof SettingsTreeSettingElement && this.groupContainsSetting(group, child.setting); }).length; } private groupContainsSetting(group: SettingsTreeGroupElement, setting: ISetting): boolean { return group.children.some(child => { if (child instanceof SettingsTreeSettingElement) { - return child.setting.key === setting.key; - } else if (child instanceof SettingsTreeGroupElement) { - return this.groupContainsSetting(child, setting); + return child.setting.key === setting.key && child.matchesAllTags(this.viewState.tagFilters); } else { return false; } @@ -69,49 +87,34 @@ export class TOCTreeModel { export type TOCTreeElement = SettingsTreeGroupElement | TOCTreeModel; export class TOCDataSource implements IDataSource { - constructor( - @IConfigurationService private configService: IConfigurationService - ) { - } - getId(tree: ITree, element: SettingsTreeGroupElement): string { return element.id; } hasChildren(tree: ITree, element: TOCTreeElement): boolean { return element instanceof TOCTreeModel || - (element instanceof SettingsTreeGroupElement && element.children && element.children.every(child => child instanceof SettingsTreeGroupElement)); + (element instanceof SettingsTreeGroupElement && element.children && element.children.some(child => child instanceof SettingsTreeGroupElement)); } - getChildren(tree: ITree, element: TOCTreeElement): TPromise { + getChildren(tree: ITree, element: TOCTreeElement): TPromise { return TPromise.as(this._getChildren(element)); } private _getChildren(element: TOCTreeElement): SettingsTreeElement[] { - // TODO@roblou hack. Clean up or remove this option - if (this.configService.getValue('workbench.settings.settingsSearchTocBehavior') === 'filter') { - const children = element.children as SettingsTreeElement[]; // TS???? - return children.filter(group => { - return (group).count !== 0; - }); - } - - return element.children; + return (element.children) + .filter(child => child instanceof SettingsTreeGroupElement); } - 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 +128,90 @@ 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; + template.labelElement.title = 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: ITreeOptions = { + showLoading: false, + twistiePixels: 15, + horizontalScrollMode: ScrollbarVisibility.Hidden + }; + + 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..8d084c1559a 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -10,6 +10,7 @@ import { join } from 'vs/base/common/paths'; import { ISettingsEditorModel, ISearchResult } from 'vs/workbench/services/preferences/common/preferences'; import { IEditor } from 'vs/workbench/common/editor'; import { IKeybindingItemEntry } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; +import { CancellationToken } from 'vs/base/common/cancellation'; export interface IWorkbenchSettingsConfiguration { workbench: { @@ -40,7 +41,7 @@ export interface IPreferencesSearchService { } export interface ISearchProvider { - searchModel(preferencesModel: ISettingsEditorModel): TPromise; + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise; } export interface IKeybindingsEditor extends IEditor { @@ -61,8 +62,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,9 +73,9 @@ 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'; +export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults'; diff --git a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts index f1c464e54c5..88c1483e5b6 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts @@ -24,7 +24,7 @@ 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 { isLinux } from 'vs/base/common/platform'; -import { isEqual, hasToIgnoreCase } from 'vs/base/common/resources'; +import { isEqual } from 'vs/base/common/resources'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -82,15 +82,15 @@ export class PreferencesContribution implements IWorkbenchContribution { // Global User Settings File if (isEqual(resource, URI.file(this.environmentService.appSettingsPath), !isLinux)) { - return { override: this.preferencesService.openGlobalSettings(options, group) }; + return { override: this.preferencesService.openGlobalSettings(true, options, group) }; } // Single Folder Workspace Settings File const state = this.workspaceService.getWorkbenchState(); if (state === WorkbenchState.FOLDER) { const folders = this.workspaceService.getWorkspace().folders; - if (isEqual(resource, folders[0].toResource(FOLDER_SETTINGS_PATH), hasToIgnoreCase(resource))) { - return { override: this.preferencesService.openWorkspaceSettings(options, group) }; + if (isEqual(resource, folders[0].toResource(FOLDER_SETTINGS_PATH))) { + return { override: this.preferencesService.openWorkspaceSettings(true, options, group) }; } } @@ -98,8 +98,8 @@ 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 (isEqual(resource, folders[i].toResource(FOLDER_SETTINGS_PATH), hasToIgnoreCase(resource))) { - return { override: this.preferencesService.openFolderSettings(folders[i].uri, options, group) }; + if (isEqual(resource, folders[i].toResource(FOLDER_SETTINGS_PATH))) { + return { override: this.preferencesService.openFolderSettings(folders[i].uri, true, 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 c8ab50f2ba7..7159ac5c1b1 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -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 { OpenDefaultKeybindingsFileAction, 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, OpenSettingsJsonAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; import { IKeybindingsEditor, IPreferencesSearchService, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, - KEYBINDINGS_EDITOR_COMMAND_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, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } 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'; @@ -35,6 +35,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { PreferencesSearchService } from 'vs/workbench/parts/preferences/electron-browser/preferencesSearch'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { Command } from 'vs/editor/browser/editorExtensions'; +import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; registerSingleton(IPreferencesSearchService, PreferencesSearchService); @@ -151,11 +152,17 @@ class KeybindingsEditorInputFactory implements IEditorInputFactory { class SettingsEditor2InputFactory implements IEditorInputFactory { public serialize(editorInput: SettingsEditor2Input): string { - return JSON.stringify({}); + const input = editorInput; + + const serialized: ISerializedDefaultPreferencesEditorInput = { resource: input.getResource().toString() }; + + return JSON.stringify(serialized); } - public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { - return instantiationService.createInstance(SettingsEditor2Input); + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): SettingsEditor2Input { + const deserialized: ISerializedDefaultPreferencesEditorInput = JSON.parse(serializedEditorInput); + + return instantiationService.createInstance(SettingsEditor2Input, URI.parse(deserialized.resource)); } } @@ -191,9 +198,11 @@ 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(OpenSettingsJsonAction, OpenSettingsJsonAction.ID, OpenSettingsJsonAction.LABEL), 'Preferences: Open Settings (JSON)', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettings2Action, OpenSettings2Action.ID, OpenSettings2Action.LABEL), 'Preferences: Open Settings (UI)', 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); @@ -355,23 +364,6 @@ const startSearchCommand = new StartSearchDefaultSettingsCommand({ }); startSearchCommand.register(); -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, weight: KeybindingWeight.WorkbenchContrib } -}); -focusSearchFromSettingsCommand.register(); - - class ClearSearchResultsCommand extends SettingsCommand { public runCommand(accessor: ServicesAccessor, args: any): void { @@ -401,14 +393,14 @@ class FocusSettingsFileEditorCommand extends SettingsCommand { } const focusSettingsFileEditorCommand = new FocusSettingsFileEditorCommand({ id: SETTINGS_EDITOR_COMMAND_FOCUS_FILE, - precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_SEARCH_FOCUS, SuggestContext.Visible.toNegated()), kbOpts: { primary: KeyCode.DownArrow, weight: KeybindingWeight.EditorContrib } }); focusSettingsFileEditorCommand.register(); const focusSettingsFromSearchCommand = new FocusSettingsFileEditorCommand({ id: SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, - precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_SEARCH_FOCUS, SuggestContext.Visible.toNegated()), kbOpts: { primary: KeyCode.DownArrow, weight: KeybindingWeight.WorkbenchContrib } }); focusSettingsFromSearchCommand.register(); @@ -461,23 +453,6 @@ const editFocusedSettingCommand = new EditFocusedSettingCommand({ }); editFocusedSettingCommand.register(); -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, weight: KeybindingWeight.WorkbenchContrib } -}); -editFocusedSettingCommand2.register(); - class FocusSettingsListCommand extends SettingsCommand { public runCommand(accessor: ServicesAccessor, args: any): void { @@ -495,12 +470,28 @@ const focusSettingsListCommand = new FocusSettingsListCommand({ }); focusSettingsListCommand.register(); +class ShowContextMenuCommand extends SettingsCommand { + public runCommand(accessor: ServicesAccessor, args: any): void { + const preferencesEditor = this.getPreferencesEditor(accessor); + if (preferencesEditor instanceof SettingsEditor2) { + preferencesEditor.showContextMenu(); + } + } +} + +const showContextMenuCommand = new ShowContextMenuCommand({ + id: SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, + precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR), + kbOpts: { primary: KeyMod.Shift | KeyCode.F9, weight: KeybindingWeight.WorkbenchContrib } +}); +showContextMenuCommand.register(); + // Preferences menu MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { group: '1_settings', command: { - id: OpenSettings2Action.ID, + id: OpenSettingsAction.ID, title: nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings") }, order: 1 diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts index f0037613a93..e80122a5a66 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts @@ -21,6 +21,8 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, LocalExtensionType, ILocalExtension, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { IPreferencesSearchService, ISearchProvider, IWorkbenchSettingsConfiguration } from 'vs/workbench/parts/preferences/common/preferences'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; export interface IEndpointDetails { urlBase: string; @@ -90,6 +92,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. @@ -99,30 +104,41 @@ export class LocalSearchProvider implements ISearchProvider { .trim(); } - searchModel(preferencesModel: ISettingsEditorModel): TPromise { + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise { if (!this._filter) { 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); }; @@ -144,8 +160,9 @@ interface IBingRequestDetails { class RemoteSearchProvider implements ISearchProvider { // Must keep extension filter size under 8kb. 42 filters puts us there. - private static MAX_REQUEST_FILTERS = 42; - private static MAX_REQUESTS = 10; + private static readonly MAX_REQUEST_FILTERS = 42; + private static readonly MAX_REQUESTS = 10; + private static readonly NEW_EXTENSIONS_MIN_SCORE = 1; private _remoteSearchP: TPromise; @@ -159,18 +176,23 @@ class RemoteSearchProvider implements ISearchProvider { TPromise.wrap(null); } - searchModel(preferencesModel: ISettingsEditorModel): TPromise { + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise { return this._remoteSearchP.then(remoteResult => { if (!remoteResult) { return null; } + if (token && token.isCancellationRequested) { + throw canceled(); + } + const resultKeys = Object.keys(remoteResult.scoredResults); const highScoreKey = top(resultKeys, (a, b) => remoteResult.scoredResults[b].score - remoteResult.scoredResults[a].score, 1)[0]; const highScore = highScoreKey ? remoteResult.scoredResults[highScoreKey].score : 0; const minScore = highScore / 5; if (this.options.newExtensionsOnly) { - const passingScoreKeys = resultKeys.filter(k => remoteResult.scoredResults[k].score >= minScore); + const newExtsMinScore = Math.max(RemoteSearchProvider.NEW_EXTENSIONS_MIN_SCORE, minScore); + const passingScoreKeys = resultKeys.filter(k => remoteResult.scoredResults[k].score >= newExtsMinScore); const filterMatches: ISettingMatch[] = passingScoreKeys.map(k => { const remoteSetting = remoteResult.scoredResults[k]; const setting = remoteSettingToISetting(remoteSetting); @@ -391,6 +413,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/settingsTreeModels.test.ts similarity index 85% rename from src/vs/workbench/parts/preferences/test/browser/settingsTree.test.ts rename to src/vs/workbench/parts/preferences/test/browser/settingsTreeModels.test.ts index a1d32f71ac6..44088dcbd61 100644 --- a/src/vs/workbench/parts/preferences/test/browser/settingsTree.test.ts +++ b/src/vs/workbench/parts/preferences/test/browser/settingsTreeModels.test.ts @@ -6,7 +6,7 @@ 'use strict'; import * as assert from 'assert'; -import { settingKeyToDisplayFormat } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { settingKeyToDisplayFormat } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; suite('SettingsTree', () => { test('settingKeyToDisplayFormat', () => { @@ -20,7 +20,7 @@ suite('SettingsTree', () => { assert.deepEqual( settingKeyToDisplayFormat('foo.bar.etc'), { - category: 'Foo.Bar', + category: 'Foo › Bar', label: 'Etc' }); @@ -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/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts index e76351076f2..1454998d2d9 100644 --- a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -293,7 +293,7 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { this.onBeforeRun(this.commandId); // Use a timeout to give the quick open widget a chance to close itself first - TPromise.timeout(50).done(() => { + setTimeout(() => { if (action && (!(action instanceof Action) || action.enabled)) { try { /* __GDPR__ @@ -314,7 +314,7 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { } else { this.notificationService.info(nls.localize('actionNotEnabled', "Command '{0}' is not enabled in the current context.", this.getLabel())); } - }, err => this.onError(err)); + }, 50); } private onError(error?: Error): void { diff --git a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts index 1ce6dc74391..638f3f36c58 100644 --- a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts +++ b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts @@ -165,3 +165,23 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { }, 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 505f3c8a672..48417f8b9d3 100644 --- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -703,11 +703,16 @@ export class DirtyDiffController implements IEditorContribution { return; } + if (e.target.element.className.indexOf('dirty-diff-glyph') < 0) { + return; + } + const data = e.target.detail as IMarginData; - const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft; + const offsetLeftInGutter = (e.target.element as HTMLElement).offsetLeft; + const gutterOffsetX = data.offsetX - offsetLeftInGutter; // TODO@joao TODO@alex TODO@martin this is such that we don't collide with folding - if (gutterOffsetX > 10) { + if (gutterOffsetX < -3 || gutterOffsetX > 6) { // dirty diff decoration on hover is 9px wide return; } diff --git a/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css b/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css index de4d95ed717..8a884647c66 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/dirtydiffDecorator.css @@ -6,6 +6,7 @@ .monaco-editor .dirty-diff-glyph { margin-left: 5px; cursor: pointer; + z-index: 5; } .monaco-editor .dirty-diff-deleted:after { 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 6a37c940969..453a5aa6da3 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts @@ -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': { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 102c393dddb..9c8b0a5b069 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -109,7 +109,7 @@ class StatusBarActionItem extends ActionItem { _updateLabel(): void { if (this.options.label) { - this.$e.innerHtml(renderOcticons(this.getAction().label)); + this.label.innerHTML = renderOcticons(this.getAction().label); } } } @@ -1321,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/media/stop-inverse.svg b/src/vs/workbench/parts/search/browser/media/stop-inverse.svg index ef79528e9c8..3740e50c8c0 100644 --- a/src/vs/workbench/parts/search/browser/media/stop-inverse.svg +++ b/src/vs/workbench/parts/search/browser/media/stop-inverse.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/media/stop.svg b/src/vs/workbench/parts/search/browser/media/stop.svg index 0b36e84ac92..9a036f2e620 100644 --- a/src/vs/workbench/parts/search/browser/media/stop.svg +++ b/src/vs/workbench/parts/search/browser/media/stop.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index 179ea2e6ec3..74ecdb47103 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'; @@ -26,7 +25,7 @@ import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/ import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IQueryOptions, ISearchService, ISearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search'; +import { IQueryOptions, ISearchService, IFileSearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IRange } from 'vs/editor/common/core/range'; @@ -34,10 +33,12 @@ 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 { ILabelService } from 'vs/platform/label/common/label'; +import { untildify } from 'vs/base/common/labels'; export class FileQuickOpenModel extends QuickOpenModel { - constructor(entries: QuickOpenEntry[], public stats?: ISearchStats) { + constructor(entries: QuickOpenEntry[], public stats?: IFileSearchStats) { super(entries); } } @@ -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, + @ILabelService private labelService: ILabelService ) { 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,12 +173,12 @@ 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.labelService.getUriLabel(resources.dirname(fileMatch.resource), true); results.push(this.instantiationService.createInstance(FileEntry, fileMatch.resource, label, description, iconClass)); } - return new FileQuickOpenModel(results, complete.stats); + return new FileQuickOpenModel(results, complete.stats); }); } diff --git a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts index 8882abb8fc5..14a26b1b5de 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 { ILabelService } from 'vs/platform/label/common/label'; 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 + @ILabelService private _labelService: ILabelService ) { 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._labelService.getUriLabel(this._bearing.location.uri, true); } } return containerName; diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts index 73fe8903f8a..bea4869c5e9 100644 --- a/src/vs/workbench/parts/search/browser/replaceService.ts +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -24,6 +24,9 @@ import { ResourceTextEdit } from 'vs/editor/common/modes'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { Range } from 'vs/editor/common/core/range'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { mergeSort } from 'vs/base/common/arrays'; const REPLACE_PREVIEW = 'replacePreview'; @@ -103,26 +106,7 @@ export class ReplaceService implements IReplaceService { public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): TPromise; public replace(arg: any, progress: IProgressRunner = null, resource: URI = null): TPromise { - const edits: ResourceTextEdit[] = []; - - if (arg instanceof Match) { - let match = arg; - edits.push(this.createEdit(match, match.replaceString, resource)); - } - - if (arg instanceof FileMatch) { - arg = [arg]; - } - - if (arg instanceof Array) { - arg.forEach(element => { - let fileMatch = element; - if (fileMatch.count() > 0) { - edits.push(...fileMatch.matches().map(match => this.createEdit(match, match.replaceString, resource))); - } - }); - } - + const edits: ResourceTextEdit[] = this.createEdits(arg, resource); return this.bulkEditorService.apply({ edits }, { progress }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); } @@ -140,6 +124,10 @@ export class ReplaceService implements IReplaceService { revealIfVisible: true } }).then(editor => { + const disposable = fileMatch.onDispose(() => { + editor.input.dispose(); + disposable.dispose(); + }); this.updateReplacePreview(fileMatch).then(() => { let editorControl = editor.getControl(); if (element instanceof Match) { @@ -163,7 +151,7 @@ export class ReplaceService implements IReplaceService { } else { replaceModel.undo(); } - returnValue = this.replace(fileMatch, null, replacePreviewUri); + this.applyEditsToPreview(fileMatch, replaceModel); } return returnValue.then(() => { sourceModelRef.dispose(); @@ -172,6 +160,42 @@ export class ReplaceService implements IReplaceService { }); } + private applyEditsToPreview(fileMatch: FileMatch, replaceModel: ITextModel): void { + const resourceEdits = this.createEdits(fileMatch, replaceModel.uri); + const modelEdits = []; + for (const resourceEdit of resourceEdits) { + for (const edit of resourceEdit.edits) { + const range = Range.lift(edit.range); + modelEdits.push(EditOperation.replaceMove(range, edit.text)); + } + } + replaceModel.pushEditOperations([], mergeSort(modelEdits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)), () => []); + } + + private createEdits(arg: FileMatchOrMatch | FileMatch[], resource: URI = null): ResourceTextEdit[] { + const edits: ResourceTextEdit[] = []; + + if (arg instanceof Match) { + let match = arg; + edits.push(this.createEdit(match, match.replaceString, resource)); + } + + if (arg instanceof FileMatch) { + arg = [arg]; + } + + if (arg instanceof Array) { + arg.forEach(element => { + let fileMatch = element; + if (fileMatch.count() > 0) { + edits.push(...fileMatch.matches().map(match => this.createEdit(match, match.replaceString, resource))); + } + }); + } + + return edits; + } + private createEdit(match: Match, text: string, resource: URI = null): ResourceTextEdit { let fileMatch: FileMatch = match.parent(); let resourceEdit: ResourceTextEdit = { diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index a401c67d8ac..88e4943184b 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 { ILabelService } from 'vs/platform/label/common/label'; export class SearchDataSource implements IDataSource { @@ -37,7 +36,10 @@ export class SearchDataSource implements IDataSource { private includeFolderMatch: boolean; private listener: IDisposable; - constructor(@IWorkspaceContextService private contextService: IWorkspaceContextService) { + constructor( + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IConfigurationService private configurationService: IConfigurationService, + ) { this.updateIncludeFolderMatch(); this.listener = this.contextService.onDidChangeWorkbenchState(() => this.updateIncludeFolderMatch()); } @@ -104,6 +106,14 @@ export class SearchDataSource implements IDataSource { if (numChildren <= 0) { return false; } + + const collapseOption = this.configurationService.getValue('search.collapseResults'); + if (collapseOption === 'alwaysCollapse') { + return false; + } else if (collapseOption === 'alwaysExpand') { + return true; + } + return numChildren < SearchDataSource.AUTOEXPAND_CHILD_LIMIT || element instanceof FolderMatch; } @@ -236,7 +246,7 @@ export class SearchRenderer extends Disposable implements IRenderer { } private renderFolderMatch(tree: ITree, folderMatch: FolderMatch, templateData: IFolderMatchTemplate): void { - if (folderMatch.hasRoot()) { + if (folderMatch.hasResource()) { const workspaceFolder = this.contextService.getWorkspaceFolder(folderMatch.resource()); if (workspaceFolder && resources.isEqual(workspaceFolder.uri, folderMatch.resource())) { templateData.label.setFile(folderMatch.resource(), { fileKind: FileKind.ROOT_FOLDER, hidePath: true }); @@ -263,10 +273,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,18 +329,19 @@ export class SearchRenderer extends Disposable implements IRenderer { export class SearchAccessibilityProvider implements IAccessibilityProvider { constructor( - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IEnvironmentService private environmentService: IEnvironmentService + @ILabelService private labelService: ILabelService ) { } public getAriaLabel(tree: ITree, element: FileMatchOrMatch): string { if (element instanceof FolderMatch) { - return nls.localize('folderMatchAriaLabel', "{0} matches in folder root {1}, Search result", element.count(), element.name()); + return element.hasResource() ? + nls.localize('folderMatchAriaLabel', "{0} matches in folder root {1}, Search result", element.count(), element.name()) : + nls.localize('otherFilesAriaLabel', "{0} matches outside of the workspace, Search result", element.count()); } if (element instanceof FileMatch) { - const path = getPathLabel(element.resource(), this.environmentService, this.contextService) || element.resource().fsPath; + const path = this.labelService.getUriLabel(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 85bd5d5d603..7dc0e16dfdd 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -108,6 +108,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { private readonly selectCurrentMatchEmitter: Emitter; private delayedRefresh: Delayer; private changedWhileHidden: boolean; + private isWide: boolean; private searchWithoutFolderMessageBuilder: Builder; @@ -343,7 +344,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { let searchHistory = history.search || this.viewletSettings['query.searchHistory'] || []; let replaceHistory = history.replace || this.viewletSettings['query.replaceHistory'] || []; - this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, builder, { + this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, builder.getHTMLElement(), { value: contentPattern, isRegex: isRegex, isCaseSensitive: isCaseSensitive, @@ -404,7 +405,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { if (!isReplaceShown) { this.storageService.store(SearchView.SHOW_REPLACE_STORAGE_KEY, false, StorageScope.WORKSPACE); } else { - this.storageService.remove(SearchView.SHOW_REPLACE_STORAGE_KEY); + this.storageService.remove(SearchView.SHOW_REPLACE_STORAGE_KEY, StorageScope.WORKSPACE); } } @@ -826,8 +827,10 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } if (this.size.width >= SearchView.WIDE_VIEW_SIZE) { + this.isWide = true; dom.addClass(this.getContainer(), SearchView.WIDE_CLASS_NAME); } else { + this.isWide = false; dom.removeClass(this.getContainer(), SearchView.WIDE_CLASS_NAME); } @@ -1069,7 +1072,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 }; @@ -1082,7 +1084,12 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { disregardIgnoreFiles: !useExcludesAndIgnoreFiles, disregardExcludeSettings: !useExcludesAndIgnoreFiles, excludePattern, - includePattern + includePattern, + previewOptions: { + leadingChars: 20, + maxLines: 1, + totalChars: this.isWide ? 250 : 75 + } }; const folderResources = this.contextService.getWorkspace().folders; diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index 86bf8193435..0f834a38ad8 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -20,7 +20,6 @@ import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/con import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Event, Emitter } from 'vs/base/common/event'; -import { Builder } from 'vs/base/browser/builder'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { isSearchViewFocused, appendKeyBindingLabel } from 'vs/workbench/parts/search/browser/searchActions'; import * as Constants from 'vs/workbench/parts/search/common/constants'; @@ -122,7 +121,7 @@ export class SearchWidget extends Widget { public readonly onBlur: Event = this._onBlur.event; constructor( - container: Builder, + container: HTMLElement, options: ISearchWidgetOptions, @IContextViewService private contextViewService: IContextViewService, @IThemeService private themeService: IThemeService, @@ -228,8 +227,10 @@ export class SearchWidget extends Widget { this.searchInput.focusOnRegex(); } - private render(container: Builder, options: ISearchWidgetOptions): void { - this.domNode = container.div({ 'class': 'search-widget' }).style({ position: 'relative' }).getHTMLElement(); + private render(container: HTMLElement, options: ISearchWidgetOptions): void { + this.domNode = dom.append(container, dom.$('.search-widget')); + this.domNode.style.position = 'relative'; + this.renderToggleReplaceButton(this.domNode); this.renderSearchInput(this.domNode, options); @@ -245,7 +246,8 @@ export class SearchWidget extends Widget { }; this.toggleReplaceButton = this._register(new Button(parent, opts)); this.toggleReplaceButton.element.setAttribute('aria-expanded', 'false'); - this.toggleReplaceButton.icon = 'toggle-replace-button collapse'; + this.toggleReplaceButton.element.classList.add('collapse'); + this.toggleReplaceButton.icon = 'toggle-replace-button'; // TODO@joh need to dispose this listener eventually this.toggleReplaceButton.onDidClick(() => this.onToggleReplaceButton()); this.toggleReplaceButton.element.title = nls.localize('search.replace.toggle.button.title', "Toggle Replace"); diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 525753bcae8..1ee9968ff2e 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,6 +74,8 @@ export class QueryBuilder { if (contentPattern) { this.resolveSmartCaseToCaseSensitive(contentPattern); + + contentPattern.wordSeparators = this.configurationService.getValue().editor.wordSeparators; } const query: ISearchQuery = { @@ -88,11 +91,12 @@ export class QueryBuilder { maxResults: options.maxResults, sortByScore: options.sortByScore, cacheKey: options.cacheKey, - contentPattern: contentPattern, + contentPattern, useRipgrep, disregardIgnoreFiles: options.disregardIgnoreFiles || !useIgnoreFiles, disregardExcludeSettings: options.disregardExcludeSettings, - ignoreSymlinks + ignoreSymlinks, + previewOptions: options.previewOptions }; // Filter extraFileResources against global include/exclude patterns - they are already expected to not belong to a workspace @@ -272,18 +276,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 0705774d83c..376fe0f666a 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -3,39 +3,50 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as objects from 'vs/base/common/objects'; -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 } from 'vs/base/common/winjs.base'; +import * as errors from 'vs/base/common/errors'; +import { anyEvent, Emitter, Event, fromPromise, stopwatch } from 'vs/base/common/event'; +import { getBaseLabel } from 'vs/base/common/labels'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ResourceMap, TernarySearchTree, values } from 'vs/base/common/map'; +import * as objects from 'vs/base/common/objects'; 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'; -import { ISearchService, ISearchProgressItem, ISearchComplete, ISearchQuery, IPatternInfo, IFileMatch } from 'vs/platform/search/common/search'; -import { ReplacePattern } from 'vs/platform/search/common/replace'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { TPromise } from 'vs/base/common/winjs.base'; import { Range } from 'vs/editor/common/core/range'; -import { ITextModel, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness, FindMatch } from 'vs/editor/common/model'; -import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; -import { IProgressRunner } from 'vs/platform/progress/common/progress'; +import { FindMatch, IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IProgressRunner } from 'vs/platform/progress/common/progress'; +import { ReplacePattern } from 'vs/platform/search/common/replace'; +import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, TextSearchResult } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; -import { getBaseLabel } from 'vs/base/common/labels'; +import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; export class Match { - private _lineText: string; private _id: string; private _range: Range; + private _previewText: string; + private _rangeInPreviewText: Range; - constructor(private _parent: FileMatch, text: string, lineNumber: number, offset: number, length: number) { - this._lineText = text; - this._range = new Range(1 + lineNumber, 1 + offset, 1 + lineNumber, 1 + offset + length); - this._id = this._parent.id() + '>' + lineNumber + '>' + offset + this.getMatchString(); + constructor(private _parent: FileMatch, _result: ITextSearchResult) { + this._range = new Range( + _result.range.startLineNumber + 1, + _result.range.startColumn + 1, + _result.range.endLineNumber + 1, + _result.range.endColumn + 1); + + this._rangeInPreviewText = new Range( + _result.preview.match.startLineNumber + 1, + _result.preview.match.startColumn + 1, + _result.preview.match.endLineNumber + 1, + _result.preview.match.endColumn + 1); + this._previewText = _result.preview.text; + + this._id = this._parent.id() + '>' + this._range + this.getMatchString(); } public id(): string { @@ -47,7 +58,7 @@ export class Match { } public text(): string { - return this._lineText; + return this._previewText; } public range(): Range { @@ -55,11 +66,9 @@ export class Match { } public preview(): { before: string; inside: string; after: string; } { - let before = this._lineText.substring(0, this._range.startColumn - 1), + const before = this._previewText.substring(0, this._rangeInPreviewText.startColumn - 1), inside = this.getMatchString(), - after = this._lineText.substring(this._range.endColumn - 1, Math.min(this._range.endColumn + 150, this._lineText.length)); - - before = strings.lcut(before, 26); + after = this._previewText.substring(this._rangeInPreviewText.endColumn - 1); return { before, @@ -75,7 +84,7 @@ export class Match { // If match string is not matching then regex pattern has a lookahead expression if (replaceString === null) { - replaceString = searchModel.replacePattern.getReplaceString(matchString + this._lineText.substring(this._range.endColumn - 1)); + replaceString = searchModel.replacePattern.getReplaceString(matchString + this._previewText.substring(this._rangeInPreviewText.endColumn - 1)); } // Match string is still not matching. Could be unsupported matches (multi-line). @@ -87,7 +96,7 @@ export class Match { } public getMatchString(): string { - return this._lineText.substring(this._range.startColumn - 1, this._range.endColumn - 1); + return this._previewText.substring(this._rangeInPreviewText.startColumn - 1, this._rangeInPreviewText.endColumn - 1); } } @@ -134,7 +143,7 @@ export class FileMatch extends Disposable { private _updateScheduler: RunOnceScheduler; private _modelDecorations: string[] = []; - constructor(private _query: IPatternInfo, private _maxResults: number, private _parent: FolderMatch, private rawMatch: IFileMatch, + constructor(private _query: IPatternInfo, private _previewOptions: ITextSearchPreviewOptions, private _maxResults: number, private _parent: FolderMatch, private rawMatch: IFileMatch, @IModelService private modelService: IModelService, @IReplaceService private replaceService: IReplaceService) { super(); this._resource = this.rawMatch.resource; @@ -152,11 +161,9 @@ export class FileMatch extends Disposable { this.bindModel(model); this.updateMatchesForModel(); } else { - this.rawMatch.lineMatches.forEach((rawLineMatch) => { - rawLineMatch.offsetAndLengths.forEach(offsetAndLength => { - let match = new Match(this, rawLineMatch.preview, rawLineMatch.lineNumber, offsetAndLength[0], offsetAndLength[1]); - this.add(match); - }); + this.rawMatch.matches.forEach((rawLineMatch) => { + let match = new Match(this, rawLineMatch); + this.add(match); }); } } @@ -222,7 +229,12 @@ export class FileMatch extends Disposable { private updateMatches(matches: FindMatch[], modelChange: boolean) { matches.forEach(m => { - let match = new Match(this, this._model.getLineContent(m.range.startLineNumber), m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endColumn - m.range.startColumn); + const textSearchResult = new TextSearchResult( + this._model.getLineContent(m.range.startLineNumber), + new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.startLineNumber - 1, m.range.endColumn), + this._previewOptions); + const match = new Match(this, textSearchResult); + if (!this._removedMatches.has(match.id())) { this.add(match); if (this.isMatchSelected(match)) { @@ -348,7 +360,7 @@ export class FolderMatch extends Disposable { private _unDisposedFileMatches: ResourceMap; private _replacingAll: boolean = false; - constructor(private _resource: URI, private _id: string, private _index: number, private _query: ISearchQuery, private _parent: SearchResult, private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, + constructor(private _resource: URI | null, private _id: string, private _index: number, private _query: ISearchQuery, private _parent: SearchResult, private _searchModel: SearchModel, @IReplaceService private replaceService: IReplaceService, @IInstantiationService private instantiationService: IInstantiationService) { super(); this._fileMatches = new ResourceMap(); @@ -371,7 +383,7 @@ export class FolderMatch extends Disposable { return this._id; } - public resource(): URI { + public resource(): URI | null { return this._resource; } @@ -387,21 +399,21 @@ export class FolderMatch extends Disposable { return this._parent; } - public hasRoot(): boolean { - return this._resource.fsPath !== ''; + public hasResource(): boolean { + return !!this._resource; } public add(raw: IFileMatch[], silent: boolean): void { - let changed: FileMatch[] = []; + const changed: FileMatch[] = []; raw.forEach((rawFileMatch) => { if (this._fileMatches.has(rawFileMatch.resource)) { this._fileMatches.get(rawFileMatch.resource).dispose(); } - let fileMatch = this.instantiationService.createInstance(FileMatch, this._query.contentPattern, this._query.maxResults, this, rawFileMatch); + const fileMatch = this.instantiationService.createInstance(FileMatch, this._query.contentPattern, this._query.previewOptions, this._query.maxResults, this, rawFileMatch); this.doAdd(fileMatch); changed.push(fileMatch); - let disposable = fileMatch.onChange(() => this.onFileChange(fileMatch)); + const disposable = fileMatch.onChange(() => this.onFileChange(fileMatch)); fileMatch.onDispose(() => disposable.dispose()); }); if (!silent && changed.length) { @@ -525,6 +537,7 @@ export class SearchResult extends Disposable { public readonly onChange: Event = this._onChange.event; private _folderMatches: FolderMatch[] = []; + private _otherFilesMatch: FolderMatch; private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forPaths(); private _showHighlights: boolean; @@ -539,17 +552,19 @@ export class SearchResult extends Disposable { public set query(query: ISearchQuery) { // When updating the query we could change the roots, so ensure we clean up the old roots first. this.clear(); - const otherFiles = URI.parse(''); - this._folderMatches = (query.folderQueries || []).map((fq) => fq.folder).concat([otherFiles]).map((resource, index) => { - const id = resource.toString() || 'otherFiles'; - const folderMatch = this.instantiationService.createInstance(FolderMatch, resource, id, index, query, this, this._searchModel); - const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); - folderMatch.onDispose(() => disposable.dispose()); - return folderMatch; - }); - // otherFiles is the fallback for missing values in the TrieMap. So we do not insert it. - this._folderMatches.slice(0, this.folderMatches.length - 1) - .forEach(fm => this._folderMatchesMap.set(fm.resource().fsPath, fm)); + this._folderMatches = (query.folderQueries || []) + .map(fq => fq.folder) + .map((resource, index) => this.createFolderMatch(resource, resource.toString(), index, query)); + this._folderMatches.forEach(fm => this._folderMatchesMap.set(fm.resource().fsPath, fm)); + + this._otherFilesMatch = this.createFolderMatch(null, 'otherFiles', this._folderMatches.length + 1, query); + } + + private createFolderMatch(resource: URI | null, id: string, index: number, query: ISearchQuery): FolderMatch { + const folderMatch = this.instantiationService.createInstance(FolderMatch, resource, id, index, query, this, this._searchModel); + const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); + folderMatch.onDispose(() => disposable.dispose()); + return folderMatch; } public get searchModel(): SearchModel { @@ -558,27 +573,34 @@ export class SearchResult extends Disposable { public add(allRaw: IFileMatch[], silent: boolean = false): void { // Split up raw into a list per folder so we can do a batch add per folder. - let rawPerFolder = new ResourceMap(); + const rawPerFolder = new ResourceMap(); + const otherFileMatches: IFileMatch[] = []; this._folderMatches.forEach((folderMatch) => rawPerFolder.set(folderMatch.resource(), [])); allRaw.forEach(rawFileMatch => { let folderMatch = this.getFolderMatch(rawFileMatch.resource); - if (folderMatch) { + if (folderMatch.resource()) { rawPerFolder.get(folderMatch.resource()).push(rawFileMatch); + } else { + otherFileMatches.push(rawFileMatch); } }); + rawPerFolder.forEach((raw) => { if (!raw.length) { return; } - let folderMatch = this.getFolderMatch(raw[0].resource); + + const folderMatch = this.getFolderMatch(raw[0].resource); if (folderMatch) { folderMatch.add(raw, silent); } }); + + this.otherFiles.add(otherFileMatches, silent); } public clear(): void { - this._folderMatches.forEach((folderMatch) => folderMatch.clear()); + this.folderMatches().forEach((folderMatch) => folderMatch.clear()); this.disposeMatches(); } @@ -615,19 +637,21 @@ export class SearchResult extends Disposable { } public folderMatches(): FolderMatch[] { - return this._folderMatches.concat(); + return this._otherFilesMatch ? + this._folderMatches.concat(this._otherFilesMatch) : + this._folderMatches.concat(); } public matches(): FileMatch[] { let matches: FileMatch[][] = []; - this._folderMatches.forEach((folderMatch) => { + this.folderMatches().forEach((folderMatch) => { matches.push(folderMatch.matches()); }); return [].concat(...matches); } public isEmpty(): boolean { - return this._folderMatches.every((folderMatch) => folderMatch.isEmpty()); + return this.folderMatches().every((folderMatch) => folderMatch.isEmpty()); } public fileCount(): number { @@ -674,18 +698,19 @@ export class SearchResult extends Disposable { } private get otherFiles(): FolderMatch { - return this._folderMatches[this._folderMatches.length - 1]; + return this._otherFilesMatch; } private set replacingAll(running: boolean) { - this._folderMatches.forEach((folderMatch) => { + this.folderMatches().forEach((folderMatch) => { folderMatch.replacingAll = running; }); } private disposeMatches(): void { - this._folderMatches.forEach(folderMatch => folderMatch.dispose()); + this.folderMatches().forEach(folderMatch => folderMatch.dispose()); this._folderMatches = []; + this._otherFilesMatch = null; this._folderMatchesMap = TernarySearchTree.forPaths(); this._rangeHighlightDecorations.removeHighlightRange(); } @@ -796,13 +821,24 @@ export class SearchModel extends Disposable { const options: IPatternInfo = objects.assign({}, this._searchQuery.contentPattern); delete options.pattern; + + const stats = completed && completed.stats as ITextSearchStats; + + const fileSchemeOnly = this._searchQuery.folderQueries.every(fq => fq.folder.scheme === 'file'); + const otherSchemeOnly = this._searchQuery.folderQueries.every(fq => fq.folder.scheme !== 'file'); + const scheme = fileSchemeOnly ? 'file' : + otherSchemeOnly ? 'other' : + 'mixed'; + /* __GDPR__ "searchResultsShown" : { "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "fileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "options": { "${inline}": [ "${IPatternInfo}" ] }, "duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "useRipgrep": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "useRipgrep": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ this.telemetryService.publicLog('searchResultsShown', { @@ -810,7 +846,9 @@ export class SearchModel extends Disposable { fileCount: this._searchResult.fileCount(), options, duration, - useRipgrep: this._searchQuery.useRipgrep + useRipgrep: this._searchQuery.useRipgrep, + type: stats && stats.type, + scheme }); return completed; } 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 af07196b912..2deab589339 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -560,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: [ @@ -585,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': { @@ -606,20 +606,31 @@ 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."), + }, + 'search.collapseResults': { + type: 'string', + enum: ['auto', 'alwaysCollapse', 'alwaysExpand'], + enumDescriptions: [ + 'Files with less than 10 results are expanded. Others are collapsed.', + '', + '' + ], + default: 'auto', + description: nls.localize('search.collapseAllResults', "Controls whether the search results will be collapsed or expanded."), } } }); @@ -642,3 +653,14 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { }, 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/browser/searchActions.test.ts b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts index 47f5db6be49..8a46a0b3801 100644 --- a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts @@ -129,13 +129,26 @@ suite('Search Actions', () => { function aFileMatch(): FileMatch { let rawMatch: IFileMatch = { resource: URI.file('somepath' + ++counter), - lineMatches: [] + matches: [] }; - return instantiationService.createInstance(FileMatch, null, null, null, rawMatch); + return instantiationService.createInstance(FileMatch, null, null, null, null, rawMatch); } function aMatch(fileMatch: FileMatch): Match { - let match = new Match(fileMatch, 'some match', ++counter, 0, 2); + const line = ++counter; + const range = { + startLineNumber: line, + startColumn: 0, + endLineNumber: line, + endColumn: 2 + }; + let match = new Match(fileMatch, { + preview: { + text: 'some match', + match: range + }, + range + }); fileMatch.add(match); return match; } diff --git a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts index 2c273d2744a..45eb45be630 100644 --- a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts @@ -9,7 +9,7 @@ import uri from 'vs/base/common/uri'; import { Match, FileMatch, SearchResult } from 'vs/workbench/parts/search/common/searchModel'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { SearchDataSource, SearchSorter } from 'vs/workbench/parts/search/browser/searchResultsView'; -import { IFileMatch, ILineMatch } from 'vs/platform/search/common/search'; +import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult } from 'vs/platform/search/common/search'; 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'; @@ -31,9 +31,22 @@ suite('Search - Viewlet', () => { let ds = instantiation.createInstance(SearchDataSource); let result: SearchResult = instantiation.createInstance(SearchResult, null); result.query = { type: 1, folderQueries: [{ folder: uri.parse('file://c:/') }] }; + + const range = { + startLineNumber: 1, + startColumn: 0, + endLineNumber: 1, + endColumn: 1 + }; result.add([{ resource: uri.parse('file:///c:/foo'), - lineMatches: [{ lineNumber: 1, preview: 'bar', offsetAndLengths: [[0, 1]] }] + matches: [{ + preview: { + text: 'bar', + match: range + }, + range + }] }]); let fileMatch = result.matches()[0]; @@ -41,7 +54,7 @@ suite('Search - Viewlet', () => { assert.equal(ds.getId(null, result), 'root'); assert.equal(ds.getId(null, fileMatch), 'file:///c%3A/foo'); - assert.equal(ds.getId(null, lineMatch), 'file:///c%3A/foo>1>0b'); + assert.equal(ds.getId(null, lineMatch), 'file:///c%3A/foo>[2,1 -> 2,2]b'); assert(!ds.hasChildren(null, 'foo')); assert(ds.hasChildren(null, result)); @@ -53,9 +66,9 @@ suite('Search - Viewlet', () => { let fileMatch1 = aFileMatch('C:\\foo'); let fileMatch2 = aFileMatch('C:\\with\\path'); let fileMatch3 = aFileMatch('C:\\with\\path\\foo'); - let lineMatch1 = new Match(fileMatch1, 'bar', 1, 1, 1); - let lineMatch2 = new Match(fileMatch1, 'bar', 2, 1, 1); - let lineMatch3 = new Match(fileMatch1, 'bar', 2, 1, 1); + let lineMatch1 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(0, 1, 1))); + let lineMatch2 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1))); + let lineMatch3 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1))); let s = new SearchSorter(); @@ -69,12 +82,12 @@ suite('Search - Viewlet', () => { assert(s.compare(null, lineMatch2, lineMatch3) === 0); }); - function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ILineMatch[]): FileMatch { + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch { let rawMatch: IFileMatch = { resource: uri.file('C:\\' + path), - lineMatches: lineMatches + matches: lineMatches }; - return instantiation.createInstance(FileMatch, null, null, searchResult, rawMatch); + return instantiation.createInstance(FileMatch, null, null, null, searchResult, rawMatch); } function stubModelService(instantiationService: TestInstantiationService): IModelService { 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 c3e961993fc..82ef9f6b2fa 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,10 +37,11 @@ suite('QueryBuilder', () => { mockConfigService = new TestConfigurationService(); mockConfigService.setUserConfiguration('search', DEFAULT_USER_CONFIG); + mockConfigService.setUserConfiguration('editor', DEFAULT_EDITOR_CONFIG); instantiationService.stub(IConfigurationService, mockConfigService); mockContextService = new TestContextService(); - mockWorkspace = new Workspace('workspace', 'workspace', toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }])); + mockWorkspace = new Workspace('workspace', toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }])); mockContextService.setWorkspace(mockWorkspace); instantiationService.stub(IWorkspaceContextService, mockContextService); 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 589973b27a6..56125398146 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -16,7 +16,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IFileMatch, IFolderQuery, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, IUncachedSearchStats } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchResult, TextSearchResult, OneLineRange } 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'; @@ -41,21 +41,25 @@ const nullEvent = new class { } }; +const lineOneRange = new OneLineRange(1, 0, 1); suite('SearchModel', () => { let instantiationService: TestInstantiationService; let restoreStubs: sinon.SinonStub[]; - const testSearchStats: IUncachedSearchStats = { + const testSearchStats: IFileSearchStats = { fromCache: false, - resultCount: 4, - traversal: 'node', - errors: [], - fileWalkStartTime: 0, - fileWalkResultTime: 1, - directoriesWalked: 2, - filesWalked: 3 + resultCount: 1, + type: 'searchProcess', + detailStats: { + traversal: 'node', + fileWalkTime: 0, + cmdTime: 0, + cmdResultCount: 0, + directoriesWalked: 2, + filesWalked: 3 + } }; const folderQueries: IFolderQuery[] = [ @@ -101,7 +105,11 @@ suite('SearchModel', () => { } 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'))]; + let results = [ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + aRawMatch('file://c:/2', new TextSearchResult('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -127,7 +135,12 @@ 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'))]; + let results = [ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + aRawMatch('file://c:/2', + new TextSearchResult('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -165,7 +178,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, 'publicLog', target1); instantiationService.stub(ISearchService, searchServiceWithResults( - [aRawMatch('file://c:/1', aLineMatch('some preview'))], + [aRawMatch('file://c:/1', new TextSearchResult('some preview', lineOneRange))], { results: [], stats: testSearchStats })); let testObject = instantiationService.createInstance(SearchModel); @@ -226,7 +239,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'))]; + let results = [ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + aRawMatch('file://c:/2', + new TextSearchResult('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); @@ -251,7 +269,10 @@ 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]]))]; + let results = [ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -278,12 +299,8 @@ suite('SearchModel', () => { assert.equal('helloe', match.replaceString); }); - function aRawMatch(resource: string, ...lineMatches: ILineMatch[]): IFileMatch { - return { resource: URI.parse(resource), lineMatches }; - } - - function aLineMatch(preview: string, lineNumber: number = 1, offsetAndLengths: number[][] = [[0, 1]]): ILineMatch { - return { preview, lineNumber, offsetAndLengths }; + function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch { + return { resource: URI.parse(resource), matches }; } function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub { diff --git a/src/vs/workbench/parts/search/test/common/searchResult.test.ts b/src/vs/workbench/parts/search/test/common/searchResult.test.ts index e9f486bc193..2d324073590 100644 --- a/src/vs/workbench/parts/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchResult.test.ts @@ -9,7 +9,7 @@ import * as sinon from 'sinon'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Match, FileMatch, SearchResult, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; import URI from 'vs/base/common/uri'; -import { IFileMatch, ILineMatch } from 'vs/platform/search/common/search'; +import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { Range } from 'vs/editor/common/core/range'; @@ -19,6 +19,8 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +const lineOneRange = new OneLineRange(1, 0, 1); + suite('SearchResult', () => { let instantiationService: TestInstantiationService; @@ -33,21 +35,17 @@ suite('SearchResult', () => { test('Line Match', function () { let fileMatch = aFileMatch('folder/file.txt', null); - let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3); + let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); assert.equal(lineMatch.text(), 'foo bar'); assert.equal(lineMatch.range().startLineNumber, 2); assert.equal(lineMatch.range().endLineNumber, 2); assert.equal(lineMatch.range().startColumn, 1); assert.equal(lineMatch.range().endColumn, 4); - assert.equal('file:///folder/file.txt>1>0foo', lineMatch.id()); + assert.equal('file:///folder/file.txt>[2,1 -> 2,4]foo', lineMatch.id()); }); test('Line Match - Remove', function () { - let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), ...[{ - preview: 'foo bar', - lineNumber: 1, - offsetAndLengths: [[0, 3]] - }]); + let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); let lineMatch = fileMatch.matches()[0]; fileMatch.remove(lineMatch); assert.equal(fileMatch.matches().length, 0); @@ -66,15 +64,11 @@ suite('SearchResult', () => { }); test('File Match: Select an existing match', function () { - let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ - preview: 'foo', - lineNumber: 1, - offsetAndLengths: [[0, 3]] - }, { - preview: 'bar', - lineNumber: 1, - offsetAndLengths: [[5, 3]] - }]); + let testObject = aFileMatch( + 'folder/file.txt', + aSearchResult(), + new TextSearchResult('foo', new OneLineRange(1, 0, 3)), + new TextSearchResult('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); @@ -82,15 +76,11 @@ suite('SearchResult', () => { }); test('File Match: Select non existing match', function () { - let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ - preview: 'foo', - lineNumber: 1, - offsetAndLengths: [[0, 3]] - }, { - preview: 'bar', - lineNumber: 1, - offsetAndLengths: [[5, 3]] - }]); + let testObject = aFileMatch( + 'folder/file.txt', + aSearchResult(), + new TextSearchResult('foo', new OneLineRange(1, 0, 3)), + new TextSearchResult('bar', new OneLineRange(1, 5, 3))); let target = testObject.matches()[0]; testObject.remove(target); @@ -100,15 +90,11 @@ suite('SearchResult', () => { }); test('File Match: isSelected return true for selected match', function () { - let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ - preview: 'foo', - lineNumber: 1, - offsetAndLengths: [[0, 3]] - }, { - preview: 'bar', - lineNumber: 1, - offsetAndLengths: [[5, 3]] - }]); + let testObject = aFileMatch( + 'folder/file.txt', + aSearchResult(), + new TextSearchResult('foo', new OneLineRange(1, 0, 3)), + new TextSearchResult('bar', new OneLineRange(1, 5, 3))); let target = testObject.matches()[0]; testObject.setSelectedMatch(target); @@ -116,32 +102,20 @@ suite('SearchResult', () => { }); test('File Match: isSelected return false for un-selected match', function () { - let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ - preview: 'foo', - lineNumber: 1, - offsetAndLengths: [[0, 3]] - }, { - preview: 'bar', - lineNumber: 1, - offsetAndLengths: [[5, 3]] - }]); - + let testObject = aFileMatch('folder/file.txt', + aSearchResult(), + new TextSearchResult('foo', new OneLineRange(1, 0, 3)), + new TextSearchResult('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); - assert.ok(!testObject.isMatchSelected(testObject.matches()[1])); }); test('File Match: unselect', function () { - let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ - preview: 'foo', - lineNumber: 1, - offsetAndLengths: [[0, 3]] - }, { - preview: 'bar', - lineNumber: 1, - offsetAndLengths: [[5, 3]] - }]); - + let testObject = aFileMatch( + 'folder/file.txt', + aSearchResult(), + new TextSearchResult('foo', new OneLineRange(1, 0, 3)), + new TextSearchResult('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); testObject.setSelectedMatch(null); @@ -149,16 +123,11 @@ suite('SearchResult', () => { }); test('File Match: unselect when not selected', function () { - let testObject = aFileMatch('folder/file.txt', aSearchResult(), ...[{ - preview: 'foo', - lineNumber: 1, - offsetAndLengths: [[0, 3]] - }, { - preview: 'bar', - lineNumber: 1, - offsetAndLengths: [[5, 3]] - }]); - + let testObject = aFileMatch( + 'folder/file.txt', + aSearchResult(), + new TextSearchResult('foo', new OneLineRange(1, 0, 3)), + new TextSearchResult('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(null); assert.equal(null, testObject.getSelectedMatch()); @@ -167,7 +136,7 @@ suite('SearchResult', () => { test('Alle Drei Zusammen', function () { let searchResult = instantiationService.createInstance(SearchResult, null); let fileMatch = aFileMatch('far/boo', searchResult); - let lineMatch = new Match(fileMatch, 'foo bar', 1, 0, 3); + let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); assert(lineMatch.parent() === fileMatch); assert(fileMatch.parent() === searchResult); @@ -175,7 +144,10 @@ suite('SearchResult', () => { test('Adding a raw match will add a file match with line matches', function () { let testObject = aSearchResult(); - let target = [aRawMatch('file://c:/', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]), aLineMatch('preview 2'))]; + let target = [aRawMatch('file://c:/', + new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)), + new TextSearchResult('preview 2', lineOneRange))]; testObject.add(target); @@ -200,7 +172,12 @@ suite('SearchResult', () => { test('Adding multiple raw matches', function () { let testObject = aSearchResult(); - let target = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; + let target = [ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + aRawMatch('file://c:/2', + new TextSearchResult('preview 2', lineOneRange))]; testObject.add(target); @@ -228,7 +205,11 @@ suite('SearchResult', () => { let target2 = sinon.spy(); let testObject = aSearchResult(); - testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1')), aRawMatch('file://c:/2', aLineMatch('preview 2'))]); + testObject.add([ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', lineOneRange)), + aRawMatch('file://c:/2', + new TextSearchResult('preview 2', lineOneRange))]); testObject.matches()[0].onDispose(target1); testObject.matches()[1].onDispose(target2); @@ -243,7 +224,9 @@ suite('SearchResult', () => { test('remove triggers change event', function () { let target = sinon.spy(); let testObject = aSearchResult(); - testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + testObject.add([ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', lineOneRange))]); let objectRoRemove = testObject.matches()[0]; testObject.onChange(target); @@ -256,7 +239,9 @@ suite('SearchResult', () => { test('remove triggers change event', function () { let target = sinon.spy(); let testObject = aSearchResult(); - testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + testObject.add([ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', lineOneRange))]); let objectRoRemove = testObject.matches()[0]; testObject.onChange(target); @@ -268,7 +253,9 @@ suite('SearchResult', () => { test('Removing all line matches and adding back will add file back to result', function () { let testObject = aSearchResult(); - testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + testObject.add([ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', lineOneRange))]); let target = testObject.matches()[0]; let matchToRemove = target.matches()[0]; target.remove(matchToRemove); @@ -283,7 +270,9 @@ suite('SearchResult', () => { test('replace should remove the file match', function () { instantiationService.stubPromise(IReplaceService, 'replace', null); let testObject = aSearchResult(); - testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + testObject.add([ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', lineOneRange))]); testObject.replace(testObject.matches()[0]); @@ -294,7 +283,9 @@ suite('SearchResult', () => { let target = sinon.spy(); instantiationService.stubPromise(IReplaceService, 'replace', null); let testObject = aSearchResult(); - testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1'))]); + testObject.add([ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', lineOneRange))]); testObject.onChange(target); let objectRoRemove = testObject.matches()[0]; @@ -307,7 +298,11 @@ suite('SearchResult', () => { test('replaceAll should remove all file matches', function () { instantiationService.stubPromise(IReplaceService, 'replace', null); let testObject = aSearchResult(); - testObject.add([aRawMatch('file://c:/1', aLineMatch('preview 1')), aRawMatch('file://c:/2', aLineMatch('preview 2'))]); + testObject.add([ + aRawMatch('file://c:/1', + new TextSearchResult('preview 1', lineOneRange)), + aRawMatch('file://c:/2', + new TextSearchResult('preview 2', lineOneRange))]); testObject.replaceAll(null); @@ -358,12 +353,12 @@ suite('SearchResult', () => { // lineHasNoDecoration(oneModel, 2); //}); - function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ILineMatch[]): FileMatch { + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch { let rawMatch: IFileMatch = { resource: URI.file('/' + path), - lineMatches: lineMatches + matches: lineMatches }; - return instantiationService.createInstance(FileMatch, null, null, searchResult, rawMatch); + return instantiationService.createInstance(FileMatch, null, null, null, searchResult, rawMatch); } function aSearchResult(): SearchResult { @@ -372,12 +367,8 @@ suite('SearchResult', () => { return searchModel.searchResult; } - function aRawMatch(resource: string, ...lineMatches: ILineMatch[]): IFileMatch { - return { resource: URI.parse(resource), lineMatches }; - } - - function aLineMatch(preview: string, lineNumber: number = 1, offsetAndLengths: number[][] = [[0, 1]]): ILineMatch { - return { preview, lineNumber, offsetAndLengths }; + function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch { + return { resource: URI.parse(resource), matches }; } function stubModelService(instantiationService: TestInstantiationService): IModelService { diff --git a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts index c7398954035..24ad084aa27 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts @@ -9,7 +9,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { writeFile, exists } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { join, basename, dirname, extname } from 'path'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; @@ -18,6 +17,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import URI from 'vs/base/common/uri'; import { ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import { values } from 'vs/base/common/map'; +import { IQuickPickItem, IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; const id = 'workbench.action.openSnippets'; @@ -27,7 +27,7 @@ namespace ISnippetPick { } } -interface ISnippetPick extends IPickOpenEntry { +interface ISnippetPick extends IQuickPickItem { filepath: string; hint?: true; } @@ -179,22 +179,23 @@ async function createLanguageSnippetFile(pick: ISnippetPick) { CommandsRegistry.registerCommand(id, async accessor => { const snippetService = accessor.get(ISnippetsService); - const quickOpenService = accessor.get(IQuickOpenService); + const quickInputService = accessor.get(IQuickInputService); const opener = accessor.get(IOpenerService); const windowService = accessor.get(IWindowService); const modeService = accessor.get(IModeService); const envService = accessor.get(IEnvironmentService); - const { existing, future } = await computePicks(snippetService, envService, modeService); - const newGlobalPick = { label: nls.localize('new.global', "New Global Snippets file...") }; + const picks = await computePicks(snippetService, envService, modeService); + const existing: QuickPickInput[] = picks.existing; + const newGlobalPick = { label: nls.localize('new.global', "New Global Snippets file...") }; if (existing.length > 0) { - existing[0].separator = { label: nls.localize('group.global', "Existing Snippets") }; - newGlobalPick.separator = { border: true, label: nls.localize('new.global.sep', "New Snippets") }; + existing.unshift({ type: 'separator', label: nls.localize('group.global', "Existing Snippets") }); + existing.push({ type: 'separator', label: nls.localize('new.global.sep', "New Snippets") }); } else { - newGlobalPick.separator = { label: nls.localize('new.global.sep', "New Snippets") }; + existing.push({ type: 'separator', label: nls.localize('new.global.sep', "New Snippets") }); } - const pick = await quickOpenService.pick(<(IPickOpenEntry | ISnippetPick)[]>[].concat(existing, newGlobalPick, future), { + const pick = await quickInputService.pick(<(IQuickPickItem | ISnippetPick)[]>[].concat(existing, newGlobalPick, picks.future), { placeHolder: nls.localize('openSnippet.pickLanguage', "Select Snippets File or Create Snippets"), matchOnDescription: true }); diff --git a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts index 581e4854c2f..062036e19b4 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IModeService } from 'vs/editor/common/services/modeService'; import { LanguageId } from 'vs/editor/common/modes'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -16,8 +15,9 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2 import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Snippet } from 'vs/workbench/parts/snippets/electron-browser/snippetsFile'; +import { IQuickPickItem, IQuickInputService, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; -interface ISnippetPick extends IPickOpenEntry { +interface ISnippetPick extends IQuickPickItem { snippet: Snippet; } @@ -71,7 +71,7 @@ class InsertSnippetAction extends EditorAction { return undefined; } - const quickOpenService = accessor.get(IQuickOpenService); + const quickInputService = accessor.get(IQuickInputService); const { lineNumber, column } = editor.getPosition(); let { snippet, name, langId } = Args.fromUser(arg); @@ -116,7 +116,7 @@ class InsertSnippetAction extends EditorAction { } else { // let user pick a snippet const snippets = (await snippetService.getSnippets(languageId)).sort(Snippet.compare); - const picks: ISnippetPick[] = []; + const picks: QuickPickInput[] = []; let prevSnippet: Snippet; for (const snippet of snippets) { const pick: ISnippetPick = { @@ -125,14 +125,14 @@ class InsertSnippetAction extends EditorAction { snippet }; if (!snippet.isFromExtension && !prevSnippet) { - pick.separator = { label: nls.localize('sep.userSnippet', "User Snippets") }; + picks.push({ type: 'separator', label: nls.localize('sep.userSnippet', "User Snippets") }); } else if (snippet.isFromExtension && (!prevSnippet || !prevSnippet.isFromExtension)) { - pick.separator = { label: nls.localize('sep.extSnippet', "Extension Snippets") }; + picks.push({ type: 'separator', label: nls.localize('sep.extSnippet', "Extension Snippets") }); } picks.push(pick); prevSnippet = snippet; } - return quickOpenService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); + return quickInputService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); } }).then(snippet => { if (snippet) { 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 69dc72ef055..6ec87c8ce50 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -76,7 +76,7 @@ namespace schema { const extensionLocation = extension.description.extensionLocation; const snippetLocation = resources.joinPath(extensionLocation, snippet.path); - if (snippetLocation.path.indexOf(extensionLocation.path) !== 0) { + if (!resources.isEqualOrParent(snippetLocation, extensionLocation)) { extension.collector.error(localize( 'invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", 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..5f76b802ea5 --- /dev/null +++ b/src/vs/workbench/parts/splash/electron-browser/partsSplash.contribution.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * 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, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; +import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; +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, editorBackground, foreground } from 'vs/platform/theme/common/colorRegistry'; +import { Color } from 'vs/base/common/color'; + +class PartsSplash { + + private static readonly _splashElementId = 'monaco-parts-splash'; + + private readonly _disposables: IDisposable[] = []; + + private lastBaseTheme: string; + private lastBackground: string; + + constructor( + @IThemeService private readonly _themeService: IThemeService, + @IPartService private readonly _partService: IPartService, + @IStorageService private readonly _storageService: IStorageService, + @ILifecycleService lifecycleService: ILifecycleService, + @IBroadcastService private broadcastService: IBroadcastService + ) { + lifecycleService.when(LifecyclePhase.Running).then(_ => this._removePartsSplash()); + debounceEvent(_partService.onEditorLayout, () => { }, 50)(this._savePartsSplash, this, this._disposables); + } + + dispose(): void { + dispose(this._disposables); + } + + private _savePartsSplash() { + const baseTheme = getThemeTypeSelector(this._themeService.getTheme().type); + const colorInfo = { + foreground: this._getThemeColor(foreground), + editorBackground: this._getThemeColor(editorBackground), + 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, baseTheme }), StorageScope.GLOBAL); + + if (baseTheme !== this.lastBaseTheme || colorInfo.editorBackground !== this.lastBackground) { + // notify the main window on background color changes: the main window sets the background color to new windows + this.lastBaseTheme = baseTheme; + this.lastBackground = colorInfo.editorBackground; + + // the color needs to be in hex + const backgroundColor = this._themeService.getTheme().getColor(editorBackground) || themes.WORKBENCH_BACKGROUND(this._themeService.getTheme()); + this.broadcastService.broadcast({ channel: 'vscode:changeColorTheme', payload: JSON.stringify({ baseTheme, background: Color.Format.CSS.formatHex(backgroundColor) }) }); + } + } + + 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 19bc5120d59..cc62a408918 100644 --- a/src/vs/workbench/parts/stats/node/workspaceStats.ts +++ b/src/vs/workbench/parts/stats/node/workspaceStats.ts @@ -356,11 +356,11 @@ export class WorkspaceStats implements IWorkbenchContribution { private findFolder({ filesToOpen, filesToCreate, filesToDiff }: IWindowConfiguration): URI { if (filesToOpen && filesToOpen.length) { - return this.parentURI(URI.file(filesToOpen[0].filePath)); + return this.parentURI(filesToOpen[0].fileUri); } else if (filesToCreate && filesToCreate.length) { - return this.parentURI(URI.file(filesToCreate[0].filePath)); + return this.parentURI(filesToCreate[0].fileUri); } else if (filesToDiff && filesToDiff.length) { - return this.parentURI(URI.file(filesToDiff[0].filePath)); + return this.parentURI(filesToDiff[0].fileUri); } return undefined; } 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/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index a4e284cf727..0e0288da207 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -57,7 +57,6 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IStatusbarItem, IStatusbarRegistry, Extensions as StatusbarExtensions, StatusbarItemDescriptor, StatusbarAlignment } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; -import { IQuickOpenService, IPickOpenEntry, IPickOpenAction, IPickOpenItem } from 'vs/platform/quickOpen/common/quickOpen'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import Constants from 'vs/workbench/parts/markers/electron-browser/constants'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -90,6 +89,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, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; let tasksCategory = nls.localize('tasksCategory', "Tasks"); @@ -424,7 +424,7 @@ class TaskMap { } } -interface TaskQuickPickEntry extends IPickOpenEntry { +interface TaskQuickPickEntry extends IQuickPickItem { task: Task; } @@ -474,7 +474,7 @@ class TaskService implements ITaskService { @ILifecycleService lifecycleService: ILifecycleService, @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, @@ -871,12 +871,12 @@ class TaskService implements ITaskService { } private attachProblemMatcher(task: ContributedTask | CustomTask): TPromise { - interface ProblemMatcherPickEntry extends IPickOpenEntry { + interface ProblemMatcherPickEntry extends IQuickPickItem { matcher: NamedProblemMatcher; never?: boolean; learnMore?: boolean; } - let entries: ProblemMatcherPickEntry[] = []; + let entries: QuickPickInput[] = []; for (let key of ProblemMatcherRegistry.keys()) { let matcher = ProblemMatcherRegistry.get(key); if (matcher.deprecated) { @@ -894,15 +894,14 @@ class TaskService implements ITaskService { } if (entries.length > 0) { entries = entries.sort((a, b) => a.label.localeCompare(b.label)); - entries[0].separator = { border: true, label: nls.localize('TaskService.associate', 'associate') }; + entries.unshift({ type: 'separator', label: nls.localize('TaskService.associate', 'associate') }); entries.unshift( { label: nls.localize('TaskService.attachProblemMatcher.continueWithout', 'Continue without scanning the task output'), matcher: undefined }, { label: nls.localize('TaskService.attachProblemMatcher.never', 'Never scan the task output'), matcher: undefined, never: true }, { label: nls.localize('TaskService.attachProblemMatcher.learnMoreAbout', 'Learn more about scanning the task output'), matcher: undefined, learnMore: true } ); - return this.quickOpenService.pick(entries, { + return this.quickInputService.pick(entries, { placeHolder: nls.localize('selectProblemMatcher', 'Select for which kind of errors and warnings to scan the task output'), - autoFocus: { autoFocusFirstEntry: true } }).then((selected) => { if (selected) { if (selected.learnMore) { @@ -1073,7 +1072,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) { @@ -1805,32 +1804,13 @@ class TaskService implements ITaskService { } return { label: task._label, description, task }; }; - let taskService = this; - let action = new class extends Action implements IPickOpenAction { - constructor() { - super('configureAction', 'Configure Task', 'quick-open-task-configure', true); + function fillEntries(entries: QuickPickInput[], tasks: Task[], groupLabel: string): void { + if (tasks.length) { + entries.push({ type: 'separator', label: groupLabel }); } - public run(item: IPickOpenItem): TPromise { - let task: Task = item.getPayload(); - taskService.quickOpenService.close(); - if (ContributedTask.is(task)) { - taskService.customize(task, undefined, true); - } else if (CustomTask.is(task)) { - taskService.openConfig(task); - } - return TPromise.as(false); - } - }; - function fillEntries(entries: TaskQuickPickEntry[], tasks: Task[], groupLabel: string, withBorder: boolean = false): void { - let first = true; for (let task of tasks) { let entry: TaskQuickPickEntry = TaskQuickPickEntry(task); - if (first) { - first = false; - entry.separator = { label: groupLabel, border: withBorder }; - } - entry.action = action; - entry.payload = task; + entry.buttons = [{ iconClass: 'quick-open-task-configure', tooltip: nls.localize('configureTask', "Configure Task") }]; entries.push(entry); } } @@ -1868,13 +1848,11 @@ class TaskService implements ITaskService { } } const sorter = this.createSorter(); - let hasRecentlyUsed: boolean = recent.length > 0; fillEntries(entries, recent, nls.localize('recentlyUsed', 'recently used tasks')); configured = configured.sort((a, b) => sorter.compare(a, b)); - let hasConfigured = configured.length > 0; - fillEntries(entries, configured, nls.localize('configured', 'configured tasks'), hasRecentlyUsed); + fillEntries(entries, configured, nls.localize('configured', 'configured tasks')); detected = detected.sort((a, b) => sorter.compare(a, b)); - fillEntries(entries, detected, nls.localize('detected', 'detected tasks'), hasRecentlyUsed || hasConfigured); + fillEntries(entries, detected, nls.localize('detected', 'detected tasks')); } } else { if (sort) { @@ -1894,12 +1872,24 @@ class TaskService implements ITaskService { return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort)); } }; - return this.quickOpenService.pick(_createEntries().then((entries) => { + return this.quickInputService.pick(_createEntries().then((entries) => { if (entries.length === 0 && defaultEntry) { entries.push(defaultEntry); } return entries; - }), { placeHolder, autoFocus: { autoFocusFirstEntry: true }, matchOnDescription: true }).then(entry => entry ? entry.task : undefined); + }), { + placeHolder, + matchOnDescription: true, + onDidTriggerItemButton: context => { + let task = context.item.task; + this.quickInputService.cancel(); + if (ContributedTask.is(task)) { + this.customize(task, undefined, true); + } else if (CustomTask.is(task)) { + this.openConfig(task); + } + } + }).then(entry => entry ? entry.task : undefined); } private showIgnoredFoldersMessage(): TPromise { @@ -2205,7 +2195,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; } @@ -2252,8 +2242,8 @@ class TaskService implements ITaskService { } }; - function isTaskEntry(value: IPickOpenEntry): value is IPickOpenEntry & { task: Task } { - let candidate: IPickOpenEntry & { task: Task } = value as any; + function isTaskEntry(value: IQuickPickItem): value is IQuickPickItem & { task: Task } { + let candidate: IQuickPickItem & { task: Task } = value as any; return candidate && !!candidate.task; } @@ -2265,8 +2255,8 @@ class TaskService implements ITaskService { let openLabel = nls.localize('TaskService.openJsonFile', 'Open tasks.json file'); let entries = TPromise.join(stats).then((stats) => { return taskPromise.then((taskMap) => { - type EntryType = (IPickOpenEntry & { task: Task; }) | (IPickOpenEntry & { folder: IWorkspaceFolder; }); - let entries: EntryType[] = []; + type EntryType = (IQuickPickItem & { task: Task; }) | (IQuickPickItem & { folder: IWorkspaceFolder; }); + let entries: QuickPickInput[] = []; if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { let tasks = taskMap.all(); let needsCreateOrOpen: boolean = true; @@ -2281,7 +2271,10 @@ class TaskService implements ITaskService { } if (needsCreateOrOpen) { let label = stats[0] !== void 0 ? openLabel : createLabel; - entries.push({ label, folder: this.contextService.getWorkspace().folders[0], separator: entries.length > 0 ? { border: true } : undefined }); + if (entries.length) { + entries.push({ type: 'separator' }); + } + entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); } } else { let folders = this.contextService.getWorkspace().folders; @@ -2293,14 +2286,14 @@ class TaskService implements ITaskService { for (let i = 0; i < tasks.length; i++) { let entry: EntryType = { label: tasks[i]._label, task: tasks[i], description: folder.name }; if (i === 0) { - entry.separator = { label: folder.name, border: index > 0 }; + entries.push({ type: 'separator', label: folder.name }); } entries.push(entry); } } else { let label = stats[index] !== void 0 ? openLabel : createLabel; let entry: EntryType = { label, folder: folder }; - entry.separator = { label: folder.name, border: index > 0 }; + entries.push({ type: 'separator', label: folder.name }); entries.push(entry); } index++; @@ -2310,8 +2303,8 @@ class TaskService implements ITaskService { }); }); - this.quickOpenService.pick(entries, - { placeHolder: nls.localize('TaskService.pickTask', 'Select a task to configure'), autoFocus: { autoFocusFirstEntry: true } }). + this.quickInputService.pick(entries, + { placeHolder: nls.localize('TaskService.pickTask', 'Select a task to configure') }). then((selection) => { if (!selection) { return; @@ -2426,8 +2419,8 @@ class TaskService implements ITaskService { } } -MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '1_run', +MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: '2_run', command: { id: 'workbench.action.tasks.runTask', title: nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task...") @@ -2435,8 +2428,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { order: 1 }); -MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '1_run', +MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: '2_run', command: { id: 'workbench.action.tasks.build', title: nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task...") @@ -2445,8 +2438,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { }); // Manage Tasks -MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', +MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: '3_manage', command: { precondition: TASK_RUNNING_STATE, id: 'workbench.action.tasks.showTasks', @@ -2455,8 +2448,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { order: 1 }); -MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', +MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: '3_manage', command: { precondition: TASK_RUNNING_STATE, id: 'workbench.action.tasks.restartTask', @@ -2465,8 +2458,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { order: 2 }); -MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', +MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: '3_manage', command: { precondition: TASK_RUNNING_STATE, id: 'workbench.action.tasks.terminate', @@ -2476,8 +2469,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { }); // Configure Tasks -MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '3_configure', +MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: '4_configure', command: { id: 'workbench.action.tasks.configureTaskRunner', title: nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks...") @@ -2485,8 +2478,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { order: 1 }); -MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '3_configure', +MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: '4_configure', command: { id: 'workbench.action.tasks.configureDefaultBuildTask', title: nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task...") diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 245a8ac3a6e..b7a2c8e5915 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -97,8 +97,6 @@ export interface ITerminalConfiguration { windows: { [key: string]: string }; }; showExitAlert: boolean; - experimentalRestore: boolean; - experimentalTextureCachingStrategy: 'static' | 'dynamic'; } export interface ITerminalConfigHelper { diff --git a/src/vs/workbench/parts/terminal/common/terminalMenu.ts b/src/vs/workbench/parts/terminal/common/terminalMenu.ts index 52498a02f51..873c8c688a5 100644 --- a/src/vs/workbench/parts/terminal/common/terminalMenu.ts +++ b/src/vs/workbench/parts/terminal/common/terminalMenu.ts @@ -16,15 +16,15 @@ export function setupTerminalMenu() { group: '4_panels', command: { id: TERMINAL_COMMAND_ID.TOGGLE, - title: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal") + title: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Terminal") }, order: 3 }); // Manage - const manageGroup = '1_manage'; + const createGroup = '1_create'; MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: manageGroup, + group: createGroup, command: { id: TERMINAL_COMMAND_ID.NEW, title: nls.localize({ key: 'miNewTerminal', comment: ['&& denotes a mnemonic'] }, "&&New Terminal") @@ -32,7 +32,7 @@ export function setupTerminalMenu() { order: 1 }); MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: manageGroup, + group: createGroup, command: { id: TERMINAL_COMMAND_ID.SPLIT, title: nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal"), @@ -41,34 +41,15 @@ export function setupTerminalMenu() { 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 + order: 3 }); MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { group: runGroup, @@ -76,45 +57,6 @@ export function setupTerminalMenu() { 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 2c28f173a59..69b56fe9739 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, 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,8 +68,6 @@ 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(); } @@ -94,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 @@ -139,22 +112,12 @@ 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()); } public getTabLabels(): string[] { - return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title}`); + return this._terminalTabs.filter(tab => tab.terminalInstances.length > 0).map((tab, index) => `${index + 1}: ${tab.title ? tab.title : ''}`); } private _removeTab(tab: ITerminalTab): void { 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 835be4cc86f..9ef058f2994 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -75,12 +75,12 @@ configurationRegistry.registerConfiguration({ 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. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + 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. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + 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' @@ -88,12 +88,12 @@ configurationRegistry.registerConfiguration({ default: [] }, 'terminal.integrated.shell.osx': { - description: 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)."), + 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 macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + 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' @@ -104,12 +104,12 @@ configurationRegistry.registerConfiguration({ default: ['-l'] }, 'terminal.integrated.shell.windows': { - description: 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)."), + 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. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + 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' @@ -137,7 +137,7 @@ configurationRegistry.registerConfiguration({ 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."), + 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 @@ -189,7 +189,7 @@ configurationRegistry.registerConfiguration({ 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 macOS, `false` on other platforms."), + 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 }, @@ -202,7 +202,7 @@ configurationRegistry.registerConfiguration({ 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. 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', @@ -329,7 +329,7 @@ configurationRegistry.registerConfiguration({ ].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 macOS. Set to `null` to delete the environment variable."), + 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'] @@ -337,7 +337,7 @@ configurationRegistry.registerConfiguration({ 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. Set to `null` to delete the environment variable."), + 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'] @@ -345,7 +345,7 @@ configurationRegistry.registerConfiguration({ 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. Set to `null` to delete the environment variable."), + 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'] @@ -356,19 +356,7 @@ configurationRegistry.registerConfiguration({ 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 - }, - 'terminal.integrated.experimentalRestore': { - description: nls.localize('terminal.integrated.experimentalRestore', "Controls 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 or be removed in the future."), - type: 'boolean', - default: false - }, - // TODO: Default to dynamic and remove setting in 1.27 - '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' - }, + } } }); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 25b65e9eafc..d1cafbd699d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -16,8 +16,8 @@ import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IQuickOpenService, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; +import { IQuickInputService, IPickOptions, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ActionBarContributor } from 'vs/workbench/browser/actions'; import { TerminalEntry } from 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -258,7 +258,7 @@ export class CreateNewTerminalAction extends Action { // single root instancePromise = TPromise.as(this.terminalService.createTerminal(undefined, true)); } else { - const options: IPickOptions = { + const options: IPickOptions = { placeHolder: nls.localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal") }; instancePromise = this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]).then(workspace => { @@ -327,7 +327,7 @@ export class SplitTerminalAction extends Action { let pathPromise: TPromise = TPromise.as({}); if (folders.length > 1) { // Only choose a path when there's more than 1 folder - const options: IPickOptions = { + const options: IPickOptions = { placeHolder: nls.localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal") }; pathPromise = this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]).then(workspace => { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index df30323b6a6..dd08c8bf750 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 terminal is now using the fallback renderer.")); }); } } as IPromptChoice, @@ -712,8 +713,6 @@ export class TerminalInstance implements ITerminalInstance { this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper); this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this)); this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)); - this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows); - this._processManager.onProcessData(data => this._onData.fire(data)); if (this._shellLaunchConfig.name) { @@ -733,6 +732,12 @@ export class TerminalInstance implements ITerminalInstance { }); }); } + + // Create the process asynchronously to allow the terminal's container + // to be created so dimensions are accurate + setTimeout(() => { + this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows); + }, 0); } private _onProcessData(data: string): void { @@ -884,6 +889,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 { 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/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index 5500c17791a..5da6bdacde8 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -79,7 +79,11 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { } private _setupTitlePolling() { - this._sendProcessTitle(); + // Send initial timeout async to give event listeners a chance to init + setTimeout(() => { + this._sendProcessTitle(); + }, 0); + // Setup polling setInterval(() => { if (this._currentTitle !== this._ptyProcess.process) { this._sendProcessTitle(); diff --git a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts index 52bc33c6fa5..cac16c5ff22 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts @@ -7,6 +7,7 @@ import { ITerminalChildProcess } from 'vs/workbench/parts/terminal/node/terminal import { Event, Emitter } from 'vs/base/common/event'; import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerminalProcessExtHostProxy { private _disposables: IDisposable[] = []; @@ -32,10 +33,15 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, - @ITerminalService private _terminalService: ITerminalService + @ITerminalService private _terminalService: ITerminalService, + @IExtensionService private readonly _extensionService: IExtensionService ) { - // TODO: Return TPromise indicating success? Teardown if failure? - this._terminalService.requestExtHostProcess(this, shellLaunchConfig, cols, rows); + this._extensionService.whenInstalledExtensionsRegistered().then(() => { + // TODO: MainThreadTerminalService is not ready at this point, fix this + setTimeout(() => { + this._terminalService.requestExtHostProcess(this, shellLaunchConfig, cols, rows); + }, 0); + }); } public dispose(): void { 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 dfa15ae450f..524a0915457 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -13,7 +13,6 @@ import { KeyMod, KeyChord, 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 { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IWorkbenchThemeService, COLOR_THEME_SETTING, ICON_THEME_SETTING, IColorTheme, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -27,6 +26,7 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat import { LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { schemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; export class SelectColorThemeAction extends Action { @@ -36,7 +36,7 @@ export class SelectColorThemeAction extends Action { constructor( id: string, label: string, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, @IViewletService private viewletService: IViewletService, @@ -49,15 +49,18 @@ export class SelectColorThemeAction extends Action { return this.themeService.getColorThemes().then(themes => { const currentTheme = this.themeService.getColorTheme(); - const picks: IPickOpenEntry[] = [].concat( + const picks: QuickPickInput[] = [].concat( toEntries(themes.filter(t => t.type === LIGHT), localize('themes.category.light', "light themes")), - toEntries(themes.filter(t => t.type === DARK), localize('themes.category.dark', "dark themes"), true), - toEntries(themes.filter(t => t.type === HIGH_CONTRAST), localize('themes.category.hc', "high contrast themes"), true), - configurationEntries(this.extensionGalleryService, this.viewletService, 'category:themes', localize('installColorThemes', "Install Additional Color Themes...")) + toEntries(themes.filter(t => t.type === DARK), localize('themes.category.dark', "dark themes")), + toEntries(themes.filter(t => t.type === HIGH_CONTRAST), localize('themes.category.hc', "high contrast themes")), + configurationEntries(this.extensionGalleryService, localize('installColorThemes', "Install Additional Color Themes...")) ); const selectTheme = (theme, applyTheme: boolean) => { if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry + if (applyTheme) { + openExtensionViewlet(this.viewletService, 'category:themes'); + } theme = currentTheme; } let target = null; @@ -75,12 +78,12 @@ export class SelectColorThemeAction extends Action { }; const placeHolder = localize('themes.selectTheme', "Select Color Theme (Up/Down Keys to Preview)"); - const autoFocusIndex = firstIndex(picks, p => p.id === currentTheme.id); + const autoFocusIndex = firstIndex(picks, p => p.type !== 'separator' && p.id === currentTheme.id); const delayer = new Delayer(100); const chooseTheme = theme => delayer.trigger(() => selectTheme(theme || currentTheme, true), 0); const tryTheme = theme => delayer.trigger(() => selectTheme(theme, false)); - return this.quickOpenService.pick(picks, { placeHolder, autoFocus: { autoFocusIndex }, onDidFocus: tryTheme }) + return this.quickInputService.pick(picks, { placeHolder, activeItem: picks[autoFocusIndex], onDidFocus: tryTheme }) .then(chooseTheme); }); } @@ -94,7 +97,7 @@ class SelectIconThemeAction extends Action { constructor( id: string, label: string, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, @IViewletService private viewletService: IViewletService, @@ -108,14 +111,17 @@ class SelectIconThemeAction extends Action { return this.themeService.getFileIconThemes().then(themes => { const currentTheme = this.themeService.getFileIconTheme(); - let picks: IPickOpenEntry[] = [{ id: '', label: localize('noIconThemeLabel', 'None'), description: localize('noIconThemeDesc', 'Disable file icons') }]; + let picks: QuickPickInput[] = [{ id: '', label: localize('noIconThemeLabel', 'None'), description: localize('noIconThemeDesc', 'Disable file icons') }]; picks = picks.concat( toEntries(themes), - configurationEntries(this.extensionGalleryService, this.viewletService, 'tag:icon-theme', localize('installIconThemes', "Install Additional File Icon Themes...")) + configurationEntries(this.extensionGalleryService, localize('installIconThemes', "Install Additional File Icon Themes...")) ); const selectTheme = (theme, applyTheme: boolean) => { if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry + if (applyTheme) { + openExtensionViewlet(this.viewletService, 'tag:icon-theme'); + } theme = currentTheme; } let target = null; @@ -132,39 +138,46 @@ class SelectIconThemeAction extends Action { }; const placeHolder = localize('themes.selectIconTheme', "Select File Icon Theme"); - const autoFocusIndex = firstIndex(picks, p => p.id === currentTheme.id); + const autoFocusIndex = firstIndex(picks, p => p.type !== 'separator' && p.id === currentTheme.id); const delayer = new Delayer(100); const chooseTheme = theme => delayer.trigger(() => selectTheme(theme || currentTheme, true), 0); const tryTheme = theme => delayer.trigger(() => selectTheme(theme, false)); - return this.quickOpenService.pick(picks, { placeHolder, autoFocus: { autoFocusIndex }, onDidFocus: tryTheme }) + return this.quickInputService.pick(picks, { placeHolder, activeItem: picks[autoFocusIndex], onDidFocus: tryTheme }) .then(chooseTheme); }); } } -function configurationEntries(extensionGalleryService: IExtensionGalleryService, viewletService: IViewletService, query: string, label: string): IPickOpenEntry[] { +function configurationEntries(extensionGalleryService: IExtensionGalleryService, label: string): QuickPickInput[] { if (extensionGalleryService.isEnabled()) { - return [{ - id: void 0, - label: label, - separator: { border: true }, - alwaysShow: true, - run: () => viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { - (viewlet).search(query); - viewlet.focus(); - }) - }]; + return [ + { + type: 'separator' + }, + { + id: void 0, + label: label, + alwaysShow: true, + } + ]; } return []; } -function toEntries(themes: (IColorTheme | IFileIconTheme)[], label?: string, border = false) { - const toEntry = theme => { id: theme.id, label: theme.label, description: theme.description }; - const sorter = (t1: IColorTheme, t2: IColorTheme) => t1.label.localeCompare(t2.label); - let entries = themes.map(toEntry).sort(sorter); - if (entries.length > 0 && (label || border)) { - entries[0].separator = { label, border }; +function openExtensionViewlet(viewletService: IViewletService, query: string) { + return viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { + (viewlet).search(query); + viewlet.focus(); + }); +} + +function toEntries(themes: (IColorTheme | IFileIconTheme)[], label?: string) { + const toEntry = theme => { id: theme.id, label: theme.label, description: theme.description }; + const sorter = (t1: IQuickPickItem, t2: IQuickPickItem) => t1.label.localeCompare(t2.label); + let entries: QuickPickInput[] = themes.map(toEntry).sort(sorter); + if (entries.length > 0 && label) { + entries.unshift({ type: 'separator', label }); } return entries; } 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.contribution.ts b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts index a87651e4bfc..4e791267124 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.contribution.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts @@ -13,9 +13,8 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { IGlobalActivityRegistry, GlobalActivityExtensions } from 'vs/workbench/common/activity'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, Win3264BitContribution, WinUserSetupContribution } from './update'; +import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, Win3264BitContribution } from './update'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import product from 'vs/platform/node/product'; const workbench = Registry.as(WorkbenchExtensions.Workbench); @@ -25,10 +24,6 @@ if (platform.isWindows) { if (process.arch === 'ia32') { workbench.registerWorkbenchContribution(Win3264BitContribution, LifecyclePhase.Running); } - - if (product.target !== 'user') { - workbench.registerWorkbenchContribution(WinUserSetupContribution, LifecyclePhase.Running); - } } Registry.as(GlobalActivityExtensions) diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts index 941351830b6..675bea320d2 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -21,7 +21,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IUpdateService, State as UpdateState, StateType, IUpdate, UpdateType } from 'vs/platform/update/common/update'; +import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update'; import * as semver from 'semver'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INotificationService, INotificationHandle } from 'vs/platform/notification/common/notification'; @@ -209,80 +209,6 @@ export class Win3264BitContribution implements IWorkbenchContribution { } } -export class WinUserSetupContribution implements IWorkbenchContribution { - - private static readonly KEY = 'update/win32-usersetup'; - - 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'; - private static readonly INSIDER_URL = 'https://vscode-update.azurewebsites.net/latest/win32-x64-user/insider'; - private static readonly INSIDER_URL_32BIT = 'https://vscode-update.azurewebsites.net/latest/win32-user/insider'; - - // TODO@joao this needs to change to the 1.26 release notes - private static readonly READ_MORE = 'https://aka.ms/vscode-win32-user-setup'; - - private disposables: IDisposable[] = []; - - constructor( - @IStorageService private storageService: IStorageService, - @INotificationService private notificationService: INotificationService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IOpenerService private openerService: IOpenerService, - @IUpdateService private updateService: IUpdateService - ) { - updateService.onStateChange(this.onUpdateStateChange, this, this.disposables); - this.onUpdateStateChange(this.updateService.state); - } - - private onUpdateStateChange(state: UpdateState): void { - if (state.type !== StateType.Idle) { - return; - } - - if (state.updateType !== UpdateType.Setup) { - return; - } - - if (!this.environmentService.isBuilt || this.environmentService.disableUpdates) { - return; - } - - const neverShowAgain = new NeverShowAgain(WinUserSetupContribution.KEY, this.storageService); - - if (!neverShowAgain.shouldShow()) { - return; - } - - const handle = this.notificationService.prompt( - severity.Info, - nls.localize('usersetup', "We recommend switching to our new User Setup distribution of {0} for Windows! Click [here]({1}) to learn more.", product.nameShort, WinUserSetupContribution.READ_MORE), - [ - { - label: nls.localize('downloadnow', "Download"), - run: () => { - const url = product.quality === 'insider' - ? (process.arch === 'ia32' ? WinUserSetupContribution.INSIDER_URL_32BIT : WinUserSetupContribution.INSIDER_URL) - : (process.arch === 'ia32' ? WinUserSetupContribution.STABLE_URL_32BIT : WinUserSetupContribution.STABLE_URL); - - return this.openerService.open(URI.parse(url)); - } - }, - { - label: nls.localize('neveragain', "Don't Show Again"), - isSecondary: true, - run: () => { - neverShowAgain.action.run(handle); - neverShowAgain.action.dispose(); - } - }] - ); - } - - dispose(): void { - this.disposables = dispose(this.disposables); - } -} - class CommandAction extends Action { constructor( @@ -297,7 +223,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'; @@ -525,8 +451,8 @@ export class UpdateContribution implements IGlobalActivity { new CommandAction(UpdateContribution.showCommandsId, nls.localize('commandPalette', "Command Palette..."), this.commandService), new Separator(), new CommandAction(UpdateContribution.openSettingsId, nls.localize('settings', "Settings"), this.commandService), + new CommandAction(UpdateContribution.showExtensionsId, nls.localize('showExtensions', "Extensions"), this.commandService), new CommandAction(UpdateContribution.openKeybindingsId, nls.localize('keyboardShortcuts', "Keyboard Shortcuts"), this.commandService), - new CommandAction(UpdateContribution.showExtensionsId, nls.localize('showExtensions', "Manage Extensions"), this.commandService), new Separator(), new CommandAction(UpdateContribution.openUserSnippets, nls.localize('userSnippets', "User Snippets"), this.commandService), new Separator(), diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts index 82252859c77..24e9760e39d 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 { @@ -244,7 +244,7 @@ export class WebviewEditor extends BaseWebviewEditor { this._webview.initialScrollProgress = input.scrollYPercentage; } - this._webview.state = input.state.state; + this._webview.state = input.webviewState; this._content.setAttribute('aria-flowto', this._webviewContent.id); diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts index 6d48bf36101..b49e917ed70 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts @@ -2,23 +2,58 @@ * 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 = ''; @@ -30,14 +65,15 @@ export class WebviewEditorInput extends EditorInput { private _group?: GroupIdentifier; private _scrollYPercentage: number = 0; private _state: any; - private _webviewState: string | undefined; private _revived: boolean = false; public readonly extensionLocation: URI | undefined; + private readonly _id: number; constructor( public readonly viewType: string, + id: number | undefined, name: string, options: WebviewInputOptions, state: any, @@ -47,6 +83,14 @@ export class WebviewEditorInput extends EditorInput { @IPartService private readonly _partService: IPartService, ) { super(); + + if (typeof id === 'number') { + this._id = id; + WebviewEditorInput.handlePool = Math.max(id, WebviewEditorInput.handlePool) + 1; + } else { + this._id = WebviewEditorInput.handlePool++; + } + this._name = name; this._options = options; this._events = events; @@ -58,6 +102,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 +127,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,8 +150,17 @@ 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; + return other === this || (other instanceof WebviewEditorInput && other._id === this._id); } public get group(): GroupIdentifier | undefined { @@ -130,7 +193,7 @@ export class WebviewEditorInput extends EditorInput { } public get webviewState() { - return this._webviewState; + return this._state.state; } public get options(): WebviewInputOptions { @@ -154,7 +217,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 +232,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; @@ -203,7 +265,7 @@ export class WebviewEditorInput extends EditorInput { }, null, this._webviewDisposables); this._webview.onDidUpdateState(newState => { - this._webviewState = newState; + this._state.state = newState; }, null, this._webviewDisposables); } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts index 094c71d62a2..860efe5d38c 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts @@ -11,14 +11,12 @@ import URI from 'vs/base/common/uri'; interface SerializedWebview { readonly viewType: string; + readonly id: number; 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 { @@ -44,27 +42,23 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { const data: SerializedWebview = { viewType: input.viewType, + id: input.getId(), 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.id, 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..5c5b051b766 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts @@ -35,7 +35,9 @@ export interface IWebviewEditorService { reviveWebview( viewType: string, + id: number, title: string, + iconPath: { light: URI, dark: URI } | undefined, state: any, options: WebviewInputOptions, extensionLocation: URI @@ -106,7 +108,7 @@ export class WebviewEditorService implements IWebviewEditorService { extensionLocation: URI, events: WebviewEvents ): WebviewEditorInput { - const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, title, options, {}, events, extensionLocation, undefined); + const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, undefined, title, options, {}, events, extensionLocation, undefined); this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus }, showOptions.group); return webviewInput; } @@ -125,12 +127,14 @@ export class WebviewEditorService implements IWebviewEditorService { reviveWebview( viewType: string, + id: number, title: string, + iconPath: { light: URI, dark: URI } | undefined, state: any, options: WebviewInputOptions, extensionLocation: URI ): WebviewEditorInput { - const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, title, options, state, {}, extensionLocation, { + const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, id, title, options, state, {}, extensionLocation, { canRevive: (_webview) => { return true; }, @@ -148,7 +152,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 330e72730c4..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 || []) ); } @@ -467,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 257d791463e..62b0d5e5916 100644 --- a/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts +++ b/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts @@ -16,20 +16,26 @@ 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, - @IExperimentService experimentService: IExperimentService, - @IConfigurationService configurationService: IConfigurationService + @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; @@ -45,42 +51,95 @@ export class TelemetryOptOut implements IWorkbenchContribution { } storageService.store(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true); - const optOutUrl = product.telemetryOptOutUrl; - const privacyUrl = product.privacyStatementUrl || product.telemetryOptOutUrl; + this.optOutUrl = product.telemetryOptOutUrl; + this.privacyUrl = product.privacyStatementUrl || product.telemetryOptOutUrl; if (experimentState && experimentState.state === ExperimentState.Run && telemetryService.isOptedIn) { - notificationService.prompt( - Severity.Info, - 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.", privacyUrl), - [ - { - label: localize('telemetryOptOut.OptIn', "Yes, glad to help"), - run: () => { } - }, - { - label: localize('telemetryOptOut.OptOut', "No, thanks"), - run: () => { - configurationService.updateValue('telemetry.enableTelemetry', false); - configurationService.updateValue('telemetry.enableCrashReporter', false); - } - }] - ); - experimentService.markAsCompleted(experimentId); + 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}).", 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); + 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 dff4b7f549b..f2b267da9e9 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -28,18 +28,18 @@ import { IExtensionEnablementService, IExtensionManagementService, IExtensionGal import { used } from 'vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { tildify, getBaseLabel, getPathLabel } from 'vs/base/common/labels'; +import { tildify, getBaseLabel } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; 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, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, 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 { ILabelService } from 'vs/platform/label/common/label'; used(); @@ -54,9 +54,7 @@ export class WelcomePageContribution implements IWorkbenchContribution { @IConfigurationService configurationService: IConfigurationService, @IEditorService editorService: IEditorService, @IBackupFileService backupFileService: IBackupFileService, - @ITelemetryService telemetryService: ITelemetryService, @ILifecycleService lifecycleService: ILifecycleService, - @IStorageService storageService: IStorageService ) { const enabled = isWelcomePageEnabled(configurationService); if (enabled && lifecycleService.startupKind !== StartupKind.ReloadedWindow) { @@ -225,6 +223,7 @@ class WelcomePage { @IWorkspaceContextService private contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService, @IEnvironmentService private environmentService: IEnvironmentService, + @ILabelService private labelService: ILabelService, @INotificationService private notificationService: INotificationService, @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, @@ -256,7 +255,7 @@ class WelcomePage { return this.editorService.openEditor(this.editorInput, { pinned: false }); } - private onReady(container: HTMLElement, recentlyOpened: TPromise<{ files: string[]; workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]; }>, installedExtensions: TPromise): void { + private onReady(container: HTMLElement, recentlyOpened: TPromise<{ files: URI[]; workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]; }>, installedExtensions: TPromise): void { const enabled = isWelcomePageEnabled(this.configurationService); const showOnStartup = container.querySelector('#showOnStartup'); if (enabled) { @@ -281,9 +280,9 @@ class WelcomePage { let resource: URI; if (isSingleFolderWorkspaceIdentifier(workspace)) { resource = workspace; - label = getWorkspaceLabel(workspace, this.environmentService); + label = this.labelService.getWorkspaceLabel(workspace); } else if (isWorkspaceIdentifier(workspace)) { - label = getWorkspaceLabel(workspace, this.environmentService); + label = this.labelService.getWorkspaceLabel(workspace); resource = URI.file(workspace.configPath); } else { label = getBaseLabel(workspace); @@ -305,7 +304,7 @@ class WelcomePage { } parentFolderPath = tildify(parentFolder, this.environmentService.userHome); } else { - parentFolderPath = getPathLabel(resource, this.environmentService); + parentFolderPath = this.labelService.getUriLabel(resource); } 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/walkThroughPart.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts index 6914b7b8cd5..625b1675d72 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts @@ -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/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts index 3af98297733..8148609ee53 100644 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts @@ -39,7 +39,7 @@ const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', crypto.cre class TestBackupFileService extends BackupFileService { constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { - const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); + const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); super(workspaceBackupPath, fileService); } diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index dd3d391644c..2d542bc09cf 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 { ILabelService } from 'vs/platform/label/common/label'; abstract class Recording { @@ -235,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 + @ILabelService private readonly _uriLabelServie: ILabelService ) { this._editor = editor; this._progress = progress || emptyProgressRunner; @@ -342,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._uriLabelServie.getUriLabel(edit.resource, true)); recording.stop(); @@ -372,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 + @ILabelService private readonly _labelService: ILabelService ) { } @@ -404,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._labelService); 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..61a10f303f5 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)) { 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 0ba296dc6db..2576c9e4cfc 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -16,8 +16,8 @@ import { Queue } from 'vs/base/common/async'; import { stat, writeFile } from 'vs/base/node/pfs'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform'; import { IFileService } from 'vs/platform/files/common/files'; -import { isLinux } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration'; @@ -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, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { 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'; @@ -40,7 +40,7 @@ import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/works import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; -import { isEqual, hasToIgnoreCase } from 'vs/base/common/resources'; +import { isEqual } from 'vs/base/common/resources'; export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { @@ -131,7 +131,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { switch (this.getWorkbenchState()) { case WorkbenchState.FOLDER: - return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && isEqual(workspaceIdentifier, this.workspace.folders[0].uri, hasToIgnoreCase(workspaceIdentifier)); + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && isEqual(workspaceIdentifier, this.workspace.folders[0].uri); case WorkbenchState.WORKSPACE: return isWorkspaceIdentifier(workspaceIdentifier) && this.workspace.id === workspaceIdentifier.id; } @@ -340,8 +340,7 @@ 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); - return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath); + return new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); }); } @@ -349,13 +348,25 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat if (folder.scheme === Schemas.file) { return stat(folder.fsPath) .then(workspaceStat => { - const ctime = isLinux ? workspaceStat.ino : workspaceStat.birthtime.getTime(); // On Linux, birthtime is ctime, so we cannot use it! We use the ino instead! + 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(folder.fsPath).update(ctime ? String(ctime) : '').digest('hex'); - return new Workspace(id, getWorkspaceLabel(folder, this.environmentService), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); + return new Workspace(id, 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), toWorkspaceFolders([{ uri: folder.toString() }]), null)); + return TPromise.as(new Workspace(id, toWorkspaceFolders([{ uri: folder.toString() }]), null)); } } @@ -724,7 +735,7 @@ export class DefaultConfigurationExportHelper { const processProperty = (name: string, prop: IConfigurationPropertySchema) => { const propDetails: IExportedConfigurationNode = { name, - description: prop.description, + description: prop.description || prop.markdownDescription || '', default: prop.default, type: prop.type }; @@ -733,8 +744,8 @@ export class DefaultConfigurationExportHelper { propDetails.enum = prop.enum; } - if (prop.enumDescriptions) { - propDetails.enumDescriptions = prop.enumDescriptions; + if (prop.enumDescriptions || prop.markdownEnumDescriptions) { + propDetails.enumDescriptions = prop.enumDescriptions || prop.markdownEnumDescriptions; } settings.push(propDetails); diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts index ec8107bed48..b9fb772b270 100644 --- a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -115,7 +115,7 @@ suite('WorkspaceConfigurationChangeEvent', () => { configurationChangeEvent.change(['window.restoreWindows'], URI.file('folder2')); configurationChangeEvent.telemetryData(ConfigurationTarget.WORKSPACE, {}); - let testObject = new WorkspaceConfigurationChangeEvent(configurationChangeEvent, new Workspace('id', 'name', + let testObject = new WorkspaceConfigurationChangeEvent(configurationChangeEvent, new Workspace('id', [new WorkspaceFolder({ index: 0, name: '1', uri: URI.file('folder1') }), new WorkspaceFolder({ index: 1, name: '2', uri: URI.file('folder2') }), new WorkspaceFolder({ index: 2, name: '3', uri: URI.file('folder3') })])); 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 64e9352f99a..078f5e4fb0d 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 @@ -247,7 +247,7 @@ suite('WorkspaceContextService - Workspace', () => { const addedFolders = [{ uri: URI.file(path.join(workspaceDir, 'd')) }, { uri: URI.file(path.join(workspaceDir, 'c')) }]; return testObject.addFolders(addedFolders) .then(() => { - assert.ok(target.calledOnce); + assert.equal(target.callCount, 1, `Should be called only once but called ${target.callCount} times`); const actual = target.args[0][0]; assert.deepEqual(actual.added.map(r => r.uri.toString()), addedFolders.map(a => a.uri.toString())); assert.deepEqual(actual.removed, []); diff --git a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts index 8832ac16a31..668c8fa75fa 100644 --- a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts @@ -12,12 +12,15 @@ import * as dom from 'vs/base/browser/dom'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { remote, webFrame } from 'electron'; +import { webFrame } from 'electron'; import { unmnemonicLabel } from 'vs/base/common/labels'; import { Event, Emitter } from 'vs/base/common/event'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IContextMenuDelegate, ContextSubMenu, IEvent } from 'vs/base/browser/contextmenu'; +import { IContextMenuDelegate, ContextSubMenu, IContextMenuEvent } from 'vs/base/browser/contextmenu'; +import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; +import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; +import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu'; export class ContextMenuService extends Disposable implements IContextMenuService { @@ -36,12 +39,16 @@ export class ContextMenuService extends Disposable implements IContextMenuServic showContextMenu(delegate: IContextMenuDelegate): void { delegate.getActions().then(actions => { - if (!actions.length) { - return TPromise.as(null); - } + if (actions.length) { + const onHide = once(() => { + if (delegate.onHide) { + delegate.onHide(undefined); + } - return TPromise.timeout(0).then(() => { // https://github.com/Microsoft/vscode/issues/3638 - const menu = this.createMenu(delegate, actions); + this._onDidContextMenu.fire(); + }); + + const menu = this.createMenu(delegate, actions, onHide); const anchor = delegate.getAnchor(); let x: number, y: number; @@ -60,63 +67,74 @@ export class ContextMenuService extends Disposable implements IContextMenuServic x *= zoom; y *= zoom; - menu.popup(remote.getCurrentWindow(), { x: Math.floor(x), y: Math.floor(y), positioningItem: delegate.autoSelectFirstItem ? 0 : void 0 }); - this._onDidContextMenu.fire(); - if (delegate.onHide) { - delegate.onHide(undefined); - } - }); - }); - } - - private createMenu(delegate: IContextMenuDelegate, entries: (IAction | ContextSubMenu)[]): Electron.Menu { - const menu = new remote.Menu(); - const actionRunner = delegate.actionRunner || new ActionRunner(); - - entries.forEach(e => { - if (e instanceof Separator) { - menu.append(new remote.MenuItem({ type: 'separator' })); - } else if (e instanceof ContextSubMenu) { - const submenu = new remote.MenuItem({ - submenu: this.createMenu(delegate, e.entries), - label: unmnemonicLabel(e.label) + popup(menu, { + x: Math.floor(x), + y: Math.floor(y), + positioningItem: delegate.autoSelectFirstItem ? 0 : void 0, + onHide: () => onHide() }); - - menu.append(submenu); - } else { - const options: Electron.MenuItemConstructorOptions = { - label: unmnemonicLabel(e.label), - checked: !!e.checked || !!e.radio, - type: !!e.checked ? 'checkbox' : !!e.radio ? 'radio' : void 0, - enabled: !!e.enabled, - click: (menuItem, win, event) => { - this.runAction(actionRunner, e, delegate, event); - } - }; - - const keybinding = !!delegate.getKeyBinding ? delegate.getKeyBinding(e) : this.keybindingService.lookupKeybinding(e.id); - if (keybinding) { - const electronAccelerator = keybinding.getElectronAccelerator(); - if (electronAccelerator) { - options.accelerator = electronAccelerator; - } else { - const label = keybinding.getLabel(); - if (label) { - options.label = `${options.label} [${label}]`; - } - } - } - - const item = new remote.MenuItem(options); - - menu.append(item); } }); - - return menu; } - private runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IEvent): void { + private createMenu(delegate: IContextMenuDelegate, entries: (IAction | ContextSubMenu)[], onHide: () => void): IContextMenuItem[] { + const actionRunner = delegate.actionRunner || new ActionRunner(); + + return entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide)); + } + + private createMenuItem(delegate: IContextMenuDelegate, entry: IAction | ContextSubMenu, actionRunner: IActionRunner, onHide: () => void): IContextMenuItem { + + // Separator + if (entry instanceof Separator) { + return { type: 'separator' } as IContextMenuItem; + } + + // Submenu + if (entry instanceof ContextSubMenu) { + return { + label: unmnemonicLabel(entry.label), + submenu: this.createMenu(delegate, entry.entries, onHide) + } as IContextMenuItem; + } + + // Normal Menu Item + else { + const item: IContextMenuItem = { + label: unmnemonicLabel(entry.label), + checked: !!entry.checked || !!entry.radio, + type: !!entry.checked ? 'checkbox' : !!entry.radio ? 'radio' : void 0, + enabled: !!entry.enabled, + click: event => { + + // To preserve pre-electron-2.x behaviour, we first trigger + // the onHide callback and then the action. + // Fixes https://github.com/Microsoft/vscode/issues/45601 + onHide(); + + // Run action which will close the menu + this.runAction(actionRunner, entry, delegate, event); + } + }; + + const keybinding = !!delegate.getKeyBinding ? delegate.getKeyBinding(entry) : this.keybindingService.lookupKeybinding(entry.id); + if (keybinding) { + const electronAccelerator = keybinding.getElectronAccelerator(); + if (electronAccelerator) { + item.accelerator = electronAccelerator; + } else { + const label = keybinding.getLabel(); + if (label) { + item.label = `${item.label} [${label}]`; + } + } + } + + return item; + } + } + + private runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IContextMenuEvent): void { /* __GDPR__ "workbenchActionExecuted" : { "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, 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/decorations.ts b/src/vs/workbench/services/decorations/browser/decorations.ts index 6d1e6607375..16ba75cd049 100644 --- a/src/vs/workbench/services/decorations/browser/decorations.ts +++ b/src/vs/workbench/services/decorations/browser/decorations.ts @@ -26,7 +26,7 @@ export interface IDecoration { readonly tooltip: string; readonly labelClassName: string; readonly badgeClassName: string; - update(source?: string, data?: IDecorationData): IDecoration; + update(data: IDecorationData): IDecoration; } export interface IDecorationsProvider { diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 101a0f69d96..d8b1f1cbe89 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -141,25 +141,12 @@ class DecorationStyles { labelClassName, badgeClassName, tooltip, - update: (source, insert) => { + update: (replace) => { let newData = data.slice(); - if (!source) { - // add -> just append - newData.push(insert); - - } else { - // remove/replace -> require a walk - for (let i = 0; i < newData.length; i++) { - if (newData[i].source === source) { - if (!insert) { - // remove - newData.splice(i, 1); - i--; - } else { - // replace - newData[i] = insert; - } - } + for (let i = 0; i < newData.length; i++) { + if (newData[i].source === replace.source) { + // replace + newData[i] = replace; } } return this.asDecoration(newData, onlyChildren); @@ -434,7 +421,7 @@ export class FileDecorationsService implements IDecorationsService { // result, maybe overwrite let result = this._decorationStyles.asDecoration(data, containsChildren); if (overwrite) { - return result.update(overwrite.source, overwrite); + return result.update(overwrite); } else { return result; } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 7b29d31a644..1ee855ef3d5 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 { ILabelService } from 'vs/platform/label/common/label'; 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, + @ILabelService private labelService: ILabelService, @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.labelService.getUriLabel(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, + @ILabelService labelService: ILabelService, @IFileService fileService: IFileService, @IConfigurationService configurationService: IConfigurationService ) { super( editorGroupService, untitledEditorService, - workspaceContextService, instantiationService, - environmentService, + labelService, fileService, configurationService ); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 0b6dd68de6e..45c186eb197 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -8,9 +8,8 @@ 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 { isWindows } from 'vs/base/common/platform'; import { findFreePort } from 'vs/base/node/ports'; import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; @@ -20,24 +19,36 @@ import { ChildProcess, fork } from 'child_process'; import { ipcRenderer as ipc } from 'electron'; import product from 'vs/platform/node/product'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; import { createServer, Server, Socket } from 'net'; import { Event, Emitter, debounceEvent, mapEvent, anyEvent, fromNodeEventEmitter } from 'vs/base/common/event'; -import { IInitData, IWorkspaceData, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; +import { IInitData, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; -import { isEqual } from 'vs/base/common/paths'; +import { isEqual } from 'vs/base/common/resources'; 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, 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'; +import { timeout } from 'vs/base/common/async'; +import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; +import { ILabelService } from 'vs/platform/label/common/label'; +import URI from 'vs/base/common/uri'; -export class ExtensionHostProcessWorker { +export interface IExtensionHostStarter { + readonly onCrashed: Event<[number, string]>; + start(): TPromise; + getInspectPort(): number; + dispose(): void; +} + +export class ExtensionHostProcessWorker implements IExtensionHostStarter { private readonly _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>(); public readonly onCrashed: Event<[number, string]> = this._onCrashed.event; @@ -72,7 +83,8 @@ export class ExtensionHostProcessWorker { @IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @ICrashReporterService private readonly _crashReporterService: ICrashReporterService, - @ILogService private readonly _logService: ILogService + @ILogService private readonly _logService: ILogService, + @ILabelService private readonly _labelService: ILabelService ) { // handle extension host lifecycle a bit special when we know we are developing an extension that runs inside this._isExtensionDevHost = this._environmentService.isExtensionDevelopment; @@ -109,15 +121,15 @@ export class ExtensionHostProcessWorker { // Close Ext Host Window Request if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this._isExtensionDevHost) { - const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + const extensionLocations = broadcast.payload as string[]; + if (Array.isArray(extensionLocations) && extensionLocations.some(uriString => isEqual(this._environmentService.extensionDevelopmentLocationURI, URI.parse(uriString)))) { this._windowService.closeWindow(); } } if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this._isExtensionDevHost) { const extensionPaths = broadcast.payload as string[]; - if (Array.isArray(extensionPaths) && extensionPaths.some(path => isEqual(this._environmentService.extensionDevelopmentPath, path, !isLinux))) { + if (Array.isArray(extensionPaths) && extensionPaths.some(uriString => isEqual(this._environmentService.extensionDevelopmentLocationURI, URI.parse(uriString)))) { this._windowService.reloadWindow(); } } @@ -141,7 +153,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. @@ -170,7 +183,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[] }; @@ -191,13 +204,13 @@ export class ExtensionHostProcessWorker { }, 100); // Print out extension host output - onDebouncedOutput(data => { - const inspectorUrlIndex = !this._environmentService.isBuilt && data.data && data.data.indexOf('chrome-devtools://'); - if (inspectorUrlIndex >= 0) { - console.log(`%c[Extension Host] %cdebugger inspector at ${data.data.substr(inspectorUrlIndex)}`, 'color: blue', 'color: black'); + onDebouncedOutput(output => { + const inspectorUrlMatch = !this._environmentService.isBuilt && output.data && output.data.match(/ws:\/\/([^\s]+)/); + if (inspectorUrlMatch) { + console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color: black'); } else { console.group('Extension Host'); - console.log(data.data, ...data.format); + console.log(output.data, ...output.format); console.groupEnd(); } }); @@ -331,13 +344,13 @@ export class ExtensionHostProcessWorker { const disposable = protocol.onMessage(msg => { - if (msg === 'ready') { + if (isMessageOfType(msg, MessageType.Ready)) { // 1) Extension Host is ready to receive messages, initialize it - this._createExtHostInitData().then(data => protocol.send(JSON.stringify(data))); + this._createExtHostInitData().then(data => protocol.send(Buffer.from(JSON.stringify(data)))); return; } - if (msg === 'initialized') { + if (isMessageOfType(msg, MessageType.Initialized)) { // 2) Extension Host is initialized clearTimeout(handle); @@ -361,16 +374,22 @@ export class ExtensionHostProcessWorker { private _createExtHostInitData(): TPromise { return TPromise.join([this._telemetryService.getTelemetryInfo(), this._extensions]).then(([telemetryInfo, extensionDescriptions]) => { const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: {} }; + const workspace = this._contextService.getWorkspace(); const r: IInitData = { parentPid: process.pid, environment: { isExtensionDevelopmentDebug: this._isExtensionDevDebug, appRoot: this._environmentService.appRoot, appSettingsHome: this._environmentService.appSettingsHome, - extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsPath: this._environmentService.extensionTestsPath }, - workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace(), + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { + configuration: workspace.configuration, + folders: workspace.folders, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + }, extensions: extensionDescriptions, // Send configurations scopes only in development mode. configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData, @@ -461,9 +480,7 @@ export class ExtensionHostProcessWorker { // Send the extension host a request to terminate itself // (graceful termination) - protocol.send({ - type: '__$terminate' - }); + protocol.send(createMessageOfType(MessageType.Terminate)); // Give the extension host 60s, after which we will // try to kill the process and release any resources @@ -504,7 +521,7 @@ export class ExtensionHostProcessWorker { } }); - event.veto(TPromise.timeout(100 /* wait a bit for IPC to get delivered */).then(() => false)); + event.veto(timeout(100 /* wait a bit for IPC to get delivered */).then(() => false)); } } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 45c442c325b..f84965c20b9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -27,8 +27,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { ExtensionHostProcessWorker, IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -39,22 +39,24 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import product from 'vs/platform/node/product'; import * as strings from 'vs/base/common/strings'; -import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { RPCProtocol, IRPCProtocolLogger } 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'; +import { isEqualOrParent } from 'vs/base/common/resources'; 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; } @@ -118,14 +120,14 @@ export class ExtensionHostProcessManager extends Disposable { private readonly _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; }; private _extensionHostProcessRPCProtocol: RPCProtocol; private readonly _extensionHostProcessCustomers: IDisposable[]; - private readonly _extensionHostProcessWorker: ExtensionHostProcessWorker; + private readonly _extensionHostProcessWorker: IExtensionHostStarter; /** * 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, + extensionHostProcessWorker: IExtensionHostStarter, initialActivationEvents: string[], @IInstantiationService private readonly _instantiationService: IInstantiationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @@ -134,7 +136,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 +169,7 @@ export class ExtensionHostProcessManager extends Disposable { errors.onUnexpectedError(err); } } + this._extensionHostProcessProxy = null; super.dispose(); } @@ -178,11 +180,12 @@ export class ExtensionHostProcessManager extends Disposable { private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape { + let logger: IRPCProtocolLogger = null; if (logExtensionHostCommunication || this._environmentService.logExtensionHostCommunication) { - protocol = asLoggingProtocol(protocol); + logger = new RPCLogger(); } - this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol); + this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol, logger); const extHostContext: IExtHostContext = { getProxy: (identifier: ProxyIdentifier): T => this._extensionHostProcessRPCProtocol.getProxy(identifier), set: (identifier: ProxyIdentifier, instance: R): R => this._extensionHostProcessRPCProtocol.set(identifier, instance), @@ -218,6 +221,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; @@ -546,12 +554,23 @@ export class ExtensionService extends Disposable implements IExtensionService { const enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || []; + const notFound = (id: string) => nls.localize('notFound', "Extension \`{0}\` cannot use PROPOSED API as it cannot be found", id); + + if (enableProposedApiFor.length) { + let allProposed = (enableProposedApiFor instanceof Array ? enableProposedApiFor : [enableProposedApiFor]); + allProposed.forEach(id => { + if (!allExtensions.some(description => description.id === id)) { + console.error(notFound(id)); + } + }); + } + const enableProposedApiForAll = !this._environmentService.isBuilt || - (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0) || + (!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong.indexOf('Insiders') >= 0) || (enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args); for (const extension of allExtensions) { - const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && extension.extensionLocation.scheme === Schemas.file && extension.extensionLocation.fsPath.indexOf(this._environmentService.extensionDevelopmentPath) === 0; + const isExtensionUnderDevelopment = this._environmentService.isExtensionDevelopment && isEqualOrParent(extension.extensionLocation, this._environmentService.extensionDevelopmentLocationURI); // Do not disable extensions under development if (!isExtensionUnderDevelopment) { if (disabledExtensions.some(disabled => areSameExtensions(disabled, extension))) { @@ -791,7 +810,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)); @@ -844,13 +863,12 @@ export class ExtensionService extends Disposable implements IExtensionService { ); // Always load developed extensions while extensions development - const developedExtensions = ( - environmentService.isExtensionDevelopment - ? ExtensionScanner.scanOneOrMultipleExtensions( - new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentPath, false, true, translations), log - ) - : TPromise.as([]) - ); + let developedExtensions = TPromise.as([]); + if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) { + developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions( + new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentLocationURI.fsPath, false, true, translations), log + ); + } return TPromise.join([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { const system = extensionDescriptions[0]; @@ -937,20 +955,20 @@ export class ExtensionService extends Disposable implements IExtensionService { } } -function asLoggingProtocol(protocol: IMessagePassingProtocol): IMessagePassingProtocol { +class RPCLogger implements IRPCProtocolLogger { - protocol.onMessage(msg => { - console.log('%c[Extension \u2192 Window]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg); - }); + private _totalIncoming = 0; + private _totalOutgoing = 0; - return { - onMessage: protocol.onMessage, + logIncoming(msgLength: number, str: string, data?: any): void { + this._totalIncoming += msgLength; + console.log(`%c[Extension \u2192 Window]%c[${strings.pad(this._totalIncoming, 7, ' ')}]%c[len: ${strings.pad(msgLength, 5, ' ')}]`, 'color: darkgreen', 'color: grey', 'color: grey', str, data); + } - send(msg: any) { - protocol.send(msg); - console.log('%c[Window \u2192 Extension]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg); - } - }; + logOutgoing(msgLength: number, str: string, data?: any): void { + this._totalOutgoing += msgLength; + console.log(`%c[Window \u2192 Extension]%c[${strings.pad(this._totalOutgoing, 7, ' ')}]%c[len: ${strings.pad(msgLength, 5, ' ')}]`, 'color: darkgreen', 'color: grey', 'color: grey', str, data); + } } interface IExtensionCacheData { diff --git a/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts b/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts index e26546753ff..c0ae970d509 100644 --- a/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts @@ -6,6 +6,9 @@ import { IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; import URI from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { localize } from 'vs/nls'; + +const localExtensionManagementServerAuthority: string = 'vscode-local'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -16,14 +19,14 @@ export class ExtensionManagementServerService implements IExtensionManagementSer constructor( localExtensionManagementService: IExtensionManagementService ) { - this.extensionManagementServers = [{ extensionManagementService: localExtensionManagementService, location: URI.from({ scheme: Schemas.file }) }]; + this.extensionManagementServers = [{ extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }]; } getExtensionManagementServer(location: URI): IExtensionManagementServer { return this.extensionManagementServers[0]; } - getDefaultExtensionManagementServer(): IExtensionManagementServer { + getLocalExtensionManagementServer(): IExtensionManagementServer { return this.extensionManagementServers[0]; } } @@ -41,11 +44,11 @@ export class SingleServerExtensionManagementServerService implements IExtensionM } getExtensionManagementServer(location: URI): IExtensionManagementServer { - location = location.scheme === Schemas.file ? URI.from({ scheme: Schemas.file }) : location; - return this.extensionManagementServers.filter(server => location.authority === server.location.authority)[0]; + const authority = location.scheme === Schemas.file ? localExtensionManagementServerAuthority : location.authority; + return this.extensionManagementServers.filter(server => authority === server.authority)[0]; } - getDefaultExtensionManagementServer(): IExtensionManagementServer { + getLocalExtensionManagementServer(): IExtensionManagementServer { return this.extensionManagementServers[0]; } } \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts index 6830dfa03c8..effdbaf5518 100644 --- a/src/vs/workbench/services/extensions/node/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -6,7 +6,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { LazyPromise } from 'vs/workbench/services/extensions/node/lazyPromise'; import { ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { CharCode } from 'vs/base/common/charCode'; @@ -85,8 +85,15 @@ function transformIncomingURIs(obj: any, transformer: IURITransformer): any { return result; } +export interface IRPCProtocolLogger { + logIncoming(msgLength: number, str: string, data?: any): void; + logOutgoing(msgLength: number, str: string, data?: any): void; +} + export class RPCProtocol implements IRPCProtocol { + private readonly _protocol: IMessagePassingProtocol; + private readonly _logger: IRPCProtocolLogger; private readonly _uriTransformer: IURITransformer; private _isDisposed: boolean; private readonly _locals: { [id: string]: any; }; @@ -94,9 +101,10 @@ export class RPCProtocol implements IRPCProtocol { private _lastMessageId: number; private readonly _invokedHandlers: { [req: string]: TPromise; }; private readonly _pendingRPCReplies: { [msgId: string]: LazyPromise; }; - private readonly _multiplexor: RPCMultiplexer; - constructor(protocol: IMessagePassingProtocol, transformer: IURITransformer = null) { + constructor(protocol: IMessagePassingProtocol, logger: IRPCProtocolLogger = null, transformer: IURITransformer = null) { + this._protocol = protocol; + this._logger = logger; this._uriTransformer = transformer; this._isDisposed = false; this._locals = Object.create(null); @@ -104,7 +112,7 @@ export class RPCProtocol implements IRPCProtocol { this._lastMessageId = 0; this._invokedHandlers = Object.create(null); this._pendingRPCReplies = {}; - this._multiplexor = new RPCMultiplexer(protocol, (msg) => this._receiveOneMessage(msg)); + this._protocol.onMessage((msg) => this._receiveOneMessage(msg)); } public dispose(): void { @@ -159,59 +167,112 @@ export class RPCProtocol implements IRPCProtocol { } } - private _receiveOneMessage(rawmsg: string): void { + private _receiveOneMessage(rawmsg: Buffer): void { if (this._isDisposed) { return; } - let msg = JSON.parse(rawmsg); - if (this._uriTransformer) { - msg = transformIncomingURIs(msg, this._uriTransformer); - } + const msgLength = rawmsg.length; + const buff = MessageBuffer.read(rawmsg, 0); + const messageType = buff.readUInt8(); + const req = buff.readUInt32(); - switch (msg.type) { - case MessageType.Request: - this._receiveRequest(msg); + switch (messageType) { + case MessageType.RequestJSONArgs: { + let { rpcId, method, args } = MessageIO.deserializeRequestJSONArgs(buff); + if (this._uriTransformer) { + args = transformIncomingURIs(args, this._uriTransformer); + } + this._receiveRequest(msgLength, req, rpcId, method, args); break; - case MessageType.Cancel: - this._receiveCancel(msg); + } + case MessageType.RequestMixedArgs: { + let { rpcId, method, args } = MessageIO.deserializeRequestMixedArgs(buff); + if (this._uriTransformer) { + args = transformIncomingURIs(args, this._uriTransformer); + } + this._receiveRequest(msgLength, req, rpcId, method, args); break; - case MessageType.Reply: - this._receiveReply(msg); + } + case MessageType.Cancel: { + this._receiveCancel(msgLength, req); break; - case MessageType.ReplyErr: - this._receiveReplyErr(msg); + } + case MessageType.ReplyOKEmpty: { + this._receiveReply(msgLength, req, undefined); break; + } + case MessageType.ReplyOKJSON: { + let value = MessageIO.deserializeReplyOKJSON(buff); + if (this._uriTransformer) { + value = transformIncomingURIs(value, this._uriTransformer); + } + this._receiveReply(msgLength, req, value); + break; + } + case MessageType.ReplyOKBuffer: { + let value = MessageIO.deserializeReplyOKBuffer(buff); + this._receiveReply(msgLength, req, value); + break; + } + case MessageType.ReplyErrError: { + let err = MessageIO.deserializeReplyErrError(buff); + if (this._uriTransformer) { + err = transformIncomingURIs(err, this._uriTransformer); + } + this._receiveReplyErr(req, err); + break; + } + case MessageType.ReplyErrEmpty: { + this._receiveReplyErr(req, undefined); + break; + } } } - private _receiveRequest(msg: RequestMessage): void { - const callId = msg.id; - const proxyId = msg.proxyId; + private _receiveRequest(msgLength: number, req: number, rpcId: string, method: string, args: any[]): void { + if (this._logger) { + this._logger.logIncoming(msgLength, `receiveRequest ${req}, ${rpcId}.${method}:`, args); + } + const callId = String(req); - this._invokedHandlers[callId] = this._invokeHandler(proxyId, msg.method, msg.args); + this._invokedHandlers[callId] = this._invokeHandler(rpcId, method, args); this._invokedHandlers[callId].then((r) => { delete this._invokedHandlers[callId]; if (this._uriTransformer) { r = transformOutgoingURIs(r, this._uriTransformer); } - this._multiplexor.send(MessageFactory.replyOK(callId, r)); + const msg = MessageIO.serializeReplyOK(req, r); + if (this._logger) { + this._logger.logOutgoing(msg.byteLength, `replyOK ${req}:`, r); + } + this._protocol.send(msg); }, (err) => { delete this._invokedHandlers[callId]; - this._multiplexor.send(MessageFactory.replyErr(callId, err)); + const msg = MessageIO.serializeReplyErr(req, err); + if (this._logger) { + this._logger.logOutgoing(msg.byteLength, `replyErr ${req}:`, err); + } + this._protocol.send(msg); }); } - private _receiveCancel(msg: CancelMessage): void { - const callId = msg.id; + private _receiveCancel(msgLength: number, req: number): void { + if (this._logger) { + this._logger.logIncoming(msgLength, `receiveCancel ${req}`); + } + const callId = String(req); if (this._invokedHandlers[callId]) { this._invokedHandlers[callId].cancel(); } } - private _receiveReply(msg: ReplyMessage): void { - const callId = msg.id; + private _receiveReply(msgLength: number, req: number, value: any): void { + if (this._logger) { + this._logger.logIncoming(msgLength, `receiveReply ${req}:`, value); + } + const callId = String(req); if (!this._pendingRPCReplies.hasOwnProperty(callId)) { return; } @@ -219,11 +280,11 @@ export class RPCProtocol implements IRPCProtocol { const pendingReply = this._pendingRPCReplies[callId]; delete this._pendingRPCReplies[callId]; - pendingReply.resolveOk(msg.res); + pendingReply.resolveOk(value); } - private _receiveReplyErr(msg: ReplyErrMessage): void { - const callId = msg.id; + private _receiveReplyErr(req: number, value: any): void { + const callId = String(req); if (!this._pendingRPCReplies.hasOwnProperty(callId)) { return; } @@ -232,11 +293,11 @@ export class RPCProtocol implements IRPCProtocol { delete this._pendingRPCReplies[callId]; let err: Error = null; - if (msg.err && msg.err.$isError) { + if (value && value.$isError) { err = new Error(); - err.name = msg.err.name; - err.message = msg.err.message; - err.stack = msg.err.stack; + err.name = value.name; + err.message = value.message; + err.stack = value.stack; } pendingReply.resolveErr(err); } @@ -266,110 +327,347 @@ export class RPCProtocol implements IRPCProtocol { return TPromise.wrapError(errors.canceled()); } - const callId = String(++this._lastMessageId); + const req = ++this._lastMessageId; + const callId = String(req); const result = new LazyPromise(() => { - this._multiplexor.send(MessageFactory.cancel(callId)); + const msg = MessageIO.serializeCancel(req); + if (this._logger) { + this._logger.logOutgoing(msg.byteLength, `cancel ${req}`); + } + this._protocol.send(MessageIO.serializeCancel(req)); }); this._pendingRPCReplies[callId] = result; if (this._uriTransformer) { args = transformOutgoingURIs(args, this._uriTransformer); } - this._multiplexor.send(MessageFactory.request(callId, proxyId, methodName, args)); + const msg = MessageIO.serializeRequest(req, proxyId, methodName, args); + if (this._logger) { + this._logger.logOutgoing(msg.byteLength, `request ${req}: ${proxyId}.${methodName}:`, args); + } + this._protocol.send(msg); return result; } } -/** - * Sends/Receives multiple messages in one go: - * - multiple messages to be sent from one stack get sent in bulk at `process.nextTick`. - * - each incoming message is handled in a separate `process.nextTick`. - */ -class RPCMultiplexer { +class MessageBuffer { - private readonly _protocol: IMessagePassingProtocol; - private readonly _sendAccumulatedBound: () => void; + public static alloc(type: MessageType, req: number, messageSize: number): MessageBuffer { + let result = new MessageBuffer(Buffer.allocUnsafe(messageSize + 1 /* type */ + 4 /* req */), 0); + result.writeUInt8(type); + result.writeUInt32(req); + return result; + } - private _messagesToSend: string[]; + public static read(buff: Buffer, offset: number): MessageBuffer { + return new MessageBuffer(buff, offset); + } - constructor(protocol: IMessagePassingProtocol, onMessage: (msg: string) => void) { - this._protocol = protocol; - this._sendAccumulatedBound = this._sendAccumulated.bind(this); + private _buff: Buffer; + private _offset: number; - this._messagesToSend = []; + public get buffer(): Buffer { + return this._buff; + } - this._protocol.onMessage(data => { - for (let i = 0, len = data.length; i < len; i++) { - onMessage(data[i]); + private constructor(buff: Buffer, offset: number) { + this._buff = buff; + this._offset = offset; + } + + public writeUInt8(n: number): void { + this._buff.writeUInt8(n, this._offset, true); this._offset += 1; + } + + public readUInt8(): number { + const n = this._buff.readUInt8(this._offset, true); this._offset += 1; + return n; + } + + public writeUInt32(n: number): void { + this._buff.writeUInt32BE(n, this._offset, true); this._offset += 4; + } + + public readUInt32(): number { + const n = this._buff.readUInt32BE(this._offset, true); this._offset += 4; + return n; + } + + public static sizeShortString(str: string, strByteLength: number): number { + return 1 /* string length */ + strByteLength /* actual string */; + } + + public writeShortString(str: string, strByteLength: number): void { + this._buff.writeUInt8(strByteLength, this._offset, true); this._offset += 1; + this._buff.write(str, this._offset, strByteLength, 'utf8'); this._offset += strByteLength; + } + + public readShortString(): string { + const strLength = this._buff.readUInt8(this._offset, true); this._offset += 1; + const str = this._buff.toString('utf8', this._offset, this._offset + strLength); this._offset += strLength; + return str; + } + + public static sizeLongString(str: string, strByteLength: number): number { + return 4 /* string length */ + strByteLength /* actual string */; + } + + public writeLongString(str: string, strByteLength: number): void { + this._buff.writeUInt32LE(strByteLength, this._offset, true); this._offset += 4; + this._buff.write(str, this._offset, strByteLength, 'utf8'); this._offset += strByteLength; + } + + public readLongString(): string { + const strLength = this._buff.readUInt32LE(this._offset, true); this._offset += 4; + const str = this._buff.toString('utf8', this._offset, this._offset + strLength); this._offset += strLength; + return str; + } + + public static sizeBuffer(buff: Buffer, buffByteLength: number): number { + return 4 /* buffer length */ + buffByteLength /* actual buffer */; + } + + public writeBuffer(buff: Buffer, buffByteLength: number): void { + this._buff.writeUInt32LE(buffByteLength, this._offset, true); this._offset += 4; + buff.copy(this._buff, this._offset); this._offset += buffByteLength; + } + + public readBuffer(): Buffer { + const buffLength = this._buff.readUInt32LE(this._offset, true); this._offset += 4; + const buff = this._buff.slice(this._offset, this._offset + buffLength); this._offset += buffLength; + return buff; + } + + public static sizeMixedArray(arr: (string | Buffer)[], arrLengths: number[]): number { + let size = 0; + size += 1; // arr length + for (let i = 0, len = arr.length; i < len; i++) { + const el = arr[i]; + const elLength = arrLengths[i]; + size += 1; // arg type + if (typeof el === 'string') { + size += this.sizeLongString(el, elLength); + } else { + size += this.sizeBuffer(el, elLength); } - }); - } - - private _sendAccumulated(): void { - const tmp = this._messagesToSend; - this._messagesToSend = []; - this._protocol.send(tmp); - } - - public send(msg: string): void { - if (this._messagesToSend.length === 0) { - process.nextTick(this._sendAccumulatedBound); } - this._messagesToSend.push(msg); + return size; + } + + public writeMixedArray(arr: (string | Buffer)[], arrLengths: number[]): void { + this._buff.writeUInt8(arr.length, this._offset, true); this._offset += 1; + for (let i = 0, len = arr.length; i < len; i++) { + const el = arr[i]; + const elLength = arrLengths[i]; + if (typeof el === 'string') { + this.writeUInt8(ArgType.String); + this.writeLongString(el, elLength); + } else { + this.writeUInt8(ArgType.Buffer); + this.writeBuffer(el, elLength); + } + } + } + + public readMixedArray(): (string | Buffer)[] { + const arrLen = this._buff.readUInt8(this._offset, true); this._offset += 1; + let arr: (string | Buffer)[] = new Array(arrLen); + for (let i = 0; i < arrLen; i++) { + const argType = this.readUInt8(); + if (argType === ArgType.String) { + arr[i] = this.readLongString(); + } else { + arr[i] = this.readBuffer(); + } + } + return arr; } } -class MessageFactory { - public static cancel(req: string): string { - return `{"type":${MessageType.Cancel},"id":"${req}"}`; +class MessageIO { + + private static _arrayContainsBuffer(arr: any[]): boolean { + for (let i = 0, len = arr.length; i < len; i++) { + if (Buffer.isBuffer(arr[i])) { + return true; + } + } + return false; } - public static request(req: string, rpcId: string, method: string, args: any[]): string { - return `{"type":${MessageType.Request},"id":"${req}","proxyId":"${rpcId}","method":"${method}","args":${JSON.stringify(args)}}`; + public static serializeRequest(req: number, rpcId: string, method: string, args: any[]): Buffer { + if (this._arrayContainsBuffer(args)) { + let massagedArgs: (string | Buffer)[] = new Array(args.length); + let argsLengths: number[] = new Array(args.length); + for (let i = 0, len = args.length; i < len; i++) { + const arg = args[i]; + if (Buffer.isBuffer(arg)) { + massagedArgs[i] = arg; + argsLengths[i] = arg.byteLength; + } else { + massagedArgs[i] = JSON.stringify(arg); + argsLengths[i] = Buffer.byteLength(massagedArgs[i], 'utf8'); + } + } + return this._requestMixedArgs(req, rpcId, method, massagedArgs, argsLengths); + } + return this._requestJSONArgs(req, rpcId, method, JSON.stringify(args)); } - public static replyOK(req: string, res: any): string { + private static _requestJSONArgs(req: number, rpcId: string, method: string, args: string): Buffer { + const rpcIdByteLength = Buffer.byteLength(rpcId, 'utf8'); + const methodByteLength = Buffer.byteLength(method, 'utf8'); + const argsByteLength = Buffer.byteLength(args, 'utf8'); + + let len = 0; + len += MessageBuffer.sizeShortString(rpcId, rpcIdByteLength); + len += MessageBuffer.sizeShortString(method, methodByteLength); + len += MessageBuffer.sizeLongString(args, argsByteLength); + + let result = MessageBuffer.alloc(MessageType.RequestJSONArgs, req, len); + result.writeShortString(rpcId, rpcIdByteLength); + result.writeShortString(method, methodByteLength); + result.writeLongString(args, argsByteLength); + return result.buffer; + } + + public static deserializeRequestJSONArgs(buff: MessageBuffer): { rpcId: string; method: string; args: any[]; } { + const rpcId = buff.readShortString(); + const method = buff.readShortString(); + const args = buff.readLongString(); + return { + rpcId: rpcId, + method: method, + args: JSON.parse(args) + }; + } + + private static _requestMixedArgs(req: number, rpcId: string, method: string, args: (string | Buffer)[], argsLengths: number[]): Buffer { + const rpcIdByteLength = Buffer.byteLength(rpcId, 'utf8'); + const methodByteLength = Buffer.byteLength(method, 'utf8'); + + let len = 0; + len += MessageBuffer.sizeShortString(rpcId, rpcIdByteLength); + len += MessageBuffer.sizeShortString(method, methodByteLength); + len += MessageBuffer.sizeMixedArray(args, argsLengths); + + let result = MessageBuffer.alloc(MessageType.RequestMixedArgs, req, len); + result.writeShortString(rpcId, rpcIdByteLength); + result.writeShortString(method, methodByteLength); + result.writeMixedArray(args, argsLengths); + return result.buffer; + } + + public static deserializeRequestMixedArgs(buff: MessageBuffer): { rpcId: string; method: string; args: any[]; } { + const rpcId = buff.readShortString(); + const method = buff.readShortString(); + const rawargs = buff.readMixedArray(); + const args: any[] = new Array(rawargs.length); + for (let i = 0, len = rawargs.length; i < len; i++) { + const rawarg = rawargs[i]; + if (typeof rawarg === 'string') { + args[i] = JSON.parse(rawarg); + } else { + args[i] = rawarg; + } + } + return { + rpcId: rpcId, + method: method, + args: args + }; + } + + public static serializeCancel(req: number): Buffer { + return MessageBuffer.alloc(MessageType.Cancel, req, 0).buffer; + } + + public static serializeReplyOK(req: number, res: any): Buffer { if (typeof res === 'undefined') { - return `{"type":${MessageType.Reply},"id":"${req}"}`; + return this._serializeReplyOKEmpty(req); } - return `{"type":${MessageType.Reply},"id":"${req}","res":${JSON.stringify(res)}}`; + if (Buffer.isBuffer(res)) { + return this._serializeReplyOKBuffer(req, res); + } + return this._serializeReplyOKJSON(req, JSON.stringify(res)); } - public static replyErr(req: string, err: any): string { + private static _serializeReplyOKEmpty(req: number): Buffer { + return MessageBuffer.alloc(MessageType.ReplyOKEmpty, req, 0).buffer; + } + + private static _serializeReplyOKBuffer(req: number, res: Buffer): Buffer { + const resByteLength = res.byteLength; + + let len = 0; + len += MessageBuffer.sizeBuffer(res, resByteLength); + + let result = MessageBuffer.alloc(MessageType.ReplyOKBuffer, req, len); + result.writeBuffer(res, resByteLength); + return result.buffer; + } + + public static deserializeReplyOKBuffer(buff: MessageBuffer): Buffer { + return buff.readBuffer(); + } + + private static _serializeReplyOKJSON(req: number, res: string): Buffer { + const resByteLength = Buffer.byteLength(res, 'utf8'); + + let len = 0; + len += MessageBuffer.sizeLongString(res, resByteLength); + + let result = MessageBuffer.alloc(MessageType.ReplyOKJSON, req, len); + result.writeLongString(res, resByteLength); + return result.buffer; + } + + public static deserializeReplyOKJSON(buff: MessageBuffer): any { + const res = buff.readLongString(); + return JSON.parse(res); + } + + public static serializeReplyErr(req: number, err: any): Buffer { if (err instanceof Error) { - return `{"type":${MessageType.ReplyErr},"id":"${req}","err":${JSON.stringify(errors.transformErrorForSerialization(err))}}`; + return this._serializeReplyErrEror(req, err); } - return `{"type":${MessageType.ReplyErr},"id":"${req}","err":null}`; + return this._serializeReplyErrEmpty(req); + } + + private static _serializeReplyErrEror(req: number, _err: Error): Buffer { + const err = JSON.stringify(errors.transformErrorForSerialization(_err)); + const errByteLength = Buffer.byteLength(err, 'utf8'); + + let len = 0; + len += MessageBuffer.sizeLongString(err, errByteLength); + + let result = MessageBuffer.alloc(MessageType.ReplyErrError, req, len); + result.writeLongString(err, errByteLength); + return result.buffer; + } + + public static deserializeReplyErrError(buff: MessageBuffer): Error { + const err = buff.readLongString(); + return JSON.parse(err); + } + + private static _serializeReplyErrEmpty(req: number): Buffer { + return MessageBuffer.alloc(MessageType.ReplyErrEmpty, req, 0).buffer; } } const enum MessageType { - Request = 1, - Cancel = 2, - Reply = 3, - ReplyErr = 4 + RequestJSONArgs = 1, + RequestMixedArgs = 2, + Cancel = 3, + ReplyOKEmpty = 4, + ReplyOKBuffer = 5, + ReplyOKJSON = 6, + ReplyErrError = 7, + ReplyErrEmpty = 8, } -class RequestMessage { - type: MessageType.Request; - id: string; - proxyId: string; - method: string; - args: any[]; +const enum ArgType { + String = 1, + Buffer = 2 } -class CancelMessage { - type: MessageType.Cancel; - id: string; -} -class ReplyMessage { - type: MessageType.Reply; - id: string; - res: any; -} -class ReplyErrMessage { - type: MessageType.ReplyErr; - id: string; - err: errors.SerializedError; -} - -type RPCMessage = RequestMessage | CancelMessage | ReplyMessage | ReplyErrMessage; diff --git a/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts new file mode 100644 index 00000000000..602a7d37d62 --- /dev/null +++ b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; +import { Event, Emitter } from 'vs/base/common/event'; +import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +import { TPromise } from 'vs/base/common/winjs.base'; + +suite('RPCProtocol', () => { + + class MessagePassingProtocol implements IMessagePassingProtocol { + private _pair: MessagePassingProtocol; + + private readonly _onMessage: Emitter = new Emitter(); + public readonly onMessage: Event = this._onMessage.event; + + public setPair(other: MessagePassingProtocol) { + this._pair = other; + } + + public send(buffer: Buffer): void { + process.nextTick(() => { + this._pair._onMessage.fire(buffer); + }); + } + } + + let delegate: (a1: any, a2: any) => any; + let bProxy: BClass; + class BClass { + $m(a1: any, a2: any): TPromise { + return TPromise.as(delegate.call(null, a1, a2)); + } + } + + setup(() => { + let a_protocol = new MessagePassingProtocol(); + let b_protocol = new MessagePassingProtocol(); + a_protocol.setPair(b_protocol); + b_protocol.setPair(a_protocol); + + let A = new RPCProtocol(a_protocol); + let B = new RPCProtocol(b_protocol); + + delegate = null; + + const bIdentifier = new ProxyIdentifier(false, 'bb'); + const bInstance = new BClass(); + B.set(bIdentifier, bInstance); + bProxy = A.getProxy(bIdentifier); + }); + + test('simple call', function (done) { + delegate = (a1: number, a2: number) => a1 + a2; + bProxy.$m(4, 1).then((res: number) => { + assert.equal(res, 5); + done(null); + }, done); + }); + + test('simple call without result', function (done) { + delegate = (a1: number, a2: number) => { }; + bProxy.$m(4, 1).then((res: number) => { + assert.equal(res, undefined); + done(null); + }, done); + }); + + test('passing buffer as argument', function (done) { + delegate = (a1: Buffer, a2: number) => { + assert.ok(Buffer.isBuffer(a1)); + return a1[a2]; + }; + let b = Buffer.allocUnsafe(4); + b[0] = 1; + b[1] = 2; + b[2] = 3; + b[3] = 4; + bProxy.$m(b, 2).then((res: number) => { + assert.equal(res, 3); + done(null); + }, done); + }); + + test('returning a buffer', function (done) { + delegate = (a1: number, a2: number) => { + let b = Buffer.allocUnsafe(4); + b[0] = 1; + b[1] = 2; + b[2] = 3; + b[3] = 4; + return b; + }; + bProxy.$m(4, 1).then((res: Buffer) => { + assert.ok(Buffer.isBuffer(res)); + assert.equal(res[0], 1); + assert.equal(res[1], 2); + assert.equal(res[2], 3); + assert.equal(res[3], 4); + done(null); + }, done); + }); + + test('cancelling a call', function () { + delegate = (a1: number, a2: number) => a1 + a2; + let p = bProxy.$m(4, 1); + p.then((res: number) => { + assert.fail('should not receive result'); + }); + p.cancel(); + }); + + test('throwing an error', function (done) { + delegate = (a1: number, a2: number) => { + throw new Error(`nope`); + }; + bProxy.$m(4, 1).then((res) => { + assert.fail('unexpected'); + done(null); + }, (err) => { + assert.equal(err.message, 'nope'); + done(null); + }); + }); + + test('error promise', function (done) { + delegate = (a1: number, a2: number) => { + return TPromise.wrapError(undefined); + }; + bProxy.$m(4, 1).then((res) => { + assert.fail('unexpected'); + done(null); + }, (err) => { + assert.equal(err, undefined); + done(null); + }); + }); +}); diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index b91d4371d27..ac357fc3700 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -607,22 +607,23 @@ export class FileService extends Disposable implements IFileService { return addBomPromise.then(addBom => { // 4.) set contents and resolve - return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite).then(void 0, error => { - if (!exists || error.code !== 'EPERM' || !isWindows) { - return TPromise.wrapError(error); - } + if (!exists || !isWindows) { + return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite); + } - // On Windows and if the file exists with an EPERM error, we try a different strategy of saving the file - // by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows - // (see https://github.com/Microsoft/vscode/issues/931) + // On Windows and if the file exists, we use a different strategy of saving the file + // by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows + // (see https://github.com/Microsoft/vscode/issues/931) and prevent removing alternate data streams + // (see https://github.com/Microsoft/vscode/issues/6363) + else { - // 5.) truncate + // 4.) truncate return pfs.truncate(absolutePath, 0).then(() => { - // 6.) set contents (this time with r+ mode) and resolve again + // 5.) set contents (with r+ mode) and resolve return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' }); }); - }); + } }); }); }).then(null, error => { @@ -1051,7 +1052,7 @@ export class FileService extends Disposable implements IFileService { this.undeliveredRawFileChangesEvents.push(event); if (this.environmentService.verbose) { - console.log('%c[node.js Watcher]%c', 'color: green', 'color: black', event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', event.path); + console.log('%c[File Watcher (node.js)]%c', 'color: blue', 'color: black', event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', event.path); } // handle emit through delayer to accommodate for bulk changes @@ -1065,7 +1066,7 @@ export class FileService extends Disposable implements IFileService { // Logging if (this.environmentService.verbose) { normalizedEvents.forEach(r => { - console.log('%c[node.js Watcher]%c >> normalized', 'color: green', 'color: black', r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', r.path); + console.log('%c[File Watcher (node.js)]%c >> normalized', 'color: blue', 'color: black', r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', r.path); }); } diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 3f2e2a1806f..1b13bcc8717 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 { TernarySearchTree } 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; @@ -161,12 +161,11 @@ class WorkspaceWatchLogic extends Disposable { export class RemoteFileService extends FileService { private readonly _provider: Map; - private readonly _lastKnownSchemes: string[]; constructor( @IExtensionService private readonly _extensionService: IExtensionService, - @IStorageService private readonly _storageService: IStorageService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IStorageService storageService: IStorageService, + @IEnvironmentService environmentService: IEnvironmentService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService contextService: IWorkspaceContextService, @ILifecycleService lifecycleService: ILifecycleService, @@ -175,16 +174,15 @@ export class RemoteFileService extends FileService { ) { super( contextService, - _environmentService, + environmentService, textResourceConfigurationService, configurationService, lifecycleService, - _storageService, + storageService, notificationService ); this._provider = new Map(); - this._lastKnownSchemes = JSON.parse(this._storageService.get('remote_schemes', undefined, '[]')); this._register(new WorkspaceWatchLogic(this, configurationService, contextService)); } @@ -195,7 +193,6 @@ export class RemoteFileService extends FileService { this._provider.set(scheme, provider); this._onDidChangeFileSystemProviderRegistrations.fire({ added: true, scheme, provider }); - this._storageService.store('remote_schemes', JSON.stringify(keys(this._provider))); const reg = provider.onDidChangeFile(changes => { // forward change events @@ -211,19 +208,7 @@ export class RemoteFileService extends FileService { } canHandleResource(resource: URI): boolean { - if (resource.scheme === Schemas.file || this._provider.has(resource.scheme)) { - return true; - } - // TODO@remote - // this needs to go, but this already went viral - // https://github.com/Microsoft/vscode/issues/48275 - if (this._lastKnownSchemes.indexOf(resource.scheme) < 0) { - return false; - } - if (!this._environmentService.isBuilt) { - console.warn('[remote] cache information required for ' + resource.toString()); - } - return true; + return resource.scheme === Schemas.file || this._provider.has(resource.scheme); } private _tryParseFileOperationResult(err: any): FileOperationResult { @@ -261,7 +246,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 +267,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 +275,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 +292,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 +305,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 +316,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 +419,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 +443,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 +464,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 +517,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/nsfwWatcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts index 98691d9a030..02e302838be 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts @@ -87,10 +87,14 @@ export class NsfwWatcherService implements IWatcherService { absolutePath = path.join(e.directory, e.oldFile); if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.basePath].ignored)) { undeliveredFileEvents.push({ type: FileChangeType.DELETED, path: absolutePath }); + } else if (this._verboseLogging) { + console.log(' >> ignored', absolutePath); } absolutePath = path.join(e.directory, e.newFile); if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.basePath].ignored)) { undeliveredFileEvents.push({ type: FileChangeType.ADDED, path: absolutePath }); + } else if (this._verboseLogging) { + console.log(' >> ignored', absolutePath); } } else { absolutePath = path.join(e.directory, e.file); @@ -99,6 +103,8 @@ export class NsfwWatcherService implements IWatcherService { type: nsfwActionToRawChangeType[e.action], path: absolutePath }); + } else if (this._verboseLogging) { + console.log(' >> ignored', absolutePath); } } } diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts index 1502478f301..e90d6db72e0 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts @@ -6,7 +6,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; import { Event } from 'vs/base/common/event'; import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; 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..a0fc530b01d 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -5,9 +5,8 @@ 'use strict'; -import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; +import { getNextTickChannel } from 'vs/base/parts/ipc/node/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,9 +39,9 @@ export class FileWatcher { public startWatching(): () => void { const client = new Client( - uri.parse(require.toUrl('bootstrap')).fsPath, + getPathFromAmdModule(require, 'bootstrap'), { - serverName: 'Watcher', + serverName: 'File Watcher (nsfw)', args: ['--type=watcherService'], env: { AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/nsfw/watcherApp', diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts index 1502478f301..e90d6db72e0 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts @@ -6,7 +6,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; import { Event } from 'vs/base/common/event'; import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; 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..475bdfca4ce 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -5,9 +5,8 @@ 'use strict'; -import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; +import { getNextTickChannel } from 'vs/base/parts/ipc/node/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,9 +42,9 @@ export class FileWatcher { const args = ['--type=watcherService']; const client = new Client( - uri.parse(require.toUrl('bootstrap')).fsPath, + getPathFromAmdModule(require, 'bootstrap'), { - serverName: 'Watcher', + serverName: 'File Watcher (chokidar)', args, env: { AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/unix/watcherApp', @@ -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 949c3d1b36f..4b5b8c78676 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,12 +41,12 @@ 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(); // Events over stdout - this.handle.stdout.on('data', (data: NodeBuffer) => { + this.handle.stdout.on('data', (data: Buffer) => { // Collect raw events from output const rawEvents: IRawFileChange[] = []; @@ -61,6 +61,10 @@ export class OutOfProcessWin32FolderWatcher { // Support ignores if (this.ignored && this.ignored.some(ignore => glob.match(ignore, absolutePath))) { + if (this.verboseLogging) { + console.log('%c[File Watcher (C#)]', 'color: blue', ' >> ignored', absolutePath); + } + return; } @@ -73,7 +77,7 @@ export class OutOfProcessWin32FolderWatcher { // 3 Logging else { - console.log('%c[File Watcher]', 'color: darkgreen', eventParts[1]); + console.log('%c[File Watcher (C#)]', 'color: blue', eventParts[1]); } } }); @@ -86,26 +90,26 @@ export class OutOfProcessWin32FolderWatcher { // Errors this.handle.on('error', (error: Error) => this.onError(error)); - this.handle.stderr.on('data', (data: NodeBuffer) => this.onError(data)); + this.handle.stderr.on('data', (data: Buffer) => this.onError(data)); // Exit this.handle.on('exit', (code: number, signal: string) => this.onExit(code, signal)); } - private onError(error: Error | NodeBuffer): void { - this.errorCallback('[FileWatcher] process error: ' + error.toString()); + private onError(error: Error | Buffer): void { + this.errorCallback('[File Watcher (C#)] process error: ' + error.toString()); } private onExit(code: number, signal: string): void { if (this.handle) { // exit while not yet being disposed is unexpected! - this.errorCallback(`[FileWatcher] terminated unexpectedly (code: ${code}, signal: ${signal})`); + this.errorCallback(`[File Watcher (C#)] terminated unexpectedly (code: ${code}, signal: ${signal})`); if (this.restartCounter <= OutOfProcessWin32FolderWatcher.MAX_RESTARTS) { - this.errorCallback('[FileWatcher] is restarted again...'); + this.errorCallback('[File Watcher (C#)] is restarted again...'); this.restartCounter++; this.startWatcher(); // restart } else { - this.errorCallback('[FileWatcher] Watcher failed to start after retrying for some time, giving up. Please report this as a bug report!'); + this.errorCallback('[File Watcher (C#)] Watcher failed to start after retrying for some time, giving up. Please report this as a bug report!'); } } } @@ -116,4 +120,4 @@ export class OutOfProcessWin32FolderWatcher { this.handle = null; } } -} \ No newline at end of file +} 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..43e10816ddb 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,10 +34,10 @@ 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 }); + service = new FileService(new TestContextService(new Workspace(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[] = []; @@ -852,7 +853,7 @@ suite('FileService', () => { const textResourceConfigurationService = new TestTextResourceConfigurationService(configurationService); const _service = new FileService( - new TestContextService(new Workspace(_testDir, _testDir, toWorkspaceFolders([{ path: _testDir }]))), + new TestContextService(new Workspace(_testDir, toWorkspaceFolders([{ path: _testDir }]))), TestEnvironmentService, textResourceConfigurationService, configurationService, @@ -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[] = []; @@ -897,7 +898,7 @@ suite('FileService', () => { const textResourceConfigurationService = new TestTextResourceConfigurationService(configurationService); const _service = new FileService( - new TestContextService(new Workspace(_testDir, _testDir, toWorkspaceFolders([{ path: _testDir }]))), + new TestContextService(new Workspace(_testDir, toWorkspaceFolders([{ path: _testDir }]))), TestEnvironmentService, textResourceConfigurationService, configurationService, @@ -927,11 +928,11 @@ 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( - new TestContextService(new Workspace(_testDir, _testDir, toWorkspaceFolders([{ path: _testDir }]))), + new TestContextService(new Workspace(_testDir, toWorkspaceFolders([{ path: _testDir }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), 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..2c0fa3c7118 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 { @@ -606,7 +630,7 @@ export class HistoryService extends Disposable implements IHistoryService { const input = arg1 as IResourceInput; - this.windowService.removeFromRecentlyOpened([input.resource.fsPath]); + this.windowService.removeFromRecentlyOpened([input.resource]); } private isFileOpened(resource: URI, group: IEditorGroup): boolean { @@ -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/issue/electron-browser/workbenchIssueService.ts b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts index a3e9e3727bd..3ef280db960 100644 --- a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IssueReporterStyles, IIssueService, IssueReporterData } from 'vs/platform/issue/common/issue'; +import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/common/issue'; import { TPromise } from 'vs/base/common/winjs.base'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, editorBackground, editorForeground, listHoverBackground, listHoverForeground, listHighlightForeground, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -14,6 +14,7 @@ import { IExtensionManagementService, IExtensionEnablementService, LocalExtensio import { webFrame } from 'electron'; import { assign } from 'vs/base/common/objects'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; +import { IWindowService } from 'vs/platform/windows/common/windows'; export class WorkbenchIssueService implements IWorkbenchIssueService { _serviceBrand: any; @@ -22,7 +23,8 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { @IIssueService private issueService: IIssueService, @IThemeService private themeService: IThemeService, @IExtensionManagementService private extensionManagementService: IExtensionManagementService, - @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IWindowService private windowService: IWindowService ) { } @@ -44,7 +46,8 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { openProcessExplorer(): TPromise { const theme = this.themeService.getTheme(); - const data = { + const data: ProcessExplorerData = { + pid: this.windowService.getConfiguration().mainPid, zoomLevel: webFrame.getZoomLevel(), styles: { backgroundColor: theme.getColor(editorBackground) && theme.getColor(editorBackground).toString(), diff --git a/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts b/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts index d5ab87d54f7..552888adf39 100644 --- a/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts +++ b/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts @@ -60,7 +60,7 @@ export class JSONValidationExtensionPoint { if (strings.startsWith(uri, './')) { try { const colorThemeLocation = resources.joinPath(extensionLocation, uri); - if (colorThemeLocation.path.indexOf(extensionLocation.path) !== 0) { + if (!resources.isEqualOrParent(colorThemeLocation, extensionLocation)) { collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.url` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", configurationExtPoint.name, location, extensionLocation.path)); } } catch (e) { diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index c0521529d6e..77a0eb4cd9e 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -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 92fba70ec97..cb922f849d5 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 @@ -86,7 +86,7 @@ suite('KeybindingsEditing', () => { 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 }]))), + new TestContextService(new Workspace(testDir, toWorkspaceFolders([{ path: testDir }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), 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..71c7b465467 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,21 +45,11 @@ export interface IPartService { */ onTitleBarVisibilityChange: Event; - /** - * Emits when the visibility of the menubar changes. - */ - onMenubarVisibilityChange: Event; - /** * Emits when the editor part's layout changes. */ onEditorLayout: Event; - /** - * Asks the part service to layout all parts. - */ - layout(options?: ILayoutOptions): void; - /** * Asks the part service to if all parts have been created. */ @@ -115,6 +106,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 f0c69877821..77a3cc9a1af 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 { ILabelService } from 'vs/platform/label/common/label'; const emptyEditableSettingsContent = '{\n}'; @@ -45,6 +45,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic _serviceBrand: any; private lastOpenedSettingsInput: PreferencesEditorInput = null; + private lastOpenedSettings2Input: SettingsEditor2Input = null; private readonly _onDispose: Emitter = new Emitter(); @@ -69,7 +70,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, + @ILabelService private labelService: ILabelService ) { super(); // The default keybindings.json updates based on keyboard layouts, so here we make sure @@ -175,34 +177,52 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor({ resource: this.userSettingsResource }); } - openSettings(): TPromise { + openSettings(jsonEditor?: boolean): TPromise { + if (!jsonEditor) { + return this.openSettings2(); + } + const editorInput = this.getActiveSettingsEditorInput() || this.lastOpenedSettingsInput; const resource = editorInput ? editorInput.master.getResource() : this.userSettingsResource; const target = this.getConfigurationTargetFromSettingsResource(resource); return this.openOrSwitchSettings(target, resource); } - openGlobalSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise { - return this.openOrSwitchSettings(ConfigurationTarget.USER, this.userSettingsResource, options, group); + private openSettings2(): TPromise { + const editorInput = this.getActiveSettingsEditor2Input() || this.lastOpenedSettings2Input; + const resource = editorInput ? editorInput.getResource() : this.userSettingsResource; + const target = this.getConfigurationTargetFromSettingsResource(resource); + return this.openOrSwitchSettings2(target); } - openSettings2(): TPromise { - return this.editorService.openEditor(this.instantiationService.createInstance(SettingsEditor2Input), { pinned: true }).then(() => null); + openGlobalSettings(jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise { + return jsonEditor ? + this.openOrSwitchSettings(ConfigurationTarget.USER, this.userSettingsResource, options, group) : + this.openOrSwitchSettings2(ConfigurationTarget.USER, options, group); } - openWorkspaceSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise { + openWorkspaceSettings(jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise { if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); return TPromise.as(null); } - return this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, group); + + return jsonEditor ? + this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, group) : + this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, options, group); } - openFolderSettings(folder: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise { - return this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, group); + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise { + return jsonEditor ? + this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, group) : + this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, options, group); } - switchSettings(target: ConfigurationTarget, resource: URI): TPromise { + switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): TPromise { + if (!jsonEditor) { + return this.switchSettings2(target); + } + const activeControl = this.editorService.activeControl; if (activeControl && activeControl.input instanceof PreferencesEditorInput) { return this.doSwitchSettings(target, resource, activeControl.input, activeControl.group).then(() => null); @@ -211,6 +231,16 @@ export class PreferencesService extends Disposable implements IPreferencesServic } } + switchSettings2(target: ConfigurationTarget): TPromise { + const activeControl = this.editorService.activeControl; + const resource = this.getDefaultSettingsResource(target); + if (activeControl && activeControl.input instanceof SettingsEditor2Input) { + return this.doSwitchSettings2(resource, activeControl.input, activeControl.group).then(() => null); + } else { + return this.doOpenSettings2(resource).then(() => null); + } + } + openGlobalKeybindingSettings(textual: boolean): TPromise { /* __GDPR__ "openKeybindings" : { @@ -270,6 +300,16 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.doOpenSettings(configurationTarget, resource, options, group); } + private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, options?: IEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise { + const editorInput = this.getActiveSettingsEditor2Input(group); + const resource = this.getDefaultSettingsResource(configurationTarget); + if (editorInput && editorInput.getResource().fsPath !== resource.fsPath) { + return this.doSwitchSettings2(resource, editorInput, group); + } + + return this.doOpenSettings2(resource, options, group); + } + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise { const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING); return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) @@ -290,6 +330,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } + private doOpenSettings2(resource: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise { + const settingsEditorInput = this.instantiationService.createInstance(SettingsEditor2Input, resource); + this.lastOpenedSettings2Input = settingsEditorInput; + return this.editorService.openEditor(settingsEditorInput, options, group); + } + private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup): TPromise { return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource)) .then(toInput => { @@ -307,10 +353,28 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } + private doSwitchSettings2(resource: URI, input: SettingsEditor2Input, group: IEditorGroup): TPromise { + return group.openEditor(input).then(() => { + const replaceWith = this.instantiationService.createInstance(SettingsEditor2Input, resource); + + return group.replaceEditors([{ + editor: input, + replacement: replaceWith + }]).then(() => { + this.lastOpenedSettings2Input = replaceWith; + return group.activeControl; + }); + }); + } + private getActiveSettingsEditorInput(group: IEditorGroup = this.editorGroupService.activeGroup): PreferencesEditorInput { return group.editors.filter(e => e instanceof PreferencesEditorInput)[0]; } + private getActiveSettingsEditor2Input(group: IEditorGroup = this.editorGroupService.activeGroup): SettingsEditor2Input { + return group.editors.filter(e => e instanceof SettingsEditor2Input)[0]; + } + private getConfigurationTargetFromSettingsResource(resource: URI): ConfigurationTarget { if (this.userSettingsResource.toString() === resource.toString()) { return ConfigurationTarget.USER; @@ -444,7 +508,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.labelService.getUriLabel(resource, true), error))); }); } diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index c3174c92663..db2511babc9 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -41,14 +41,19 @@ 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[]; + validator?: (value: any) => string; } export interface IExtensionSetting extends ISetting { @@ -58,6 +63,7 @@ export interface IExtensionSetting extends ISetting { export interface ISearchResult { filterMatches: ISettingMatch[]; + exactMatch?: boolean; metadata?: IFilterMetadata; } @@ -74,6 +80,7 @@ export interface IFilterResult { allGroups: ISettingsGroup[]; matches: IRange[]; metadata?: IStringDictionary; + exactMatch?: boolean; } export interface ISettingMatch { @@ -145,12 +152,11 @@ export interface IPreferencesService { createPreferencesEditorModel(uri: URI): TPromise>; openRawDefaultSettings(): TPromise; - openSettings(): TPromise; - openSettings2(): TPromise; - openGlobalSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise; - openWorkspaceSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise; - openFolderSettings(folder: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise; - switchSettings(target: ConfigurationTarget, resource: URI): TPromise; + openSettings(jsonEditor?: boolean): TPromise; + openGlobalSettings(jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise; + openWorkspaceSettings(jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise; + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: IEditorOptions, group?: IEditorGroup): TPromise; + switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): TPromise; openGlobalKeybindingSettings(textual: boolean): TPromise; openDefaultKeybindingsFile(): TPromise; diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 045707197af..19bf4306846 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -13,8 +13,6 @@ import { EditorInput, SideBySideEditorInput, Verbosity } from 'vs/workbench/comm import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { KeybindingsEditorModel } from 'vs/workbench/services/preferences/common/keybindingsEditorModel'; -import { IPreferencesService } from './preferences'; -import { DefaultSettingsEditorModel } from './preferencesModels'; export class PreferencesEditorInput extends SideBySideEditorInput { public static readonly ID: string = 'workbench.editorinputs.preferencesEditorInput'; @@ -79,29 +77,18 @@ export class KeybindingsEditorInput extends EditorInput { } } -export class SettingsEditor2Input extends EditorInput { +export class SettingsEditor2Input extends ResourceEditorInput { public static readonly ID: string = 'workbench.input.settings2'; - constructor( - @IPreferencesService private preferencesService: IPreferencesService + constructor(defaultSettingsResource: URI, + @ITextModelService textModelResolverService: ITextModelService, + @IHashService hashService: IHashService ) { - super(); + super(nls.localize('settingsEditor2InputName', "Settings (Preview)"), '', defaultSettingsResource, textModelResolverService, hashService); } getTypeId(): string { return SettingsEditor2Input.ID; } - - getName(): string { - return nls.localize('settingsEditor2InputName', "Settings (Preview)"); - } - - resolve(): TPromise { - return >this.preferencesService.createPreferencesEditorModel(URI.parse('vscode://defaultsettings/0/settings.json')); - } - - matches(otherInput: any): boolean { - return otherInput instanceof SettingsEditor2Input; - } } diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 62a5d8c1997..4b3ffbb6b80 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,45 @@ 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'); 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, + validator: createValidator(prop) + }); } } 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 { @@ -739,13 +767,22 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements } private copySetting(setting: ISetting): ISetting { - return { + 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, + deprecationMessage: setting.deprecationMessage, + keyRange: undefined, + valueRange: undefined, + descriptionIsMarkdown: undefined, + descriptionRanges: undefined }; } @@ -852,16 +889,16 @@ class SettingsContentBuilder { this.pushSettingDescription(setting, indent); - let preValueConent = indent; + let preValueContent = indent; const keyString = JSON.stringify(setting.key); - preValueConent += keyString; - setting.keyRange = { startLineNumber: this.lineCountWithOffset + 1, startColumn: preValueConent.indexOf(setting.key) + 1, endLineNumber: this.lineCountWithOffset + 1, endColumn: setting.key.length }; + preValueContent += keyString; + setting.keyRange = { startLineNumber: this.lineCountWithOffset + 1, startColumn: preValueContent.indexOf(setting.key) + 1, endLineNumber: this.lineCountWithOffset + 1, endColumn: setting.key.length }; - preValueConent += ': '; + preValueContent += ': '; const valueStart = this.lineCountWithOffset + 1; - this.pushValue(setting, preValueConent, indent); + this.pushValue(setting, preValueContent, indent); - setting.valueRange = { startLineNumber: valueStart, startColumn: preValueConent.length + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length + 1 }; + setting.valueRange = { startLineNumber: valueStart, startColumn: preValueContent.length + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length + 1 }; this._contentByLines[this._contentByLines.length - 1] += ','; this._contentByLines.push(''); setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length }; @@ -872,8 +909,7 @@ class SettingsContentBuilder { setting.descriptionRanges = []; const descriptionPreValue = indent + '// '; - for (let line of setting.description) { - // Remove setting link tag + for (let line of (setting.deprecationMessage ? [setting.deprecationMessage, ...setting.description] : setting.description)) { line = fixSettingLink(line); this._contentByLines.push(descriptionPreValue + line); @@ -882,9 +918,10 @@ class SettingsContentBuilder { if (setting.enumDescriptions && setting.enumDescriptions.some(desc => !!desc)) { setting.enumDescriptions.forEach((desc, i) => { + const displayEnum = escapeInvisibleChars(String(setting.enum[i])); const line = desc ? - `${setting.enum[i]}: ${fixSettingLink(desc)}` : - setting.enum[i]; + `${displayEnum}: ${fixSettingLink(desc)}` : + displayEnum; this._contentByLines.push(` // - ${line}`); @@ -925,6 +962,119 @@ class SettingsContentBuilder { } } +export function createValidator(prop: IConfigurationPropertySchema): ((value: any) => string) | null { + let exclusiveMax: number | undefined; + let exclusiveMin: number | undefined; + + if (typeof prop.exclusiveMaximum === 'boolean') { + exclusiveMax = prop.exclusiveMaximum ? prop.maximum : undefined; + } else { + exclusiveMax = prop.exclusiveMaximum; + } + + if (typeof prop.exclusiveMinimum === 'boolean') { + exclusiveMin = prop.exclusiveMinimum ? prop.minimum : undefined; + } else { + exclusiveMin = prop.exclusiveMinimum; + } + + let patternRegex: RegExp | undefined; + if (typeof prop.pattern === 'string') { + patternRegex = new RegExp(prop.pattern); + } + + const type = Array.isArray(prop.type) ? prop.type : [prop.type]; + const canBeType = (t: string) => type.indexOf(t) > -1; + + const isNullable = canBeType('null'); + const isNumeric = (canBeType('number') || canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable); + const isIntegral = (canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable); + + type Validator = { enabled: boolean, isValid: (value: T) => boolean; message: string }; + + let numericValidations: Validator[] = isNumeric ? [ + { + enabled: exclusiveMax !== undefined && (prop.maximum === undefined || exclusiveMax <= prop.maximum), + isValid: (value => value < exclusiveMax), + message: nls.localize('validations.exclusiveMax', "Value must be strictly less than {0}.", exclusiveMax) + }, + { + enabled: exclusiveMin !== undefined && (prop.minimum === undefined || exclusiveMin >= prop.minimum), + isValid: (value => value > exclusiveMin), + message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin) + }, + + { + enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum), + isValid: (value => value <= prop.maximum), + message: nls.localize('validations.max', "Value must be less than or equal to {0}.", prop.maximum) + }, + { + enabled: prop.minimum !== undefined && (exclusiveMin === undefined || exclusiveMin < prop.minimum), + isValid: (value => value >= prop.minimum), + message: nls.localize('validations.min', "Value must be greater than or equal to {0}.", prop.minimum) + }, + { + enabled: prop.multipleOf !== undefined, + isValid: (value => value % prop.multipleOf === 0), + message: nls.localize('validations.multipleOf', "Value must be a multiple of {0}.", prop.multipleOf) + }, + { + enabled: isIntegral, + isValid: (value => value % 1 === 0), + message: nls.localize('validations.expectedInteger', "Value must be an integer.") + }, + ].filter(validation => validation.enabled) : []; + + let stringValidations: Validator[] = [ + { + enabled: prop.maxLength !== undefined, + isValid: (value => value.length <= prop.maxLength), + message: nls.localize('validations.maxLength', "Value must be fewer than {0} characters long.", prop.maxLength) + }, + { + enabled: prop.minLength !== undefined, + isValid: (value => value.length >= prop.minLength), + message: nls.localize('validations.minLength', "Value must be more than {0} characters long.", prop.minLength) + }, + { + enabled: patternRegex !== undefined, + isValid: (value => patternRegex.test(value)), + message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern) + }, + ].filter(validation => validation.enabled); + + if (prop.type === 'string' && stringValidations.length === 0) { return null; } + + return value => { + if (isNullable && value === '') { return ''; } + + let errors = []; + + if (isNumeric) { + if (value === '' || isNaN(+value)) { + errors.push(nls.localize('validations.expectedNumeric', "Value must be a number.")); + } else { + errors.push(...numericValidations.filter(validator => !validator.isValid(+value)).map(validator => validator.message)); + } + } + + if (prop.type === 'string') { + errors.push(...stringValidations.filter(validator => !validator.isValid('' + value)).map(validator => validator.message)); + } + if (errors.length) { + return prop.errorMessage ? [prop.errorMessage, ...errors].join(' ') : errors.join(' '); + } + return ''; + }; +} + +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/preferences/test/common/preferencesModel.test.ts b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts new file mode 100644 index 00000000000..8550a177cf4 --- /dev/null +++ b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts @@ -0,0 +1,249 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { createValidator } from 'vs/workbench/services/preferences/common/preferencesModels'; +import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; + + +suite('Preferences Model test', () => { + class Tester { + private validator: (value: any) => string; + + constructor(private settings: IConfigurationPropertySchema) { + this.validator = createValidator(settings); + } + + public accepts(input) { + assert.equal(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to accept \`${input}\`. Got ${this.validator(input)}.`); + } + + public rejects(input) { + assert.notEqual(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to reject \`${input}\`.`); + return { + withMessage: + (message) => assert(this.validator(input).indexOf(message) > -1, + `Expected error of ${JSON.stringify(this.settings)} on \`${input}\` to contain ${message}. Got ${this.validator(input)}.`) + }; + } + + + public validatesNumeric() { + this.accepts('3'); + this.accepts('3.'); + this.accepts('.0'); + this.accepts('3.0'); + this.accepts(' 3.0'); + this.accepts(' 3.0 '); + this.rejects('3f'); + } + + public validatesNullableNumeric() { + this.validatesNumeric(); + this.accepts(''); + } + + public validatesNonNullableNumeric() { + this.validatesNumeric(); + this.rejects(''); + } + + public validatesString() { + this.accepts('3'); + this.accepts('3.'); + this.accepts('.0'); + this.accepts('3.0'); + this.accepts(' 3.0'); + this.accepts(' 3.0 '); + this.accepts(''); + this.accepts('3f'); + this.accepts('hello'); + } + } + + + test('exclusive max and max work together properly', () => { + { + const justMax = new Tester({ maximum: 5, type: 'number' }); + justMax.validatesNonNullableNumeric(); + justMax.rejects('5.1'); + justMax.accepts('5.0'); + } + { + const justEMax = new Tester({ exclusiveMaximum: 5, type: 'number' }); + justEMax.validatesNonNullableNumeric(); + justEMax.rejects('5.1'); + justEMax.rejects('5.0'); + justEMax.accepts('4.999'); + } + { + const bothNumeric = new Tester({ exclusiveMaximum: 5, maximum: 4, type: 'number' }); + bothNumeric.validatesNonNullableNumeric(); + bothNumeric.rejects('5.1'); + bothNumeric.rejects('5.0'); + bothNumeric.rejects('4.999'); + bothNumeric.accepts('4'); + } + { + const bothNumeric = new Tester({ exclusiveMaximum: 5, maximum: 6, type: 'number' }); + bothNumeric.validatesNonNullableNumeric(); + bothNumeric.rejects('5.1'); + bothNumeric.rejects('5.0'); + bothNumeric.accepts('4.999'); + } + }); + + test('exclusive min and min work together properly', () => { + { + const justMin = new Tester({ minimum: -5, type: 'number' }); + justMin.validatesNonNullableNumeric(); + justMin.rejects('-5.1'); + justMin.accepts('-5.0'); + } + { + const justEMin = new Tester({ exclusiveMinimum: -5, type: 'number' }); + justEMin.validatesNonNullableNumeric(); + justEMin.rejects('-5.1'); + justEMin.rejects('-5.0'); + justEMin.accepts('-4.999'); + } + { + const bothNumeric = new Tester({ exclusiveMinimum: -5, minimum: -4, type: 'number' }); + bothNumeric.validatesNonNullableNumeric(); + bothNumeric.rejects('-5.1'); + bothNumeric.rejects('-5.0'); + bothNumeric.rejects('-4.999'); + bothNumeric.accepts('-4'); + } + { + const bothNumeric = new Tester({ exclusiveMinimum: -5, minimum: -6, type: 'number' }); + bothNumeric.validatesNonNullableNumeric(); + bothNumeric.rejects('-5.1'); + bothNumeric.rejects('-5.0'); + bothNumeric.accepts('-4.999'); + } + }); + + test('multiple of works for both integers and fractions', () => { + { + const onlyEvens = new Tester({ multipleOf: 2, type: 'number' }); + onlyEvens.accepts('2.0'); + onlyEvens.accepts('2'); + onlyEvens.accepts('-4'); + onlyEvens.accepts('0'); + onlyEvens.accepts('100'); + onlyEvens.rejects('100.1'); + onlyEvens.rejects(''); + onlyEvens.rejects('we'); + } + { + const hackyIntegers = new Tester({ multipleOf: 1, type: 'number' }); + hackyIntegers.accepts('2.0'); + hackyIntegers.rejects('.5'); + } + { + const halfIntegers = new Tester({ multipleOf: 0.5, type: 'number' }); + halfIntegers.accepts('0.5'); + halfIntegers.accepts('1.5'); + halfIntegers.rejects('1.51'); + } + }); + + test('integer type correctly adds a validation', () => { + { + const integers = new Tester({ multipleOf: 1, type: 'integer' }); + integers.accepts('02'); + integers.accepts('2'); + integers.accepts('20'); + integers.rejects('.5'); + integers.rejects('2j'); + integers.rejects(''); + } + }); + + test('null is allowed only when expected', () => { + { + const nullableIntegers = new Tester({ type: ['integer', 'null'] }); + nullableIntegers.accepts('2'); + nullableIntegers.rejects('.5'); + nullableIntegers.accepts('2.0'); + nullableIntegers.rejects('2j'); + nullableIntegers.accepts(''); + } + { + const nonnullableIntegers = new Tester({ type: ['integer'] }); + nonnullableIntegers.accepts('2'); + nonnullableIntegers.rejects('.5'); + nonnullableIntegers.accepts('2.0'); + nonnullableIntegers.rejects('2j'); + nonnullableIntegers.rejects(''); + } + { + const nullableNumbers = new Tester({ type: ['number', 'null'] }); + nullableNumbers.accepts('2'); + nullableNumbers.accepts('.5'); + nullableNumbers.accepts('2.0'); + nullableNumbers.rejects('2j'); + nullableNumbers.accepts(''); + } + { + const nonnullableNumbers = new Tester({ type: ['number'] }); + nonnullableNumbers.accepts('2'); + nonnullableNumbers.accepts('.5'); + nonnullableNumbers.accepts('2.0'); + nonnullableNumbers.rejects('2j'); + nonnullableNumbers.rejects(''); + } + }); + + test('string max min length work', () => { + { + const min = new Tester({ minLength: 4, type: 'string' }); + min.rejects('123'); + min.accepts('1234'); + min.accepts('12345'); + } + { + const max = new Tester({ maxLength: 6, type: 'string' }); + max.accepts('12345'); + max.accepts('123456'); + max.rejects('1234567'); + } + { + const minMax = new Tester({ minLength: 4, maxLength: 6, type: 'string' }); + minMax.rejects('123'); + minMax.accepts('1234'); + minMax.accepts('12345'); + minMax.accepts('123456'); + minMax.rejects('1234567'); + } + }); + + test('patterns work', () => { + { + const urls = new Tester({ pattern: '^(hello)*$', type: 'string' }); + urls.accepts(''); + urls.rejects('hel'); + urls.accepts('hello'); + urls.rejects('hellohel'); + urls.accepts('hellohello'); + } + { + const urls = new Tester({ pattern: '^(hello)*$', type: 'string', patternErrorMessage: 'err: must be friendly' }); + urls.accepts(''); + urls.rejects('hel').withMessage('err: must be friendly'); + urls.accepts('hello'); + urls.rejects('hellohel').withMessage('err: must be friendly'); + urls.accepts('hellohello'); + } + }); + + test('custom error messages are shown', () => { + const withMessage = new Tester({ minLength: 1, maxLength: 0, type: 'string', errorMessage: 'always error!' }); + withMessage.rejects('').withMessage('always error!'); + withMessage.rejects(' ').withMessage('always error!'); + withMessage.rejects('1').withMessage('always error!'); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressService.test.ts index f1f23e0cea1..67dd539396e 100644 --- a/src/vs/workbench/services/progress/test/progressService.test.ts +++ b/src/vs/workbench/services/progress/test/progressService.test.ts @@ -40,6 +40,10 @@ class TestViewletService implements IViewletService { return []; } + public getAllViewlets(): ViewletDescriptor[] { + return []; + } + public getActiveViewlet(): IViewlet { return activeViewlet; } 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/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index b1683441fd7..d86cbb9b37f 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -6,28 +6,27 @@ 'use strict'; import * as childProcess from 'child_process'; -import { StringDecoder, NodeStringDecoder } from 'string_decoder'; -import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as fs from 'fs'; import * as path from 'path'; -import { isEqualOrParent } from 'vs/base/common/paths'; import { Readable } from 'stream'; -import { TPromise } from 'vs/base/common/winjs.base'; - -import * as objects from 'vs/base/common/objects'; +import { NodeStringDecoder, StringDecoder } from 'string_decoder'; import * as arrays from 'vs/base/common/arrays'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import * as glob from 'vs/base/common/glob'; +import * as normalization from 'vs/base/common/normalization'; +import * as objects from 'vs/base/common/objects'; +import { isEqualOrParent } from 'vs/base/common/paths'; import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import * as normalization from 'vs/base/common/normalization'; import * as types from 'vs/base/common/types'; -import * as glob from 'vs/base/common/glob'; -import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/search'; - +import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; import * as flow from 'vs/base/node/flow'; -import { IRawFileMatch, IRawSearch, ISearchEngine, IFolderSearch, ISerializedSearchSuccess } from './search'; +import { IProgress, ISearchEngineStats } from 'vs/platform/search/common/search'; import { spawnRipgrepCmd } from './ripgrepFileSearch'; import { rgErrorMsgForDisplay } from './ripgrepTextSearch'; +import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISearchEngineSuccess } from './search'; +import { StopWatch } from 'vs/base/common/stopwatch'; enum Traversal { Node = 1, @@ -60,13 +59,12 @@ export class FileWalker { private isLimitHit: boolean; private resultCount: number; private isCanceled: boolean; - private fileWalkStartTime: number; + private fileWalkSW: StopWatch; private directoriesWalked: number; private filesWalked: number; private traversal: Traversal; private errors: string[]; - private cmdForkStartTime: number; - private cmdForkResultTime: number; + private cmdSW: StopWatch; private cmdResultCount: number; private folderExcludePatterns: Map; @@ -120,7 +118,7 @@ export class FileWalker { } public walk(folderQueries: IFolderSearch[], extraFiles: string[], onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (error: Error, isLimitHit: boolean) => void): void { - this.fileWalkStartTime = Date.now(); + this.fileWalkSW = StopWatch.create(false); // Support that the file pattern is a full path to a file that exists if (this.isCanceled) { @@ -160,7 +158,7 @@ export class FileWalker { const isNodeTraversal = traverse === this.nodeJSTraversal; if (!isNodeTraversal) { - this.cmdForkStartTime = Date.now(); + this.cmdSW = StopWatch.create(false); } // For each root folder @@ -176,6 +174,7 @@ export class FileWalker { } }); }, (errors, result) => { + this.fileWalkSW.stop(); const err = errors ? errors.filter(e => !!e)[0] : null; done(err, this.isLimitHit); }); @@ -211,22 +210,21 @@ export class FileWalker { cmd = ripgrep.cmd; noSiblingsClauses = !Object.keys(ripgrep.siblingClauses).length; - process.nextTick(() => { - const escapedArgs = ripgrep.rgArgs.args - .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) - .join(' '); + const escapedArgs = ripgrep.rgArgs.args + .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) + .join(' '); - let rgCmd = `rg ${escapedArgs}\n - cwd: ${ripgrep.cwd}`; - if (ripgrep.rgArgs.siblingClauses) { - rgCmd += `\n - Sibling clauses: ${JSON.stringify(ripgrep.rgArgs.siblingClauses)}`; - } - onMessage({ message: rgCmd }); - }); + let rgCmd = `rg ${escapedArgs}\n - cwd: ${ripgrep.cwd}`; + if (ripgrep.rgArgs.siblingClauses) { + rgCmd += `\n - Sibling clauses: ${JSON.stringify(ripgrep.rgArgs.siblingClauses)}`; + } + onMessage({ message: rgCmd }); } else { cmd = this.spawnFindCmd(folderQuery); } process.on('exit', killCmd); + this.cmdResultCount = 0; this.collectStdout(cmd, 'utf8', useRipgrep, onMessage, (err: Error, stdout?: string, last?: boolean) => { if (err) { done(err); @@ -362,7 +360,10 @@ export class FileWalker { let onData = (err: Error, stdout?: string, last?: boolean) => { if (err || last) { onData = () => { }; - this.cmdForkResultTime = Date.now(); + + if (this.cmdSW) { + this.cmdSW.stop(); + } } cb(err, stdout, last); }; @@ -510,18 +511,13 @@ export class FileWalker { }); } - public getStats(): IUncachedSearchStats { + public getStats(): ISearchEngineStats { return { - fromCache: false, + cmdTime: this.cmdSW && this.cmdSW.elapsed(), + fileWalkTime: this.fileWalkSW.elapsed(), traversal: Traversal[this.traversal], - errors: this.errors, - fileWalkStartTime: this.fileWalkStartTime, - fileWalkResultTime: Date.now(), directoriesWalked: this.directoriesWalked, filesWalked: this.filesWalked, - resultCount: this.resultCount, - cmdForkStartTime: this.cmdForkStartTime, - cmdForkResultTime: this.cmdForkResultTime, cmdResultCount: this.cmdResultCount }; } @@ -680,10 +676,9 @@ export class Engine implements ISearchEngine { this.walker = new FileWalker(config); } - public search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void { + public search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void { this.walker.walk(this.folderQueries, this.extraFiles, onResult, onProgress, (err: Error, isLimitHit: boolean) => { done(err, { - type: 'success', limitHit: isLimitHit, stats: this.walker.getStats() }); diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index f2281f3d5c7..0f98cd5918a 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -9,18 +9,22 @@ import * as fs from 'fs'; import * as gracefulFs from 'graceful-fs'; import { join, sep } from 'path'; import * as arrays from 'vs/base/common/arrays'; +import { CancelablePromise, createCancelablePromise, toWinJsPromise } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; import * as objects from 'vs/base/common/objects'; +import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { TPromise } from 'vs/base/common/winjs.base'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; -import { ICachedSearchStats, IProgress } from 'vs/platform/search/common/search'; +import { ICachedSearchStats, IFileSearchStats, IProgress } from 'vs/platform/search/common/search'; import { Engine as FileSearchEngine, FileWalker } from 'vs/workbench/services/search/node/fileSearch'; import { RipgrepEngine } from 'vs/workbench/services/search/node/ripgrepTextSearch'; import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; -import { IFileSearchProgressItem, IRawFileMatch, IRawSearch, IRawSearchService, ISearchEngine, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ITelemetryEvent, ISerializedSearchSuccess } from './search'; -import { Event, Emitter } from 'vs/base/common/event'; +import { IFileSearchProgressItem, IRawFileMatch, IRawSearch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search'; gracefulFs.gracefulify(fs); @@ -35,16 +39,18 @@ export class SearchService implements IRawSearchService { private textSearchWorkerProvider: TextSearchWorkerProvider; - private _onTelemetry = new Emitter(); - readonly onTelemetry: Event = this._onTelemetry.event; - public fileSearch(config: IRawSearch, batchSize = SearchService.BATCH_SIZE): Event { - let promise: TPromise; + let promise: CancelablePromise; const emitter = new Emitter({ 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 } })); + promise = createCancelablePromise(token => { + return this.doFileSearch(FileSearchEngine, config, p => emitter.fire(p), token, batchSize); + }); + + promise.then( + c => emitter.fire(c), + err => emitter.fire({ type: 'error', error: { message: err.message, stack: err.stack } })); }, onLastListenerRemove: () => { promise.cancel(); @@ -55,12 +61,17 @@ export class SearchService implements IRawSearchService { } public textSearch(config: IRawSearch): Event { - let promise: TPromise; + let promise: CancelablePromise; const emitter = new Emitter({ 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 } })); + promise = createCancelablePromise(token => { + return (config.useRipgrep ? this.ripgrepTextSearch(config, p => emitter.fire(p), token) : this.legacyTextSearch(config, p => emitter.fire(p), token)); + }); + + promise.then( + c => emitter.fire(c), + err => emitter.fire({ type: 'error', error: { message: err.message, stack: err.stack } })); }, onLastListenerRemove: () => { promise.cancel(); @@ -70,11 +81,13 @@ export class SearchService implements IRawSearchService { return emitter.event; } - private ripgrepTextSearch(config: IRawSearch, progressCallback: IProgressCallback): TPromise { + private ripgrepTextSearch(config: IRawSearch, progressCallback: IProgressCallback, token: CancellationToken): Promise { config.maxFilesize = MAX_FILE_SIZE; let engine = new RipgrepEngine(config); - return new TPromise((c, e) => { + token.onCancellationRequested(() => engine.cancel()); + + return new Promise((c, e) => { // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned const collector = new BatchedCollector(SearchService.BATCH_SIZE, progressCallback); engine.search((match) => { @@ -90,12 +103,10 @@ export class SearchService implements IRawSearchService { c(stats); } }); - }, () => { - engine.cancel(); }); } - private legacyTextSearch(config: IRawSearch, progressCallback: IProgressCallback): TPromise { + private legacyTextSearch(config: IRawSearch, progressCallback: IProgressCallback, token: CancellationToken): Promise { if (!this.textSearchWorkerProvider) { this.textSearchWorkerProvider = new TextSearchWorkerProvider(); } @@ -113,14 +124,17 @@ export class SearchService implements IRawSearchService { }), this.textSearchWorkerProvider); - return this.doTextSearch(engine, progressCallback, SearchService.BATCH_SIZE); + return this.doTextSearch(engine, progressCallback, SearchService.BATCH_SIZE, token); } - doFileSearch(EngineClass: { new(config: IRawSearch): ISearchEngine; }, config: IRawSearch, progressCallback: IProgressCallback, batchSize?: number): TPromise { + doFileSearch(EngineClass: { new(config: IRawSearch): ISearchEngine; }, config: IRawSearch, progressCallback: IProgressCallback, token?: CancellationToken, batchSize?: number): TPromise { + let resultCount = 0; const fileProgressCallback: IFileProgressCallback = progress => { if (Array.isArray(progress)) { + resultCount += progress.length; progressCallback(progress.map(m => this.rawMatchToSearchItem(m))); } else if ((progress).relativePath) { + resultCount++; progressCallback(this.rawMatchToSearchItem(progress)); } else { progressCallback(progress); @@ -128,40 +142,47 @@ export class SearchService implements IRawSearchService { }; if (config.sortByScore) { - let sortedSearch = this.trySortedSearchFromCache(config, fileProgressCallback); + let sortedSearch = this.trySortedSearchFromCache(config, fileProgressCallback, token); if (!sortedSearch) { const walkerConfig = config.maxResults ? objects.assign({}, config, { maxResults: null }) : config; const engine = new EngineClass(walkerConfig); - sortedSearch = this.doSortedSearch(engine, config, progressCallback, fileProgressCallback); + sortedSearch = this.doSortedSearch(engine, config, progressCallback, fileProgressCallback, token); } return new TPromise((c, e) => { - process.nextTick(() => { // allow caller to register progress callback first - sortedSearch.then(([result, rawMatches]) => { - const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch)); - this.sendProgress(serializedMatches, progressCallback, batchSize); - c(result); - }, e); - }); - }, () => { - sortedSearch.cancel(); + sortedSearch.then(([result, rawMatches]) => { + const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch)); + this.sendProgress(serializedMatches, progressCallback, batchSize); + c(result); + }, e); }); } const engine = new EngineClass(config); - return this.doSearch(engine, fileProgressCallback, batchSize); + return this.doSearch(engine, fileProgressCallback, batchSize, token).then(complete => { + return { + limitHit: complete.limitHit, + type: 'success', + stats: { + detailStats: complete.stats, + type: 'searchProcess', + fromCache: false, + resultCount, + sortingTime: undefined + } + }; + }); } private rawMatchToSearchItem(match: IRawFileMatch): ISerializedFileMatch { return { path: match.base ? join(match.base, match.relativePath) : match.relativePath }; } - private doSortedSearch(engine: ISearchEngine, config: IRawSearch, progressCallback: IProgressCallback, fileProgressCallback: IFileProgressCallback): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { - let searchPromise: TPromise; + private doSortedSearch(engine: ISearchEngine, config: IRawSearch, progressCallback: IProgressCallback, fileProgressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { const emitter = new Emitter(); - let allResultsPromise = new TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]>((c, e) => { + let allResultsPromise = createCancelablePromise(token => { let results: IRawFileMatch[] = []; const innerProgressCallback: IFileProgressCallback = progress => { @@ -173,54 +194,55 @@ export class SearchService implements IRawSearchService { } }; - searchPromise = this.doSearch(engine, innerProgressCallback, -1) - .then(result => { - c([result, results]); - // __GDPR__TODO__ classify event - this._onTelemetry.fire({ - eventName: 'fileSearch', - data: result.stats - }); - }, e); - }, () => { - searchPromise.cancel(); + return this.doSearch(engine, innerProgressCallback, -1, token) + .then<[ISearchEngineSuccess, IRawFileMatch[]]>(result => { + return [result, results]; + }); }); let cache: Cache; if (config.cacheKey) { cache = this.getOrCreateCache(config.cacheKey); - cache.resultsToSearchCache[config.filePattern] = { + const cacheRow: ICacheRow = { promise: allResultsPromise, - event: emitter.event + event: emitter.event, + resolved: false }; - allResultsPromise.then(null, err => { + cache.resultsToSearchCache[config.filePattern] = cacheRow; + allResultsPromise.then(() => { + cacheRow.resolved = true; + }, err => { delete cache.resultsToSearchCache[config.filePattern]; }); + allResultsPromise = this.preventCancellation(allResultsPromise); } - let chained: TPromise; - return new TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]>((c, e) => { - chained = allResultsPromise.then(([result, results]) => { + return toWinJsPromise<[ISerializedSearchSuccess, IRawFileMatch[]]>( + allResultsPromise.then(([result, results]) => { const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); - const unsortedResultTime = Date.now(); - return this.sortResults(config, results, scorerCache) - .then(sortedResults => { - const sortedResultTime = Date.now(); + const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(false); + return this.sortResults(config, results, scorerCache, token) + .then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => { + // sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened. + // Contrasting with findFiles which is not sorted and will have sortingTime: undefined + const sortingTime = sortSW ? sortSW.elapsed() : -1; - c([{ + return [{ type: 'success', - stats: objects.assign({}, result.stats, { - unsortedResultTime, - sortedResultTime - }), + stats: { + detailStats: result.stats, + sortingTime, + fromCache: false, + type: 'searchProcess', + workspaceFolderCount: config.folderQueries.length, + resultCount: sortedResults.length + }, limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults - } as ISerializedSearchSuccess, sortedResults]); + } as ISerializedSearchSuccess, sortedResults]; }); - }, e); - }, () => { - chained.cancel(); - }); + }) + ); } private getOrCreateCache(cacheKey: string): Cache { @@ -231,56 +253,42 @@ export class SearchService implements IRawSearchService { return this.caches[cacheKey] = new Cache(); } - private trySortedSearchFromCache(config: IRawSearch, progressCallback: IFileProgressCallback): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { + private trySortedSearchFromCache(config: IRawSearch, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { const cache = config.cacheKey && this.caches[config.cacheKey]; if (!cache) { return undefined; } - const cacheLookupStartTime = Date.now(); - const cached = this.getResultsFromCache(cache, config.filePattern, progressCallback); + const cached = this.getResultsFromCache(cache, config.filePattern, progressCallback, token); if (cached) { - let chained: TPromise; - return new TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]>((c, e) => { - chained = cached.then(([result, results, cacheStats]) => { - const cacheLookupResultTime = Date.now(); - return this.sortResults(config, results, cache.scorerCache) - .then(sortedResults => { - const sortedResultTime = Date.now(); + return cached.then(([result, results, cacheStats]) => { + const sortSW = StopWatch.create(false); + return this.sortResults(config, results, cache.scorerCache, token) + .then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => { + const sortingTime = sortSW.elapsed(); + const stats: IFileSearchStats = { + fromCache: true, + detailStats: cacheStats, + type: 'searchProcess', + resultCount: results.length, + sortingTime + }; - const stats: ICachedSearchStats = { - fromCache: true, - cacheLookupStartTime: cacheLookupStartTime, - cacheFilterStartTime: cacheStats.cacheFilterStartTime, - cacheLookupResultTime: cacheLookupResultTime, - cacheEntryCount: cacheStats.cacheFilterResultCount, - resultCount: results.length - }; - if (config.sortByScore) { - stats.unsortedResultTime = cacheLookupResultTime; - stats.sortedResultTime = sortedResultTime; - } - if (!cacheStats.cacheWasResolved) { - stats.joined = result.stats; - } - c([ - { - type: 'success', - limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults, - stats: stats - } as ISerializedSearchSuccess, - sortedResults - ]); - }); - }, e); - }, () => { - chained.cancel(); + return [ + { + type: 'success', + limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults, + stats + } as ISerializedSearchSuccess, + sortedResults + ]; + }); }); } return undefined; } - private sortResults(config: IRawSearch, results: IRawFileMatch[], scorerCache: ScorerCache): TPromise { + private sortResults(config: IRawSearch, results: IRawFileMatch[], scorerCache: ScorerCache, token?: CancellationToken): 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 @@ -288,7 +296,7 @@ export class SearchService implements IRawSearchService { const query = prepareQuery(config.filePattern); const compare = (matchA: IRawFileMatch, matchB: IRawFileMatch) => compareItemsByScore(matchA, matchB, query, true, FileMatchItemAccessor, scorerCache); - return arrays.topAsync(results, compare, config.maxResults, 10000); + return arrays.topAsync(results, compare, config.maxResults, 10000, token); } private sendProgress(results: ISerializedFileMatch[], progressCb: IProgressCallback, batchSize: number) { @@ -301,13 +309,13 @@ export class SearchService implements IRawSearchService { } } - private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback): TPromise<[ISerializedSearchSuccess, IRawFileMatch[], CacheStats]> { + private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]> { + const cacheLookupSW = StopWatch.create(false); + // Find cache entries by prefix of search value const hasPathSep = searchValue.indexOf(sep) >= 0; - let cachedRow: CacheRow; - let wasResolved: boolean; + let cachedRow: ICacheRow; 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(sep) < 0) { @@ -315,11 +323,10 @@ export class SearchService implements IRawSearchService { } const row = cache.resultsToSearchCache[previousSearch]; - row.promise.then(() => { wasResolved = false; }); - wasResolved = true; cachedRow = { promise: this.preventCancellation(row.promise), - event: row.event + event: row.event, + resolved: row.resolved }; break; } @@ -329,40 +336,48 @@ export class SearchService implements IRawSearchService { return null; } + const cacheLookupTime = cacheLookupSW.elapsed(); + const cacheFilterSW = StopWatch.create(false); + const listener = cachedRow.event(progressCallback); + if (token) { + token.onCancellationRequested(() => { + listener.dispose(); + }); + } - return new TPromise<[ISerializedSearchSuccess, IRawFileMatch[], CacheStats]>((c, e) => { - cachedRow.promise.then(([complete, cachedEntries]) => { - const cacheFilterStartTime = Date.now(); + return toWinJsPromise(cachedRow.promise.then<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]>(([complete, cachedEntries]) => { + if (token && token.isCancellationRequested) { + throw canceled(); + } - // Pattern match on results - let results: IRawFileMatch[] = []; - const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); - for (let i = 0; i < cachedEntries.length; i++) { - let entry = cachedEntries[i]; + // Pattern match on results + let results: IRawFileMatch[] = []; + 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); + // Check if this entry is a match for the search value + if (!strings.fuzzyContains(entry.relativePath, normalizedSearchValueLowercase)) { + continue; } - c([complete, results, { - cacheWasResolved: wasResolved, - cacheFilterStartTime: cacheFilterStartTime, - cacheFilterResultCount: cachedEntries.length - }]); - }, e); - }, () => { - cachedRow.promise.cancel(); - listener.dispose(); - }); + results.push(entry); + } + + return [complete, results, { + cacheWasResolved: cachedRow.resolved, + cacheLookupTime, + cacheFilterTime: cacheFilterSW.elapsed(), + cacheEntryCount: cachedEntries.length + }]; + })); } - private doTextSearch(engine: TextSearchEngine, progressCallback: IProgressCallback, batchSize: number): TPromise { - return new TPromise((c, e) => { + private doTextSearch(engine: TextSearchEngine, progressCallback: IProgressCallback, batchSize: number, token: CancellationToken): Promise { + token.onCancellationRequested(() => engine.cancel()); + + return new Promise((c, e) => { // Use BatchedCollector to get new results to the frontend every 2s at least, until 50 results have been returned const collector = new BatchedCollector(batchSize, progressCallback); engine.search((matches) => { @@ -376,17 +391,23 @@ export class SearchService implements IRawSearchService { if (error) { e(error); } else { - c(stats); + c({ + type: 'success', + limitHit: stats.limitHit, + stats: null + }); } }); - }, () => { - engine.cancel(); }); } - private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize?: number): TPromise { - return new TPromise((c, e) => { + private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): TPromise { + return new TPromise((c, e) => { let batch: IRawFileMatch[] = []; + if (token) { + token.onCancellationRequested(() => engine.cancel()); + } + engine.search((match) => { if (match) { if (batchSize) { @@ -400,21 +421,18 @@ export class SearchService implements IRawSearchService { } } }, (progress) => { - process.nextTick(() => { - progressCallback(progress); - }); - }, (error, stats) => { + progressCallback(progress); + }, (error, complete) => { if (batch.length) { progressCallback(batch); } + if (error) { e(error); } else { - c(stats); + c(complete); } }); - }, () => { - engine.cancel(); }); } @@ -423,26 +441,35 @@ export class SearchService implements IRawSearchService { 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. - }); + /** + * Return a CancelablePromise which is not actually cancelable + * TODO@rob - Is this really needed? + */ + private preventCancellation(promise: CancelablePromise): CancelablePromise { + return new class implements CancelablePromise { + cancel() { + // Do nothing + } + then(resolve, reject) { + return promise.then(resolve, reject); + } + catch(reject?) { + return this.then(undefined, reject); + } + }; } } -interface CacheRow { - promise: TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]>; +interface ICacheRow { + // TODO@roblou - never actually canceled + promise: CancelablePromise<[ISearchEngineSuccess, IRawFileMatch[]]>; + resolved: boolean; event: Event; } class Cache { - public resultsToSearchCache: { [searchValue: string]: CacheRow; } = Object.create(null); + public resultsToSearchCache: { [searchValue: string]: ICacheRow; } = Object.create(null); public scorerCache: ScorerCache = Object.create(null); } @@ -462,12 +489,6 @@ const FileMatchItemAccessor = new class implements IItemAccessor } }; -interface CacheStats { - cacheWasResolved: boolean; - cacheFilterStartTime: number; - cacheFilterResultCount: number; -} - /** * Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every * set of items collected. diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index c9e4a7f498c..94751dc40b8 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -16,9 +16,10 @@ import * as strings from 'vs/base/common/strings'; import { TPromise } from 'vs/base/common/winjs.base'; import * as encoding from 'vs/base/node/encoding'; import * as extfs from 'vs/base/node/extfs'; -import { IProgress } from 'vs/platform/search/common/search'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { IProgress, ITextSearchPreviewOptions, ITextSearchStats, TextSearchResult } from 'vs/platform/search/common/search'; import { rgPath } from 'vscode-ripgrep'; -import { FileMatch, IFolderSearch, IRawSearch, ISerializedFileMatch, LineMatch, ISerializedSearchSuccess } from './search'; +import { FileMatch, IFolderSearch, IRawSearch, ISerializedFileMatch, ISerializedSearchSuccess } from './search'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); @@ -50,7 +51,9 @@ export class RipgrepEngine { done(null, { type: 'success', limitHit: false, - stats: null + stats: { + type: 'searchProcess' + } }); return; } @@ -61,22 +64,21 @@ export class RipgrepEngine { } const cwd = platform.isWindows ? 'c:/' : '/'; - process.nextTick(() => { // Allow caller to register progress callback - const escapedArgs = rgArgs.args - .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) - .join(' '); + const escapedArgs = rgArgs.args + .map(arg => arg.match(/^-/) ? arg : `'${arg}'`) + .join(' '); - let rgCmd = `rg ${escapedArgs}\n - cwd: ${cwd}`; - if (rgArgs.siblingClauses) { - rgCmd += `\n - Sibling clauses: ${JSON.stringify(rgArgs.siblingClauses)}`; - } + let rgCmd = `rg ${escapedArgs}\n - cwd: ${cwd}`; + if (rgArgs.siblingClauses) { + rgCmd += `\n - Sibling clauses: ${JSON.stringify(rgArgs.siblingClauses)}`; + } + + onMessage({ message: rgCmd }); - onMessage({ message: rgCmd }); - }); this.rgProc = cp.spawn(rgDiskPath, rgArgs.args, { cwd }); process.once('exit', this.killRgProcFn); - this.ripgrepParser = new RipgrepParser(this.config.maxResults, cwd, this.config.extraFiles); + this.ripgrepParser = new RipgrepParser(this.config.maxResults, cwd, this.config.extraFiles, this.config.previewOptions); this.ripgrepParser.on('result', (match: ISerializedFileMatch) => { if (this.postProcessExclusions) { const handleResultP = (>this.postProcessExclusions(match.path, undefined, glob.hasSiblingPromiseFn(() => getSiblings(match.path)))) @@ -97,7 +99,9 @@ export class RipgrepEngine { done(null, { type: 'success', limitHit: true, - stats: null + stats: { + type: 'searchProcess' + } }); }); @@ -149,12 +153,17 @@ export class RipgrepEngine { * "failed" when a fatal error was produced. */ export function rgErrorMsgForDisplay(msg: string): string | undefined { - const firstLine = msg.split('\n')[0].trim(); + const lines = msg.trim().split('\n'); + const firstLine = lines[0].trim(); if (strings.startsWith(firstLine, 'Error parsing regex')) { return firstLine; } + if (strings.startsWith(firstLine, 'regex parse error')) { + return strings.uppercaseFirstLetter(lines[lines.length - 1].trim()); + } + if (strings.startsWith(firstLine, 'error parsing glob') || strings.startsWith(firstLine, 'unsupported encoding')) { // Uppercase first letter @@ -189,7 +198,7 @@ export class RipgrepParser extends EventEmitter { private numResults = 0; - constructor(private maxResults: number, private rootFolder: string, extraFiles?: string[]) { + constructor(private maxResults: number, private rootFolder: string, extraFiles?: string[], private previewOptions?: ITextSearchPreviewOptions) { super(); this.stringDecoder = new StringDecoder(); @@ -267,7 +276,6 @@ export class RipgrepParser extends EventEmitter { text = strings.stripUTF8BOM(text); } - const lineMatch = new LineMatch(text, lineNum); if (!this.fileMatch) { // When searching a single file and no folderQueries, rg does not print the file line, so create it here const singleFile = this.extraSearchFiles[0]; @@ -278,8 +286,6 @@ export class RipgrepParser extends EventEmitter { this.fileMatch = this.getFileMatch(singleFile); } - this.fileMatch.addMatch(lineMatch); - let lastMatchEndPos = 0; let matchTextStartPos = -1; @@ -288,6 +294,7 @@ export class RipgrepParser extends EventEmitter { let textRealIdx = 0; let hitLimit = false; + const matchRanges: IRange[] = []; const realTextParts: string[] = []; for (let i = 0; i < text.length - (RipgrepParser.MATCH_END_MARKER.length - 1);) { @@ -303,7 +310,7 @@ export class RipgrepParser extends EventEmitter { const chunk = text.slice(matchTextStartPos, i); realTextParts.push(chunk); if (!hitLimit) { - lineMatch.addMatch(matchTextStartRealIdx, textRealIdx - matchTextStartRealIdx); + matchRanges.push(new Range(lineNum, matchTextStartRealIdx, lineNum, textRealIdx)); } matchTextStartPos = -1; @@ -328,7 +335,9 @@ export class RipgrepParser extends EventEmitter { // Replace preview with version without color codes const preview = realTextParts.join(''); - lineMatch.preview = preview; + matchRanges + .map(r => new TextSearchResult(preview, r, this.previewOptions)) + .forEach(m => this.fileMatch.addMatch(m)); if (hitLimit) { this.cancel(); @@ -502,6 +511,7 @@ function getRgArgs(config: IRawSearch) { } args.push('--no-config'); + args.push('--no-ignore-global'); // Folder to search args.push('--'); diff --git a/src/vs/workbench/services/search/node/search.ts b/src/vs/workbench/services/search/node/search.ts index 3bb3cc19b4d..e3f65aa8fbe 100644 --- a/src/vs/workbench/services/search/node/search.ts +++ b/src/vs/workbench/services/search/node/search.ts @@ -5,11 +5,11 @@ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IExpression } from 'vs/base/common/glob'; -import { IProgress, ILineMatch, IPatternInfo, ISearchStats } from 'vs/platform/search/common/search'; -import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { Event } from 'vs/base/common/event'; +import { IExpression } from 'vs/base/common/glob'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IFileSearchStats, IPatternInfo, IProgress, ISearchEngineStats, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats } from 'vs/platform/search/common/search'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; export interface IFolderSearch { folder: string; @@ -34,6 +34,7 @@ export interface IRawSearch { maxFilesize?: number; useRipgrep?: boolean; disregardIgnoreFiles?: boolean; + previewOptions?: ITextSearchPreviewOptions; } export interface ITelemetryEvent { @@ -45,7 +46,6 @@ export interface IRawSearchService { fileSearch(search: IRawSearch): Event; textSearch(search: IRawSearch): Event; clearCache(cacheKey: string): TPromise; - readonly onTelemetry: Event; } export interface IRawFileMatch { @@ -56,14 +56,19 @@ export interface IRawFileMatch { } export interface ISearchEngine { - search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void) => void; + search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void) => void; cancel: () => void; } export interface ISerializedSearchSuccess { type: 'success'; limitHit: boolean; - stats: ISearchStats; + stats: IFileSearchStats | ITextSearchStats; +} + +export interface ISearchEngineSuccess { + limitHit: boolean; + stats: ISearchEngineStats; } export interface ISerializedSearchError { @@ -92,7 +97,7 @@ export function isSerializedSearchSuccess(arg: ISerializedSearchComplete): arg i export interface ISerializedFileMatch { path: string; - lineMatches?: ILineMatch[]; + matches?: ITextSearchResult[]; numMatches?: number; } @@ -103,56 +108,22 @@ export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgres export class FileMatch implements ISerializedFileMatch { path: string; - lineMatches: LineMatch[]; + matches: ITextSearchResult[]; constructor(path: string) { this.path = path; - this.lineMatches = []; + this.matches = []; } - addMatch(lineMatch: LineMatch): void { - this.lineMatches.push(lineMatch); + addMatch(match: ITextSearchResult): void { + this.matches.push(match); } serialize(): ISerializedFileMatch { - let lineMatches: ILineMatch[] = []; - let numMatches = 0; - - for (let i = 0; i < this.lineMatches.length; i++) { - numMatches += this.lineMatches[i].offsetAndLengths.length; - lineMatches.push(this.lineMatches[i].serialize()); - } - return { path: this.path, - lineMatches, - numMatches + matches: this.matches, + numMatches: this.matches.length }; } } - -export class LineMatch implements ILineMatch { - preview: string; - lineNumber: number; - offsetAndLengths: number[][]; - - constructor(preview: string, lineNumber: number) { - this.preview = preview.replace(/(\r|\n)*$/, ''); - this.lineNumber = lineNumber; - this.offsetAndLengths = []; - } - - addMatch(offset: number, length: number): void { - this.offsetAndLengths.push([offset, length]); - } - - serialize(): ILineMatch { - const result = { - preview: this.preview, - lineNumber: this.lineNumber, - offsetAndLengths: this.offsetAndLengths - }; - - return result; - } -} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/searchIpc.ts b/src/vs/workbench/services/search/node/searchIpc.ts index 3450ab3e2fd..1cc62837237 100644 --- a/src/vs/workbench/services/search/node/searchIpc.ts +++ b/src/vs/workbench/services/search/node/searchIpc.ts @@ -5,13 +5,12 @@ 'use strict'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IRawSearchService, IRawSearch, ISerializedSearchComplete, ISerializedSearchProgressItem, ITelemetryEvent } from './search'; import { Event } from 'vs/base/common/event'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IRawSearch, IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search'; export interface ISearchChannel extends IChannel { - listen(event: 'telemetry'): Event; listen(event: 'fileSearch', search: IRawSearch): Event; listen(event: 'textSearch', search: IRawSearch): Event; call(command: 'clearCache', cacheKey: string): TPromise; @@ -24,7 +23,6 @@ export class SearchChannel implements ISearchChannel { listen(event: string, arg?: any): Event { switch (event) { - case 'telemetry': return this.service.onTelemetry; case 'fileSearch': return this.service.fileSearch(arg); case 'textSearch': return this.service.textSearch(arg); } @@ -41,8 +39,6 @@ export class SearchChannel implements ISearchChannel { export class SearchChannelClient implements IRawSearchService { - get onTelemetry(): Event { return this.channel.listen('telemetry'); } - constructor(private channel: ISearchChannel) { } fileSearch(search: IRawSearch): Event { diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index dad4ec17770..b27c2476c58 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -4,39 +4,45 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; 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 } from 'vs/base/common/map'; +import { ResourceMap, values } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; +import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import uri 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 { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; -import { FileMatch, IFileMatch, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, LineMatch, pathIncludedInQuery, QueryType } from 'vs/platform/search/common/search'; +import { FileMatch, ICachedSearchStats, IFileMatch, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, pathIncludedInQuery, QueryType, SearchProviderType, IFileSearchStats, TextSearchResult } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITelemetryEvent } from './search'; +import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; import { ISearchChannel, SearchChannelClient } from './searchIpc'; +import { Range } from 'vs/editor/common/core/range'; export class SearchService extends Disposable implements ISearchService { public _serviceBrand: any; private diskSearch: DiskSearch; - private readonly searchProviders: ISearchResultProvider[] = []; - private fileSearchProvider: ISearchResultProvider; + private readonly fileSearchProviders = new Map(); + private readonly textSearchProviders = new Map(); + private readonly fileIndexProviders = new Map(); constructor( @IModelService private modelService: IModelService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IEditorService private editorService: IEditorService, @IEnvironmentService environmentService: IEnvironmentService, @ITelemetryService private telemetryService: ITelemetryService, @IConfigurationService private configurationService: IConfigurationService, @@ -45,27 +51,22 @@ export class SearchService extends Disposable implements ISearchService { ) { 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; } + list.set(scheme, provider); + return toDisposable(() => { - if (scheme === 'file') { - this.fileSearchProvider = null; - } else { - const idx = this.searchProviders.indexOf(provider); - if (idx >= 0) { - this.searchProviders.splice(idx, 1); - } - } + list.delete(scheme); }); } @@ -121,43 +122,32 @@ export class SearchService extends Disposable implements ISearchService { } }; - const startTime = Date.now(); - const searchWithProvider = (provider: ISearchResultProvider) => TPromise.as(provider.search(query, onProviderProgress)); - 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(() => { - // 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; - } + const providerPromise = TPromise.join(providerActivations) + .then(() => this.searchWithProviders(query, onProviderProgress)) + .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]; - } + 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]); - }); - }); + 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]; const result: ISearchComplete = { @@ -190,6 +180,132 @@ export class SearchService extends Disposable implements ISearchService { }, () => combinedPromise && combinedPromise.cancel()); } + private searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void) { + const e2eSW = StopWatch.create(false); + + const diskSearchQueries: IFolderQuery[] = []; + const searchPs: TPromise[] = []; + + 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)); + } + }); + + const diskSearchExtraFileResources = query.extraFileResources && query.extraFileResources.filter(res => res.scheme === 'file'); + + if (diskSearchQueries.length || diskSearchExtraFileResources) { + const diskSearchQuery: ISearchQuery = { + ...query, + ...{ + folderQueries: diskSearchQueries + }, + extraFileResources: diskSearchExtraFileResources + }; + + searchPs.push(this.diskSearch.search(diskSearchQuery, onProviderProgress)); + } + + return TPromise.join(searchPs).then(completes => { + const endToEndTime = e2eSW.elapsed(); + this.logService.trace(`SearchService#search: ${endToEndTime}ms`); + completes.forEach(complete => { + this.sendTelemetry(query, endToEndTime, complete); + }); + return completes; + }); + } + + private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete: ISearchComplete): void { + const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === 'file'); + const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== 'file'); + const scheme = fileSchemeOnly ? 'file' : + otherSchemeOnly ? 'other' : + 'mixed'; + + if (query.type === QueryType.File && complete.stats) { + const fileSearchStats = complete.stats as IFileSearchStats; + if (fileSearchStats.fromCache) { + const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; + + /* __GDPR__ + "cachedSearchComplete" : { + "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cacheWasResolved" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "cacheLookupTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cacheFilterTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cacheEntryCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + } + */ + this.telemetryService.publicLog('cachedSearchComplete', { + resultCount: fileSearchStats.resultCount, + workspaceFolderCount: query.folderQueries.length, + type: fileSearchStats.type, + endToEndTime: endToEndTime, + sortingTime: fileSearchStats.sortingTime, + cacheWasResolved: cacheStats.cacheWasResolved, + cacheLookupTime: cacheStats.cacheLookupTime, + cacheFilterTime: cacheStats.cacheFilterTime, + cacheEntryCount: cacheStats.cacheEntryCount, + scheme + }); + } else { + const searchEngineStats: ISearchEngineStats = fileSearchStats.detailStats as ISearchEngineStats; + + /* __GDPR__ + "searchComplete" : { + "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "traversal" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "fileWalkTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "directoriesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + } + */ + this.telemetryService.publicLog('searchComplete', { + resultCount: fileSearchStats.resultCount, + workspaceFolderCount: query.folderQueries.length, + type: fileSearchStats.type, + endToEndTime: endToEndTime, + sortingTime: fileSearchStats.sortingTime, + traversal: searchEngineStats.traversal, + fileWalkTime: searchEngineStats.fileWalkTime, + directoriesWalked: searchEngineStats.directoriesWalked, + filesWalked: searchEngineStats.filesWalked, + cmdTime: searchEngineStats.cmdTime, + cmdResultCount: searchEngineStats.cmdResultCount, + scheme + }); + } + } + } + private getLocalResults(query: ISearchQuery): ResourceMap { const localResults = new ResourceMap(); @@ -201,6 +317,10 @@ export class SearchService extends Disposable implements ISearchService { return; } + if (!this.editorService.isOpen({ resource })) { + return; + } + // Support untitled files if (resource.scheme === Schemas.untitled) { if (!this.untitledEditorService.exists(resource)) { @@ -227,7 +347,10 @@ export class SearchService extends Disposable implements ISearchService { localResults.set(resource, fileMatch); matches.forEach((match) => { - fileMatch.lineMatches.push(new LineMatch(model.getLineContent(match.range.startLineNumber), match.range.startLineNumber - 1, [[match.range.startColumn - 1, match.range.endColumn - match.range.startColumn]])); + fileMatch.matches.push(new TextSearchResult( + model.getLineContent(match.range.startLineNumber), + new Range(match.range.startLineNumber - 1, match.range.startColumn - 1, match.range.startLineNumber - 1, match.range.endColumn), + query.previewOptions)); }); } else { localResults.set(resource, null); @@ -261,11 +384,12 @@ export class SearchService extends Disposable implements ISearchService { } public clearCache(cacheKey: string): TPromise { - return TPromise.join([ - ...this.searchProviders, - this.fileSearchProvider, - this.diskSearch - ].map(provider => provider && provider.clearCache(cacheKey))) + const clearPs = [ + this.diskSearch, + ...values(this.fileIndexProviders) + ].map(provider => provider && provider.clearCache(cacheKey)); + + return TPromise.join(clearPs) .then(() => { }); } } @@ -300,17 +424,13 @@ 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 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))) @@ -342,7 +462,8 @@ export class DiskSearch implements ISearchResultProvider { cacheKey: query.cacheKey, useRipgrep: query.useRipgrep, disregardIgnoreFiles: query.disregardIgnoreFiles, - ignoreSymlinks: query.ignoreSymlinks + ignoreSymlinks: query.ignoreSymlinks, + previewOptions: query.previewOptions }; for (const q of existingFolders) { @@ -420,10 +541,8 @@ export class DiskSearch implements ISearchResultProvider { private static createFileMatch(data: ISerializedFileMatch): FileMatch { let fileMatch = new FileMatch(uri.file(data.path)); - if (data.lineMatches) { - for (let j = 0; j < data.lineMatches.length; j++) { - fileMatch.lineMatches.push(new LineMatch(data.lineMatches[j].preview, data.lineMatches[j].lineNumber, data.lineMatches[j].offsetAndLengths)); - } + if (data.matches) { + fileMatch.matches.push(...data.matches); // TODO why } return fileMatch; } diff --git a/src/vs/workbench/services/search/node/textSearch.ts b/src/vs/workbench/services/search/node/textSearch.ts index e2a14693c19..02b15dafa4a 100644 --- a/src/vs/workbench/services/search/node/textSearch.ts +++ b/src/vs/workbench/services/search/node/textSearch.ts @@ -6,14 +6,12 @@ 'use strict'; import * as path from 'path'; - import { onUnexpectedError } from 'vs/base/common/errors'; import { IProgress } from 'vs/platform/search/common/search'; import { FileWalker } from 'vs/workbench/services/search/node/fileSearch'; - -import { ISerializedFileMatch, IRawSearch, ISearchEngine, ISerializedSearchSuccess } from './search'; -import { ISearchWorker } from './worker/searchWorkerIpc'; +import { IRawSearch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from './search'; import { ITextSearchWorkerProvider } from './textSearchWorkerProvider'; +import { ISearchWorker, ISearchWorkerSearchArgs } from './worker/searchWorkerIpc'; export class Engine implements ISearchEngine { @@ -60,7 +58,7 @@ export class Engine implements ISearchEngine { }); } - search(onResult: (match: ISerializedFileMatch[]) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void { + search(onResult: (match: ISerializedFileMatch[]) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void { this.workers = this.workerProvider.getWorkers(); this.initializeWorkers(); @@ -86,7 +84,6 @@ export class Engine implements ISearchEngine { if (!this.isDone && this.processedBytes === this.totalBytes && this.walkerIsDone) { this.isDone = true; done(this.walkerError, { - type: 'success', limitHit: this.limitReached, stats: this.walker.getStats() }); @@ -98,7 +95,7 @@ export class Engine implements ISearchEngine { this.nextWorker = (this.nextWorker + 1) % this.workers.length; const maxResults = this.config.maxResults && (this.config.maxResults - this.numResults); - const searchArgs = { absolutePaths: batch, maxResults, pattern: this.config.contentPattern, fileEncoding }; + const searchArgs: ISearchWorkerSearchArgs = { absolutePaths: batch, maxResults, pattern: this.config.contentPattern, fileEncoding, previewOptions: this.config.previewOptions }; worker.search(searchArgs).then(result => { if (!result || this.limitReached || this.isCanceled) { return unwind(batchBytes); diff --git a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts b/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts index 1b7b03ef9a7..16bc22f00b4 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 * as ipc from 'vs/base/parts/ipc/node/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { ISearchWorker, ISearchWorkerChannel, SearchWorkerChannelClient } from './worker/searchWorkerIpc'; +import { 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/node/worker/searchWorker.ts b/src/vs/workbench/services/search/node/worker/searchWorker.ts index cdda1b2b257..b1e340ac07b 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorker.ts +++ b/src/vs/workbench/services/search/node/worker/searchWorker.ts @@ -7,16 +7,17 @@ import * as fs from 'fs'; import * as gracefulFs from 'graceful-fs'; -gracefulFs.gracefulify(fs); - import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; import { TPromise } from 'vs/base/common/winjs.base'; -import { LineMatch, FileMatch } from '../search'; -import { UTF16le, UTF16be, UTF8, UTF8_with_bom, encodingExists, decode, bomLength, detectEncodingFromBuffer } from 'vs/base/node/encoding'; - +import { bomLength, decode, detectEncodingFromBuffer, encodingExists, UTF16be, UTF16le, UTF8, UTF8_with_bom } from 'vs/base/node/encoding'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextSearchPreviewOptions, TextSearchResult } from 'vs/platform/search/common/search'; +import { FileMatch } from '../search'; import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc'; +gracefulFs.gracefulify(fs); + interface ReadLinesOptions { bufferLength: number; encoding: string; @@ -95,7 +96,7 @@ export class SearchWorkerEngine { // Search in the given path, and when it's finished, search in the next path in absolutePaths const startSearchInFile = (absolutePath: string): TPromise => { - return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches)).then(fileResult => { + return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches), args.previewOptions).then(fileResult => { // Finish early if search is canceled if (this.isCanceled) { return; @@ -124,13 +125,12 @@ export class SearchWorkerEngine { this.isCanceled = true; } - private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number): TPromise { + private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number, previewOptions?: ITextSearchPreviewOptions): TPromise { let fileMatch: FileMatch = null; let limitReached = false; let numMatches = 0; const perLineCallback = (line: string, lineNumber: number) => { - let lineMatch: LineMatch = null; let match = contentPattern.exec(line); // Record all matches into file result @@ -139,12 +139,8 @@ export class SearchWorkerEngine { fileMatch = new FileMatch(absolutePath); } - if (lineMatch === null) { - lineMatch = new LineMatch(line, lineNumber); - fileMatch.addMatch(lineMatch); - } - - lineMatch.addMatch(match.index, match[0].length); + const lineMatch = new TextSearchResult(line, new Range(lineNumber, match.index, lineNumber, match.index + match[0].length), previewOptions); + fileMatch.addMatch(lineMatch); numMatches++; if (maxResults && numMatches >= maxResults) { @@ -177,8 +173,8 @@ export class SearchWorkerEngine { return clb(null); // return early if canceled or limit reached } - fs.read(fd, buffer, 0, buffer.length, null, (error: Error, bytesRead: number, buffer: NodeBuffer) => { - const decodeBuffer = (buffer: NodeBuffer, start: number, end: number): string => { + fs.read(fd, buffer, 0, buffer.length, null, (error: Error, bytesRead: number, buffer: Buffer) => { + const decodeBuffer = (buffer: Buffer, start: number, end: number): string => { if (options.encoding === UTF8 || options.encoding === UTF8_with_bom) { return buffer.toString(undefined, start, end); // much faster to use built in toString() when encoding is default } diff --git a/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts b/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts index 70aade1c801..5fed1210eec 100644 --- a/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts +++ b/src/vs/workbench/services/search/node/worker/searchWorkerIpc.ts @@ -6,9 +6,9 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; import { ISerializedFileMatch } from '../search'; -import { IPatternInfo } from 'vs/platform/search/common/search'; +import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; import { SearchWorker } from './searchWorker'; import { Event } from 'vs/base/common/event'; @@ -17,6 +17,7 @@ export interface ISearchWorkerSearchArgs { fileEncoding: string; absolutePaths: string[]; maxResults?: number; + previewOptions?: ITextSearchPreviewOptions; } export interface ISearchWorkerSearchResult { diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts index ac23cfa0c7e..8e2f789924c 100644 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts @@ -5,16 +5,13 @@ 'use strict'; -import * as path from 'path'; import * as assert from 'assert'; - +import * as path from 'path'; import * as arrays from 'vs/base/common/arrays'; import * as platform from 'vs/base/common/platform'; - -import { RipgrepParser, getAbsoluteGlob, fixDriveC, fixRegexEndingPattern } from 'vs/workbench/services/search/node/ripgrepTextSearch'; +import { fixDriveC, fixRegexEndingPattern, getAbsoluteGlob, RipgrepParser } from 'vs/workbench/services/search/node/ripgrepTextSearch'; import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; - suite('RipgrepParser', () => { const rootFolder = '/workspace'; const fileSectionEnd = '\n'; @@ -76,16 +73,40 @@ suite('RipgrepParser', () => { { numMatches: 2, path: path.join(rootFolder, 'a.txt'), - lineMatches: [ + matches: [ { - lineNumber: 0, - preview: 'beforematchafter', - offsetAndLengths: [[6, 5]] + preview: { + match: { + endColumn: 11, + endLineNumber: 0, + startColumn: 6, + startLineNumber: 0, + }, + text: 'beforematchafter' + }, + range: { + endColumn: 11, + endLineNumber: 0, + startColumn: 6, + startLineNumber: 0, + } }, { - lineNumber: 1, - preview: 'beforematchafter', - offsetAndLengths: [[6, 5]] + preview: { + match: { + endColumn: 11, + endLineNumber: 0, + startColumn: 6, + startLineNumber: 0, + }, + text: 'beforematchafter' + }, + range: { + endColumn: 11, + endLineNumber: 1, + startColumn: 6, + startLineNumber: 1, + } } ] }); @@ -157,7 +178,7 @@ suite('RipgrepParser', () => { test('Parses chunks broken in the middle of a multibyte character', () => { const text = getFileLine('foo/bar') + '\n' + getMatchLine(0, ['before漢', 'match', 'after']) + '\n'; - const buf = new Buffer(text); + const buf = Buffer.from(text); // Split the buffer at every possible position - it should still be parsed correctly for (let i = 0; i < buf.length; i++) { @@ -168,8 +189,9 @@ suite('RipgrepParser', () => { const results = parseInput(inputBufs); assert.equal(results.length, 1); - assert.equal(results[0].lineMatches.length, 1); - assert.deepEqual(results[0].lineMatches[0].offsetAndLengths, [[7, 5]]); + assert.equal(results[0].matches.length, 1); + assert.equal(results[0].matches[0].range.startColumn, 7); + assert.equal(results[0].matches[0].range.endColumn, 12); } }); }); 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 eb205675ab0..37264624d97 100644 --- a/src/vs/workbench/services/search/test/node/searchService.test.ts +++ b/src/vs/workbench/services/search/test/node/searchService.test.ts @@ -7,31 +7,28 @@ import * as assert from 'assert'; import * as path from 'path'; - -import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/search'; -import { ISearchEngine, IRawSearch, IRawFileMatch, ISerializedFileMatch, IFolderSearch, ISerializedSearchSuccess, ISerializedSearchProgressItem, ISerializedSearchComplete } from 'vs/workbench/services/search/node/search'; -import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; -import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { IProgress, ISearchEngineStats, IFileSearchStats } from 'vs/platform/search/common/search'; +import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService'; +import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search'; +import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; 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') } ]; -const stats: IUncachedSearchStats = { - fromCache: false, - resultCount: 4, +const stats: ISearchEngineStats = { traversal: 'node', - errors: [], - fileWalkStartTime: 0, - fileWalkResultTime: 1, + fileWalkTime: 0, + cmdTime: 1, directoriesWalked: 2, filesWalked: 3 }; @@ -46,13 +43,12 @@ class TestSearchEngine implements ISearchEngine { TestSearchEngine.last = this; } - public search(onResult: (match: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void { + public search(onResult: (match: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void { const self = this; (function next() { process.nextTick(() => { if (self.isCanceled) { done(null, { - type: 'success', limitHit: false, stats: stats }); @@ -61,7 +57,6 @@ class TestSearchEngine implements ISearchEngine { const result = self.result(); if (!result) { done(null, { - type: 'success', limitHit: false, stats: stats }); @@ -136,7 +131,7 @@ suite('SearchService', () => { } }; - return service.doFileSearch(Engine, rawSearch, cb, 10).then(() => { + return service.doFileSearch(Engine, rawSearch, cb, undefined, 10).then(() => { assert.deepStrictEqual(results, [10, 10, 5]); }); }); @@ -149,12 +144,12 @@ suite('SearchService', () => { const service = new RawSearchService(); function fileSearch(config: IRawSearch, batchSize: number): Event { - let promise: TPromise; + let promise: CancelablePromise; const emitter = new Emitter({ onFirstListenerAdd: () => { - promise = service.doFileSearch(Engine, config, p => emitter.fire(p), batchSize) - .then(c => emitter.fire(c), err => emitter.fire({ type: 'error', error: err })); + promise = createCancelablePromise(token => service.doFileSearch(Engine, config, p => emitter.fire(p), token, batchSize) + .then(c => emitter.fire(c), err => emitter.fire({ type: 'error', error: err }))); }, onLastListenerRemove: () => { promise.cancel(); @@ -242,7 +237,7 @@ suite('SearchService', () => { filePattern: 'bb', sortByScore: true, maxResults: 2 - }, cb, 1).then(() => { + }, cb, undefined, 1).then(() => { assert.notStrictEqual(typeof TestSearchEngine.last.config.maxResults, 'number'); assert.deepStrictEqual(results, [path.normalize('/some/where/bbc'), path.normalize('/some/where/bab')]); }); @@ -270,7 +265,7 @@ suite('SearchService', () => { filePattern: 'a', sortByScore: true, maxResults: 23 - }, cb, 10) + }, cb, undefined, 10) .then(() => { assert.deepStrictEqual(results, [10, 10, 3]); }); @@ -301,8 +296,8 @@ suite('SearchService', () => { filePattern: 'b', sortByScore: true, cacheKey: 'x' - }, cb, -1).then(complete => { - assert.strictEqual(complete.stats.fromCache, false); + }, cb, undefined, -1).then(complete => { + assert.strictEqual((complete.stats).fromCache, false); assert.deepStrictEqual(results, [path.normalize('/some/where/bcb'), path.normalize('/some/where/bbc'), path.normalize('/some/where/aab')]); }).then(() => { const results = []; @@ -318,8 +313,8 @@ suite('SearchService', () => { filePattern: 'bc', sortByScore: true, cacheKey: 'x' - }, cb, -1).then(complete => { - assert.ok(complete.stats.fromCache); + }, cb, undefined, -1).then(complete => { + assert.ok((complete.stats).fromCache); assert.deepStrictEqual(results, [path.normalize('/some/where/bcb'), path.normalize('/some/where/bbc')]); }, null); }).then(() => { @@ -344,10 +339,10 @@ suite('SearchService', () => { filePattern: 'bc', sortByScore: true, cacheKey: 'x' - }, cb, -1).then(complete => { - assert.strictEqual(complete.stats.fromCache, false); + }, cb, undefined, -1).then(complete => { + assert.strictEqual((complete.stats).fromCache, false); assert.deepStrictEqual(results, [path.normalize('/some/where/bc')]); }); }); }); -}); \ 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..df7d609571b 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); @@ -313,7 +313,7 @@ export class TextMateService implements ITextMateService { } const grammarLocation = resources.joinPath(extensionLocation, syntax.path); - if (grammarLocation.path.indexOf(extensionLocation.path) !== 0) { + if (!resources.isEqualOrParent(grammarLocation, extensionLocation)) { collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", grammarsExtPoint.name, grammarLocation.path, extensionLocation.path)); } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index dbf27ba83f8..b9e1a1a9937 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -25,7 +25,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { RunOnceScheduler, timeout } from 'vs/base/common/async'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; @@ -33,7 +33,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati 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'; +import { isEqual, isEqualOrParent } 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. @@ -42,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; } @@ -151,13 +153,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } if (fileEventImpactsModel && this.inOrphanMode !== newInOrphanModeGuess) { - let checkOrphanedPromise: TPromise; + let checkOrphanedPromise: Thenable; if (newInOrphanModeGuess) { // We have received reports of users seeing delete events even though the file still // exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665). // Since we do not want to mark the model as orphaned, we have to check if the // file is really gone and not just a faulty file event. - checkOrphanedPromise = TPromise.timeout(100).then(() => { + checkOrphanedPromise = timeout(100).then(() => { if (this.disposed) { return true; } @@ -165,10 +167,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.fileService.existsFile(this.resource).then(exists => !exists); }); } else { - checkOrphanedPromise = TPromise.as(false); + checkOrphanedPromise = Promise.resolve(false); } - checkOrphanedPromise.done(newInOrphanModeValidated => { + checkOrphanedPromise.then(newInOrphanModeValidated => { if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) { this.setOrphaned(newInOrphanModeValidated); } @@ -350,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; @@ -717,24 +715,23 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil 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 @@ -754,6 +751,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit File Saved Event this._onDidStateChange.fire(StateChange.SAVED); }, error => { + 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 @@ -773,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 (isEqual(this.resource, URI.file(this.environmentService.appSettingsPath), !isLinux)) { - return true; + 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')))) { + return 'snippets'; } // Check for workspace settings file - return this.contextService.getWorkspace().folders.some(folder => { - return isEqualOrParent(this.resource, folder.toResource('.vscode'), hasToIgnoreCase(this.resource)); - }); + const folders = this.contextService.getWorkspace().folders; + for (let i = 0; i < folders.length; i++) { + if (isEqualOrParent(this.resource, folders[i].toResource('.vscode'))) { + 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 { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index f77b7d2ecc1..933b605bfb8 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -733,7 +733,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // Handle target models if existing (if target URI is a folder, this can be multiple) let handleTargetModelPromise: TPromise = TPromise.as(void 0); - const dirtyTargetModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), target, !platform.isLinux /* ignorecase */)); + const dirtyTargetModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), target, false /* do not ignorecase, see https://github.com/Microsoft/vscode/issues/56384 */)); if (dirtyTargetModels.length) { handleTargetModelPromise = this.revertAll(dirtyTargetModels.map(targetModel => targetModel.getResource()), { soft: true }); } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 132e8b15c47..008c6d19155 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -254,8 +254,6 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport isResolved(): boolean; - isReadonly(): boolean; - isDisposed(): boolean; } diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index c7e655da6e1..76cd245f08a 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -86,7 +86,6 @@ class ResourceModelCollection extends ReferenceCollection { if (!model) { - console.error(`Unable to open '${resource}' resource is not available.`); // TODO PII return TPromise.wrapError(new Error('resource is not available')); } diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 8d8a6df7b78..fb288d66132 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -59,6 +59,7 @@ export interface IWorkbenchThemeService extends IThemeService { getColorTheme(): IColorTheme; getColorThemes(): TPromise; onDidColorThemeChange: Event; + restoreColorTheme(); setFileIconTheme(iconThemeId: string, settingsTarget: ConfigurationTarget): TPromise; getFileIconTheme(): IFileIconTheme; diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts index 7a52cc4ef45..f49ab6eea33 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -189,9 +189,12 @@ export class ColorThemeData implements IColorTheme { return objects.equals(this.colorMap, other.colorMap) && objects.equals(this.tokenColors, other.tokenColors); } + get baseTheme(): string { + return this.id.split(' ')[0]; + } + get type(): ThemeType { - let baseTheme = this.id.split(' ')[0]; - switch (baseTheme) { + switch (this.baseTheme) { case VS_LIGHT_THEME: return 'light'; case VS_HC_THEME: return 'hc'; default: return 'dark'; @@ -272,7 +275,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 +328,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/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index 7029c57018f..202f8be0707 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -95,7 +95,7 @@ export class ColorThemeStore { } const colorThemeLocation = resources.joinPath(extensionLocation, theme.path); - if (colorThemeLocation.path.indexOf(extensionLocation.path) !== 0) { + if (!resources.isEqualOrParent(colorThemeLocation, extensionLocation)) { collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", themesExtPoint.name, colorThemeLocation.path, extensionLocation.path)); } 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/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 179ae3342b1..a16ed89c917 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -97,7 +97,7 @@ export class FileIconThemeStore { } const iconThemeLocation = resources.joinPath(extensionLocation, iconTheme.path); - if (iconThemeLocation.path.indexOf(extensionLocation.path) !== 0) { + if (!resources.isEqualOrParent(iconThemeLocation, extensionLocation)) { collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", iconThemeExtPoint.name, iconThemeLocation.path, extensionLocation.path)); } diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 8dcbaf381f8..91a9a231ffa 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -18,13 +18,10 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigu import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ColorThemeData } from './colorThemeData'; import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; -import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { Color } from 'vs/base/common/color'; import { Event, Emitter } from 'vs/base/common/event'; import * as colorThemeSchema from 'vs/workbench/services/themes/common/colorThemeSchema'; import * as fileIconThemeSchema from 'vs/workbench/services/themes/common/fileIconThemeSchema'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; import { ColorThemeStore } from 'vs/workbench/services/themes/electron-browser/colorThemeStore'; import { FileIconThemeStore } from 'vs/workbench/services/themes/electron-browser/fileIconThemeStore'; import { FileIconThemeData } from 'vs/workbench/services/themes/electron-browser/fileIconThemeData'; @@ -97,7 +94,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { container: HTMLElement, @IExtensionService extensionService: IExtensionService, @IStorageService private storageService: IStorageService, - @IBroadcastService private broadcastService: IBroadcastService, @IConfigurationService private configurationService: IConfigurationService, @ITelemetryService private telemetryService: ITelemetryService, @IWindowService private windowService: IWindowService, @@ -130,9 +126,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (persistedThemeData) { themeData = ColorThemeData.fromStorageData(persistedThemeData); } - if (!themeData) { - let isLightTheme = (Array.prototype.indexOf.call(document.body.classList, 'vs') >= 0); - themeData = ColorThemeData.createUnloadedTheme(isLightTheme ? VS_LIGHT_THEME : VS_DARK_THEME); + let containerBaseTheme = this.getBaseThemeFromContainer(); + if (!themeData || themeData && themeData.baseTheme !== containerBaseTheme) { + themeData = ColorThemeData.createUnloadedTheme(containerBaseTheme); } themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); @@ -305,6 +301,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { }); } + public restoreColorTheme() { + let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); + if (colorThemeSetting !== this.currentColorTheme.settingsId) { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, null).then(theme => { + if (theme) { + this.setColorTheme(theme.id, null); + } + }); + } + } + private updateDynamicCSSRules(themeData: ITheme) { let cssRules: string[] = []; let hasRule: { [rule: string]: boolean } = {}; @@ -342,11 +349,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.onColorThemeChange.fire(this.currentColorTheme); - if (settingsTarget !== ConfigurationTarget.WORKSPACE) { - let background = Color.Format.CSS.formatHex(newTheme.getColor(editorBackground)); // only take RGB, its what is used in the initial CSS - let data = { id: newTheme.id, background: background }; - this.broadcastService.broadcast({ channel: 'vscode:changeColorTheme', payload: JSON.stringify(data) }); - } // remember theme data for a quick restore this.storageService.store(PERSISTED_THEME_STORAGE_KEY, newTheme.toStorageData()); @@ -449,6 +451,18 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } return this._configurationWriter; } + + private getBaseThemeFromContainer() { + if (this.container) { + for (let i = this.container.classList.length - 1; i >= 0; i--) { + const item = document.body.classList.item(i); + if (item === VS_LIGHT_THEME || item === VS_DARK_THEME || item === VS_HC_THEME) { + return item; + } + } + } + return VS_DARK_THEME; + } } function _applyIconTheme(data: FileIconThemeData, onApply: (theme: FileIconThemeData) => TPromise): TPromise { diff --git a/src/vs/workbench/services/timer/common/timerService.ts b/src/vs/workbench/services/timer/common/timerService.ts deleted file mode 100644 index 84842c4eb00..00000000000 --- a/src/vs/workbench/services/timer/common/timerService.ts +++ /dev/null @@ -1,98 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const ITimerService = createDecorator('timerService'); - -/* __GDPR__FRAGMENT__ - "IMemoryInfo" : { - "workingSetSize" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "peakWorkingSetSize": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "privateBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "sharedBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } - } -*/ -export interface IMemoryInfo { - workingSetSize: number; - peakWorkingSetSize: number; - privateBytes: number; - sharedBytes: number; -} - -/* __GDPR__FRAGMENT__ - "IStartupMetrics" : { - "version" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "ellapsed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedAppReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWindowLoad" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWindowLoadToRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedViewletRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedEditorRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedTimersToTimersComputed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedNlsGeneration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "platform" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "release" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "arch" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "totalmem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "freemem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "meminfo" : { "${inline}": [ "${IMemoryInfo}" ] }, - "cpus.count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cpus.speed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cpus.model" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "initialStartup" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "hasAccessibilitySupport" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "isVMLikelyhood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "emptyWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "loadavg" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } -*/ -export interface IStartupMetrics { - version: number; - ellapsed: number; - timers: { - ellapsedAppReady?: number; - ellapsedWindowLoad?: number; - ellapsedWindowLoadToRequire: number; - ellapsedExtensions: number; - ellapsedExtensionsReady: number; - ellapsedRequire: number; - ellapsedViewletRestore: number; - ellapsedEditorRestore: number; - ellapsedWorkbench: number; - ellapsedTimersToTimersComputed: number; - ellapsedNlsGeneration: number; - }; - platform: string; - release: string; - arch: string; - totalmem: number; - freemem: number; - meminfo: IMemoryInfo; - cpus: { count: number; speed: number; model: string; }; - initialStartup: boolean; - hasAccessibilitySupport: boolean; - isVMLikelyhood: number; - emptyWorkbench: boolean; - loadavg: number[]; -} - -export interface IInitData { - start: number; - windowLoad: number; - isInitialStartup: boolean; - hasAccessibilitySupport: boolean; -} - -export interface ITimerService extends IInitData { - _serviceBrand: any; - - readonly startupMetrics: IStartupMetrics; -} diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts new file mode 100644 index 00000000000..3134aa7889c --- /dev/null +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -0,0 +1,422 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { virtualMachineHint } from 'vs/base/node/id'; +import * as perf from 'vs/base/common/performance'; +import * as os from 'os'; +import { getAccessibilitySupport } from 'vs/base/browser/browser'; +import { AccessibilitySupport } from 'vs/base/common/platform'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + + +/* __GDPR__FRAGMENT__ + "IMemoryInfo" : { + "workingSetSize" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "peakWorkingSetSize": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "privateBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "sharedBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + } +*/ +export interface IMemoryInfo { + workingSetSize: number; + peakWorkingSetSize: number; + privateBytes: number; + sharedBytes: number; +} + +/* __GDPR__FRAGMENT__ + "IStartupMetrics" : { + "version" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "ellapsed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "isLatestVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "didUseCachedData": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "windowKind": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "windowCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "viewletId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "panelId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "editorIds": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "timers.ellapsedAppReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWindowLoad" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWindowLoadToRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedViewletRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedPanelRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedEditorRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedTimersToTimersComputed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedNlsGeneration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "platform" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "release" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "arch" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "totalmem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "freemem" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "meminfo" : { "${inline}": [ "${IMemoryInfo}" ] }, + "cpus.count" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cpus.speed" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cpus.model" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "initialStartup" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "hasAccessibilitySupport" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "isVMLikelyhood" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "emptyWorkbench" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "loadavg" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } +*/ +export interface IStartupMetrics { + + /** + * The version of these metrics. + */ + version: 2; + + /** + * If this started the main process and renderer or just a renderer (new or reloaded). + */ + initialStartup: boolean; + + /** + * No folder, no file, no workspace has been opened + */ + emptyWorkbench: boolean; + + /** + * This is the latest (stable/insider) version. Iff not we should ignore this + * measurement. + */ + isLatestVersion: boolean; + + /** + * Whether we asked for and V8 accepted cached data. + */ + didUseCachedData: boolean; + + /** + * How/why the window was created. See https://github.com/Microsoft/vscode/blob/d1f57d871722f4d6ba63e4ef6f06287121ceb045/src/vs/platform/lifecycle/common/lifecycle.ts#L50 + */ + windowKind: number; + + /** + * The total number of windows that have been restored/created + */ + windowCount: number; + + /** + * The active viewlet id or `undedined` + */ + viewletId: string; + + /** + * The active panel id or `undefined` + */ + panelId: string; + + /** + * The editor input types or `[]` + */ + editorIds: string[]; + + /** + * The time it took to create the workbench. + * + * * Happens in the main-process *and* the renderer-process + * * Measured with the *start* and `didStartWorkbench`-performance mark. The *start* is either the start of the + * main process or the start of the renderer. + * * This should be looked at carefully because times vary depending on + * * This being the first window, the only window, or a reloaded window + * * Cached data being present and used or not + * * The numbers and types of editors being restored + * * The numbers of windows being restored (when starting 'fresh') + * * The viewlet being restored (esp. when it's a contributed viewlet) + */ + ellapsed: number; + + /** + * Individual timers... + */ + timers: { + /** + * The time it took to receieve the [`ready`](https://electronjs.org/docs/api/app#event-ready)-event. Measured from the first line + * of JavaScript code till receiving that event. + * + * * Happens in the main-process + * * Measured with the `main:started` and `main:appReady` performance marks. + * * This can be compared between insider and stable builds. + * * This should be looked at per OS version and per electron version. + * * This is often affected by AV software (and can change with AV software updates outside of our release-cycle). + * * It is not our code running here and we can only observe what's happening. + */ + ellapsedAppReady?: number; + + /** + * The time it took to generate NLS data. + * + * * Happens in the main-process + * * Measured with the `nlsGeneration:start` and `nlsGeneration:end` performance marks. + * * This only happens when a non-english locale is being used. + * * It is our code running here and we should monitor this carefully for regressions. + */ + ellapsedNlsGeneration: number; + + /** + * The time it took to tell electron to open/restore a renderer (browser window). + * + * * Happens in the main-process + * * Measured with the `main:appReady` and `main:loadWindow` performance marks. + * * This can be compared between insider and stable builds. + * * It is our code running here and we should monitor this carefully for regressions. + */ + ellapsedWindowLoad?: number; + + /** + * The time it took to create a new renderer (browser window) and to initialize that to the point + * of load the main-bundle (`workbench.main.js`). + * + * * Happens in the main-process *and* the renderer-process + * * Measured with the `main:loadWindow` and `willLoadWorkbenchMain` performance marks. + * * This can be compared between insider and stable builds. + * * It is mostly not our code running here and we can only observe what's happening. + * + */ + ellapsedWindowLoadToRequire: number; + + /** + * The time it took to load the main-bundle of the workbench, e.g `workbench.main.js`. + * + * * Happens in the renderer-process + * * Measured with the `willLoadWorkbenchMain` and `didLoadWorkbenchMain` performance marks. + * * This varies *a lot* when V8 cached data could be used or not + * * This should be looked at with and without V8 cached data usage and per electron/v8 version + * * This is affected by the size of our code bundle (which grows about 3-5% per release) + */ + ellapsedRequire: number; + + /** + * The time it took to read extensions' package.json-files *and* interpret them (invoking + * the contribution points). + * + * * Happens in the renderer-process + * * Measured with the `willLoadExtensions` and `didLoadExtensions` performance marks. + * * Reading of package.json-files is avoided by caching them all in a single file (after the read, + * until another extension is installed) + * * Happens in parallel to other things, depends on async timing + * + * todo@joh/ramya this measures an artifical dealy we have added, see https://github.com/Microsoft/vscode/blob/2f07ddae8bf56e969e3f4ba1447258ebc999672f/src/vs/workbench/services/extensions/electron-browser/extensionService.ts#L311-L326 + */ + ellapsedExtensions: number; + + // the time from start till `didLoadExtensions` + // remove? + ellapsedExtensionsReady: number; + + /** + * The time it took to restore the viewlet. + * + * * Happens in the renderer-process + * * Measured with the `willRestoreViewlet` and `didRestoreViewlet` performance marks. + * * This should be looked at per viewlet-type/id. + * * Happens in parallel to other things, depends on async timing + */ + ellapsedViewletRestore: number; + + /** + * The time it took to restore the panel. + * + * * Happens in the renderer-process + * * Measured with the `willRestorePanel` and `didRestorePanel` performance marks. + * * This should be looked at per panel-type/id. + * * Happens in parallel to other things, depends on async timing + */ + ellapsedPanelRestore: number; + + /** + * The time it took to restore editors - that is text editor and complex editor likes the settings UI + * or webviews (markdown preview). + * + * * Happens in the renderer-process + * * Measured with the `willRestoreEditors` and `didRestoreEditors` performance marks. + * * This should be looked at per editor and per editor type. + * * Happens in parallel to other things, depends on async timing + * + * todo@joh/ramya We should probably measures each editor individually? + */ + ellapsedEditorRestore: number; + + /** + * The time it took to create the workbench. + * + * * Happens in the renderer-process + * * Measured with the `willStartWorkbench` and `didStartWorkbench` performance marks. + * + * todo@joh/ramya Not sure if this is useful because this includes too much + */ + ellapsedWorkbench: number; + + // the time it took to generate this object. + // remove? + ellapsedTimersToTimersComputed: number; + }; + + hasAccessibilitySupport: boolean; + isVMLikelyhood: number; + platform: string; + release: string; + arch: string; + totalmem: number; + freemem: number; + meminfo: IMemoryInfo; + cpus: { count: number; speed: number; model: string; }; + loadavg: number[]; +} + +export interface ITimerService { + _serviceBrand: any; + readonly startupMetrics: Promise; +} + +class TimerService implements ITimerService { + + _serviceBrand: any; + + private _startupMetrics: Promise; + + constructor( + @IWindowsService private readonly _windowsService: IWindowsService, + @IWindowService private readonly _windowService: IWindowService, + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IUpdateService private readonly _updateService: IUpdateService, + @IViewletService private readonly _viewletService: IViewletService, + @IPanelService private readonly _panelService: IPanelService, + @IEditorService private readonly _editorService: IEditorService, + ) { + } + + get startupMetrics(): Promise { + if (!this._startupMetrics) { + this._startupMetrics = Promise + .resolve(this._extensionService.whenInstalledExtensionsRegistered()) + .then(() => this._computeStartupMetrics()); + } + return this._startupMetrics; + } + + private async _computeStartupMetrics(): Promise { + + const now = Date.now(); + const initialStartup = !!this._windowService.getConfiguration().isInitialStartup; + const startMark = initialStartup ? 'main:started' : 'main:loadWindow'; + + let totalmem: number; + let freemem: number; + let cpus: { count: number; speed: number; model: string; }; + let platform: string; + let release: string; + let arch: string; + let loadavg: number[]; + let meminfo: IMemoryInfo; + let isVMLikelyhood: number; + + try { + totalmem = os.totalmem(); + freemem = os.freemem(); + platform = os.platform(); + release = os.release(); + arch = os.arch(); + loadavg = os.loadavg(); + meminfo = process.getProcessMemoryInfo(); + + isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); + + const rawCpus = os.cpus(); + if (rawCpus && rawCpus.length > 0) { + cpus = { count: rawCpus.length, speed: rawCpus[0].speed, model: rawCpus[0].model }; + } + } catch (error) { + // ignore, be on the safe side with these hardware method calls + } + + return { + version: 2, + ellapsed: perf.getDuration(startMark, 'didStartWorkbench'), + + // reflections + isLatestVersion: Boolean(await this._updateService.isLatestVersion()), + didUseCachedData: didUseCachedData(), + windowKind: this._lifecycleService.startupKind, + windowCount: await this._windowsService.getWindowCount(), + viewletId: this._viewletService.getActiveViewlet() ? this._viewletService.getActiveViewlet().getId() : undefined, + editorIds: this._editorService.visibleEditors.map(input => input.getTypeId()), + panelId: this._panelService.getActivePanel() ? this._panelService.getActivePanel().getId() : undefined, + + // timers + timers: { + ellapsedAppReady: initialStartup ? perf.getDuration('main:started', 'main:appReady') : undefined, + ellapsedNlsGeneration: initialStartup ? perf.getDuration('nlsGeneration:start', 'nlsGeneration:end') : undefined, + ellapsedWindowLoad: initialStartup ? perf.getDuration('main:appReady', 'main:loadWindow') : undefined, + ellapsedWindowLoadToRequire: perf.getDuration('main:loadWindow', 'willLoadWorkbenchMain'), + ellapsedRequire: perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'), + ellapsedExtensions: perf.getDuration('willLoadExtensions', 'didLoadExtensions'), + ellapsedEditorRestore: perf.getDuration('willRestoreEditors', 'didRestoreEditors'), + ellapsedViewletRestore: perf.getDuration('willRestoreViewlet', 'didRestoreViewlet'), + ellapsedPanelRestore: perf.getDuration('willRestorePanel', 'didRestorePanel'), + ellapsedWorkbench: perf.getDuration('willStartWorkbench', 'didStartWorkbench'), + ellapsedExtensionsReady: perf.getDuration(startMark, 'didLoadExtensions'), + ellapsedTimersToTimersComputed: Date.now() - now, + }, + + // system info + platform, + release, + arch, + totalmem, + freemem, + meminfo, + cpus, + loadavg, + initialStartup, + isVMLikelyhood, + hasAccessibilitySupport: getAccessibilitySupport() === AccessibilitySupport.Enabled, + emptyWorkbench: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY + }; + } +} + +export const ITimerService = createDecorator('timerService'); + +registerSingleton(ITimerService, TimerService); + +//#region cached data logic + +export function didUseCachedData(): boolean { + // We surely don't use cached data when we don't tell the loader to do so + if (!Boolean((global).require.getConfig().nodeCachedDataDir)) { + return false; + } + // whenever cached data is produced or rejected a onNodeCachedData-callback is invoked. That callback + // stores data in the `MonacoEnvironment.onNodeCachedData` global. See: + // https://github.com/Microsoft/vscode/blob/efe424dfe76a492eab032343e2fa4cfe639939f0/src/vs/workbench/electron-browser/bootstrap/index.js#L299 + if (!isFalsyOrEmpty(MonacoEnvironment.onNodeCachedData)) { + return false; + } + return true; +} + +declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }]; +declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] }; + +//#endregion diff --git a/src/vs/workbench/services/timer/node/timerService.ts b/src/vs/workbench/services/timer/node/timerService.ts deleted file mode 100644 index 1de3ba8f6cc..00000000000 --- a/src/vs/workbench/services/timer/node/timerService.ts +++ /dev/null @@ -1,109 +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 { ITimerService, IStartupMetrics, IInitData, IMemoryInfo } from 'vs/workbench/services/timer/common/timerService'; -import { virtualMachineHint } from 'vs/base/node/id'; -import * as perf from 'vs/base/common/performance'; -import * as os from 'os'; - -export class TimerService implements ITimerService { - - public _serviceBrand: any; - - public readonly start: number; - public readonly windowLoad: number; - - public readonly isInitialStartup: boolean; - public readonly hasAccessibilitySupport: boolean; - - private _startupMetrics: IStartupMetrics; - - constructor(initData: IInitData, private isEmptyWorkbench: boolean) { - this.start = initData.start; - this.windowLoad = initData.windowLoad; - - this.isInitialStartup = initData.isInitialStartup; - this.hasAccessibilitySupport = initData.hasAccessibilitySupport; - } - - get startupMetrics(): IStartupMetrics { - if (!this._startupMetrics) { - this._computeStartupMetrics(); - } - return this._startupMetrics; - } - - public _computeStartupMetrics(): void { - const now = Date.now(); - const initialStartup = !!this.isInitialStartup; - const start = initialStartup ? this.start : this.windowLoad; - - let totalmem: number; - let freemem: number; - let cpus: { count: number; speed: number; model: string; }; - let platform: string; - let release: string; - let arch: string; - let loadavg: number[]; - let meminfo: IMemoryInfo; - let isVMLikelyhood: number; - - try { - totalmem = os.totalmem(); - freemem = os.freemem(); - platform = os.platform(); - release = os.release(); - arch = os.arch(); - loadavg = os.loadavg(); - meminfo = process.getProcessMemoryInfo(); - - isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); - - const rawCpus = os.cpus(); - if (rawCpus && rawCpus.length > 0) { - cpus = { count: rawCpus.length, speed: rawCpus[0].speed, model: rawCpus[0].model }; - } - } catch (error) { - // ignore, be on the safe side with these hardware method calls - } - - let nlsStart = perf.getEntry('mark', 'nlsGeneration:start'); - let nlsEnd = perf.getEntry('mark', 'nlsGeneration:end'); - let nlsTime = nlsStart && nlsEnd ? nlsEnd.startTime - nlsStart.startTime : 0; - this._startupMetrics = { - version: 1, - ellapsed: perf.getEntry('mark', 'didStartWorkbench').startTime - start, - timers: { - ellapsedExtensions: perf.getDuration('willLoadExtensions', 'didLoadExtensions'), - ellapsedExtensionsReady: perf.getEntry('mark', 'didLoadExtensions').startTime - start, - ellapsedRequire: perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'), - ellapsedEditorRestore: perf.getDuration('willRestoreEditors', 'didRestoreEditors'), - ellapsedViewletRestore: perf.getDuration('willRestoreViewlet', 'didRestoreViewlet'), - ellapsedWorkbench: perf.getDuration('willStartWorkbench', 'didStartWorkbench'), - ellapsedWindowLoadToRequire: perf.getEntry('mark', 'willLoadWorkbenchMain').startTime - this.windowLoad, - ellapsedTimersToTimersComputed: Date.now() - now, - ellapsedNlsGeneration: nlsTime - }, - platform, - release, - arch, - totalmem, - freemem, - meminfo, - cpus, - loadavg, - initialStartup, - isVMLikelyhood, - hasAccessibilitySupport: !!this.hasAccessibilitySupport, - emptyWorkbench: this.isEmptyWorkbench - }; - - if (initialStartup) { - this._startupMetrics.timers.ellapsedAppReady = perf.getDuration('main:started', 'main:appReady'); - this._startupMetrics.timers.ellapsedWindowLoad = this.windowLoad - perf.getEntry('mark', 'main:appReady').startTime; - } - } -} diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index 02006a2397a..a6903bb9fb9 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -41,6 +41,11 @@ export interface IViewletService { */ getViewlet(id: string): ViewletDescriptor; + /** + * Returns all viewlets + */ + getAllViewlets(): ViewletDescriptor[]; + /** * Returns all enabled viewlets */ diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts index 979b3ca5f19..30f05343f48 100644 --- a/src/vs/workbench/services/viewlet/browser/viewletService.ts +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -92,7 +92,7 @@ export class ViewletService extends Disposable implements IViewletService { .filter(v => v.enabled); } - private getAllViewlets(): ViewletDescriptor[] { + getAllViewlets(): ViewletDescriptor[] { return this.viewletRegistry.getViewlets() .sort((v1, v2) => v1.order - v2.order); } diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index 6caf2e400a4..3f31b7e7555 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, hasToIgnoreCase } from 'vs/base/common/resources'; +import { isEqual } 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, hasToIgnoreCase(folder)))); + return (folders.some(folder => isEqual(folder, workspaceFolder.uri))); } return false; diff --git a/src/vs/workbench/test/browser/part.test.ts b/src/vs/workbench/test/browser/part.test.ts index 22c8a969e55..c4a78a473c6 100644 --- a/src/vs/workbench/test/browser/part.test.ts +++ b/src/vs/workbench/test/browser/part.test.ts @@ -6,13 +6,13 @@ 'use strict'; import * as assert from 'assert'; -import { Builder, $ } from 'vs/base/browser/builder'; import { Part } from 'vs/workbench/browser/part'; import * as Types from 'vs/base/common/types'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { StorageService, InMemoryLocalStorage } from 'vs/platform/storage/common/storageService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { append, $, hide } from 'vs/base/browser/dom'; class MyPart extends Part { @@ -42,21 +42,21 @@ class MyPart2 extends Part { } public createTitleArea(parent: HTMLElement): HTMLElement { - return $(parent).div(function (div) { - div.span({ - id: 'myPart.title', - innerHtml: 'Title' - }); - }).getHTMLElement(); + const titleContainer = append(parent, $('div')); + const titleLabel = append(titleContainer, $('span')); + titleLabel.id = 'myPart.title'; + titleLabel.innerHTML = 'Title'; + + return titleContainer; } public createContentArea(parent: HTMLElement): HTMLElement { - return $(parent).div(function (div) { - div.span({ - id: 'myPart.content', - innerHtml: 'Content' - }); - }).getHTMLElement(); + const contentContainer = append(parent, $('div')); + const contentSpan = append(contentContainer, $('span')); + contentSpan.id = 'myPart.content'; + contentSpan.innerHTML = 'Content'; + + return contentContainer; } } @@ -71,12 +71,12 @@ class MyPart3 extends Part { } public createContentArea(parent: HTMLElement): HTMLElement { - return $(parent).div(function (div) { - div.span({ - id: 'myPart.content', - innerHtml: 'Content' - }); - }).getHTMLElement(); + const contentContainer = append(parent, $('div')); + const contentSpan = append(contentContainer, $('span')); + contentSpan.id = 'myPart.content'; + contentSpan.innerHTML = 'Content'; + + return contentContainer; } } @@ -97,11 +97,12 @@ suite('Workbench parts', () => { }); test('Creation', function () { - let b = new Builder(document.getElementById(fixtureId)); - b.div().hide(); + let b = document.createElement('div'); + document.getElementById(fixtureId).appendChild(b); + hide(b); - let part = new MyPart(b.getHTMLElement()); - part.create(b.getHTMLElement()); + let part = new MyPart(b); + part.create(b); assert.strictEqual(part.getId(), 'myPart'); @@ -114,7 +115,7 @@ suite('Workbench parts', () => { part.shutdown(); // Re-Create to assert memento contents - part = new MyPart(b.getHTMLElement()); + part = new MyPart(b); memento = part.getMemento(storage); assert(memento); @@ -126,29 +127,31 @@ suite('Workbench parts', () => { delete memento.bar; part.shutdown(); - part = new MyPart(b.getHTMLElement()); + part = new MyPart(b); memento = part.getMemento(storage); assert(memento); assert.strictEqual(Types.isEmptyObject(memento), true); }); test('Part Layout with Title and Content', function () { - let b = new Builder(document.getElementById(fixtureId)); - b.div().hide(); + let b = document.createElement('div'); + document.getElementById(fixtureId).appendChild(b); + hide(b); let part = new MyPart2(); - part.create(b.getHTMLElement()); + part.create(b); assert(document.getElementById('myPart.title')); assert(document.getElementById('myPart.content')); }); test('Part Layout with Content only', function () { - let b = new Builder(document.getElementById(fixtureId)); - b.div().hide(); + let b = document.createElement('div'); + document.getElementById(fixtureId).appendChild(b); + hide(b); let part = new MyPart3(); - part.create(b.getHTMLElement()); + part.create(b); assert(!document.getElementById('myPart.title')); assert(document.getElementById('myPart.content')); 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 e524bcd9ef2..95113e157fe 100644 --- a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts @@ -16,7 +16,7 @@ import { FileKind } from 'vs/platform/files/common/files'; suite('Breadcrumb Model', function () { - const workspaceService = new TestContextService(new Workspace('ffff', 'Test', [new WorkspaceFolder({ uri: URI.parse('foo:/bar/baz/ws'), name: 'ws', index: 0 })])); + const workspaceService = new TestContextService(new Workspace('ffff', [new WorkspaceFolder({ uri: URI.parse('foo:/bar/baz/ws'), name: 'ws', index: 0 })])); const configService = new class extends TestConfigurationService { getValue(...args: any[]) { if (args[0] === 'breadcrumbs.filePath') { diff --git a/src/vs/workbench/test/browser/quickopen.test.ts b/src/vs/workbench/test/browser/quickopen.test.ts index fd71405603a..d17bd4fdc81 100644 --- a/src/vs/workbench/test/browser/quickopen.test.ts +++ b/src/vs/workbench/test/browser/quickopen.test.ts @@ -22,10 +22,6 @@ export class TestQuickOpenService implements IQuickOpenService { this.callback = callback; } - pick(arg: any, options?: any, token?: any): Promise { - return TPromise.as(null); - } - accept(): void { } 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/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 6638722bd03..763d55eb2b4 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -18,6 +18,7 @@ import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/ import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { NullLogService } from 'vs/platform/log/common/log'; import { assign } from 'vs/base/common/objects'; +import { Counter } from 'vs/base/common/numbers'; suite('ExtHostConfiguration', function () { @@ -33,7 +34,7 @@ suite('ExtHostConfiguration', function () { if (!shape) { shape = new class extends mock() { }; } - return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService()), createConfigurationData(contents)); + return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService(), new Counter()), createConfigurationData(contents)); } function createConfigurationData(contents: any): IConfigurationInitData { @@ -267,7 +268,7 @@ suite('ExtHostConfiguration', function () { test('inspect in no workspace context', function () { const testObject = new ExtHostConfiguration( new class extends mock() { }, - new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService()), + new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService(), new Counter()), { defaults: new ConfigurationModel({ 'editor': { @@ -314,7 +315,7 @@ suite('ExtHostConfiguration', function () { 'id': 'foo', 'folders': [aWorkspaceFolder(URI.file('foo'), 0)], 'name': 'foo' - }, new NullLogService()), + }, new NullLogService(), new Counter()), { defaults: new ConfigurationModel({ 'editor': { @@ -388,7 +389,7 @@ suite('ExtHostConfiguration', function () { 'id': 'foo', 'folders': [aWorkspaceFolder(firstRoot, 0), aWorkspaceFolder(secondRoot, 1)], 'name': 'foo' - }, new NullLogService()), + }, new NullLogService(), new Counter()), { defaults: new ConfigurationModel({ 'editor': { @@ -597,7 +598,7 @@ suite('ExtHostConfiguration', function () { 'id': 'foo', 'folders': [workspaceFolder], 'name': 'foo' - }, new NullLogService()), + }, new NullLogService(), new Counter()), createConfigurationData({ 'farboo': { 'config': false, 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..67628e45629 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:', () => { @@ -701,14 +650,15 @@ suite('ExtHostSearch', () => { const actualTextSearchResults: vscode.TextSearchResult[] = []; for (let fileMatch of actual) { // Make relative - for (let lineMatch of fileMatch.lineMatches) { - for (let [offset, length] of lineMatch.offsetAndLengths) { - actualTextSearchResults.push({ - preview: { text: lineMatch.preview, match: null }, - range: new Range(lineMatch.lineNumber, offset, lineMatch.lineNumber, length + offset), - uri: fileMatch.resource - }); - } + for (let lineMatch of fileMatch.matches) { + actualTextSearchResults.push({ + preview: { + text: lineMatch.preview.text, + match: new Range(lineMatch.preview.match.startLineNumber, lineMatch.preview.match.startColumn, lineMatch.preview.match.endLineNumber, lineMatch.preview.match.endColumn) + }, + range: new Range(lineMatch.range.startLineNumber, lineMatch.range.startColumn, lineMatch.range.endLineNumber, lineMatch.range.endColumn), + uri: fileMatch.resource + }); } } @@ -734,7 +684,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 +701,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 +714,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 +743,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 +784,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 +821,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 +852,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 +896,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 +960,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 +990,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 +1022,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 +1053,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 +1079,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 +1116,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/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts index 4568ce2703c..1b58bc98dd9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -15,6 +15,7 @@ import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { NullLogService } from 'vs/platform/log/common/log'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { Counter } from 'vs/base/common/numbers'; suite('ExtHostWorkspace', function () { @@ -41,7 +42,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath', function () { - const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/Applications/NewsWoWBot'), 0)], name: 'Test' }, new NullLogService()); + const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/Applications/NewsWoWBot'), 0)], name: 'Test' }, new NullLogService(), new Counter()); assertAsRelativePath(ws, '/Coding/Applications/NewsWoWBot/bernd/das/brot', 'bernd/das/brot'); assertAsRelativePath(ws, '/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart', @@ -55,7 +56,7 @@ suite('ExtHostWorkspace', function () { test('asRelativePath, same paths, #11402', function () { const root = '/home/aeschli/workspaces/samples/docker'; const input = '/home/aeschli/workspaces/samples/docker'; - const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService()); + const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService(), new Counter()); assertAsRelativePath(ws, (input), input); @@ -64,20 +65,20 @@ suite('ExtHostWorkspace', function () { }); test('asRelativePath, no workspace', function () { - const ws = new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService()); + const ws = new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService(), new Counter()); assertAsRelativePath(ws, (''), ''); assertAsRelativePath(ws, ('/foo/bar'), '/foo/bar'); }); test('asRelativePath, multiple folders', function () { - const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService()); + const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService(), new Counter()); assertAsRelativePath(ws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(ws, '/Coding/Two/files/out.txt', 'Two/files/out.txt'); assertAsRelativePath(ws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt'); }); test('slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', function () { - const mrws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService()); + const mrws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService(), new Counter()); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt'); assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -89,7 +90,7 @@ suite('ExtHostWorkspace', function () { assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true); assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false); - const srws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0)], name: 'Test' }, new NullLogService()); + const srws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0)], name: 'Test' }, new NullLogService(), new Counter()); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt'); assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt', false); assertAsRelativePath(srws, '/Coding/One/file.txt', 'One/file.txt', true); @@ -99,24 +100,24 @@ suite('ExtHostWorkspace', function () { }); test('getPath, legacy', function () { - let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService()); + let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService(), new Counter()); assert.equal(ws.getPath(), undefined); - ws = new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService()); + ws = new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService(), new Counter()); assert.equal(ws.getPath(), undefined); - ws = new ExtHostWorkspace(new TestRPCProtocol(), undefined, new NullLogService()); + ws = new ExtHostWorkspace(new TestRPCProtocol(), undefined, new NullLogService(), new Counter()); assert.equal(ws.getPath(), undefined); - ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('Folder'), 0), aWorkspaceFolderData(URI.file('Another/Folder'), 1)] }, new NullLogService()); + ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('Folder'), 0), aWorkspaceFolderData(URI.file('Another/Folder'), 1)] }, new NullLogService(), new Counter()); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); - ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('/Folder'), 0)] }, new NullLogService()); + ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('/Folder'), 0)] }, new NullLogService(), new Counter()); assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder'); }); test('WorkspaceFolder has name and index', function () { - const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService()); + const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService(), new Counter()); const [one, two] = ws.getWorkspaceFolders(); @@ -135,7 +136,7 @@ suite('ExtHostWorkspace', function () { aWorkspaceFolderData(URI.file('/Coding/Two'), 1), aWorkspaceFolderData(URI.file('/Coding/Two/Nested'), 2) ] - }, new NullLogService()); + }, new NullLogService(), new Counter()); let folder = ws.getWorkspaceFolder(URI.file('/foo/bar')); assert.equal(folder, undefined); @@ -175,7 +176,7 @@ suite('ExtHostWorkspace', function () { }); test('Multiroot change event should have a delta, #29641', function (done) { - let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService()); + let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService(), new Counter()); let finished = false; const finish = (error?: any) => { @@ -238,7 +239,7 @@ suite('ExtHostWorkspace', function () { }); test('Multiroot change keeps existing workspaces live', function () { - let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService()); + let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService(), new Counter()); let firstFolder = ws.getWorkspaceFolders()[0]; ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar2'), 0), aWorkspaceFolderData(URI.parse('foo:bar'), 1, 'renamed')] }); @@ -258,7 +259,7 @@ suite('ExtHostWorkspace', function () { }); test('updateWorkspaceFolders - invalid arguments', function () { - let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService()); + let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService(), new Counter()); assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, null, null)); assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 0)); @@ -267,7 +268,7 @@ suite('ExtHostWorkspace', function () { assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, -1, 0)); assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, -1, -1)); - ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService()); + ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService(), new Counter()); assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 1, 1)); assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2)); @@ -289,7 +290,7 @@ suite('ExtHostWorkspace', function () { assertRegistered: undefined }; - const ws = new ExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService()); + const ws = new ExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService(), new Counter()); // // Add one folder @@ -522,7 +523,7 @@ suite('ExtHostWorkspace', function () { } }; - let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService()); + let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService(), new Counter()); let sub = ws.onDidChangeWorkspace(e => { try { assert.throws(() => { @@ -545,7 +546,7 @@ suite('ExtHostWorkspace', function () { id: 'foo', name: 'Test', folders: [ aWorkspaceFolderData(URI.file('c:/Users/marek/Desktop/vsc_test/'), 0) ] - }, new NullLogService()); + }, new NullLogService(), new Counter()); assert.ok(ws.getWorkspaceFolder(URI.file('c:/Users/marek/Desktop/vsc_test/a.txt'))); assert.ok(ws.getWorkspaceFolder(URI.file('C:/Users/marek/Desktop/vsc_test/b.txt'))); 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 fdb4b5575c3..047e1cdd3da 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -28,6 +28,7 @@ import { BulkEditService } from 'vs/workbench/services/bulkEdit/electron-browser 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 { LabelService } from 'vs/platform/label/common/label'; suite('MainThreadEditors', () => { @@ -78,11 +79,12 @@ suite('MainThreadEditors', () => { const textEditorModel: ITextEditorModel = new class extends mock() { textEditorModel = modelService.getModel(resource); }; + textEditorModel.isReadonly = () => false; return TPromise.as(new ImmortalReference(textEditorModel)); } }; - const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), textModelService, new TestFileService(), textFileService, TestEnvironmentService, new TestContextService()); + const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), textModelService, new TestFileService(), textFileService, new LabelService(TestEnvironmentService, new TestContextService())); const rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock() { diff --git a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts index 4d9a092e6be..b62caa2691b 100644 --- a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts +++ b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts @@ -9,6 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { CharCode } from 'vs/base/common/charCode'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { isThenable } from 'vs/base/common/async'; export function SingleProxyRPCProtocol(thing: any): IExtHostContext { return { @@ -106,7 +107,7 @@ export class TestRPCProtocol implements IExtHostContext { let p: Thenable; try { let result = (instance[path]).apply(instance, wireArgs); - p = TPromise.is(result) ? result : TPromise.as(result); + p = isThenable(result) ? result : TPromise.as(result); } catch (err) { p = TPromise.wrapError(err); } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 14d5a319045..cd10de36a94 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -7,7 +7,7 @@ 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'; @@ -42,7 +42,7 @@ 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'; @@ -75,6 +75,7 @@ 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 { ILabelService, LabelService } from 'vs/platform/label/common/label'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, void 0); @@ -149,7 +150,7 @@ export class TestContextService implements IWorkspaceContextService { public isInsideWorkspace(resource: URI): boolean { if (resource && this.workspace) { - return resources.isEqualOrParent(resource, this.workspace.folders[0].uri, resources.hasToIgnoreCase(resource)); + return resources.isEqualOrParent(resource, this.workspace.folders[0].uri); } return false; @@ -160,7 +161,7 @@ export class TestContextService implements IWorkspaceContextService { } public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { - return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && resources.isEqual(this.workspace.folders[0].uri, workspaceIdentifier, resources.hasToIgnoreCase(workspaceIdentifier)); + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && resources.isEqual(this.workspace.folders[0].uri, workspaceIdentifier); } } @@ -241,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)); @@ -269,6 +271,7 @@ export function workbenchInstantiationService(): IInstantiationService { instantiationService.stub(IHashService, new TestHashService()); instantiationService.stub(ILogService, new TestLogService()); instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService([new TestEditorGroup(0)])); + instantiationService.stub(ILabelService, new LabelService(TestEnvironmentService, workspaceContextService)); const editorService = new TestEditorService(); instantiationService.stub(IEditorService, editorService); instantiationService.stub(ICodeEditorService, new TestCodeEditorService()); @@ -300,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 { } @@ -367,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); } } @@ -396,8 +399,6 @@ export class TestPartService implements IPartService { return this._onEditorLayout.event; } - public layout(): void { } - public isCreated(): boolean { return true; } @@ -450,6 +451,10 @@ export class TestPartService implements IPartService { return false; } + public getMenubarVisibility(): MenuBarVisibility { + return null; + } + public getSideBarPosition() { return 0; } @@ -596,7 +601,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; @@ -1087,7 +1092,7 @@ export class TestWindowService implements IWindowService { return TPromise.wrap(void 0); } - updateTouchBar(items: ISerializableCommandAction[][]): Promise { + updateTouchBar(items: ISerializableCommandAction[][]): TPromise { return TPromise.as(void 0); } } @@ -1192,11 +1197,11 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - addRecentlyOpened(files: string[]): TPromise { + addRecentlyOpened(files: URI[]): TPromise { return TPromise.as(void 0); } - removeFromRecentlyOpened(paths: string[]): TPromise { + removeFromRecentlyOpened(paths: URI[]): TPromise { return TPromise.as(void 0); } @@ -1285,27 +1290,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); } @@ -1336,10 +1345,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 2df703e1c1f..ce9193eb9cf 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/label/electron-browser/label.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 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..08c94b4287d 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).replace(/#/g, '%23'); +} + 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..34cb60f37ee 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", @@ -33,7 +33,7 @@ "rimraf": "^2.6.1", "strip-json-comments": "^2.0.1", "tmp": "0.0.33", - "typescript": "2.5.2", + "typescript": "2.9.2", "watch": "^1.0.2" } } diff --git a/test/smoke/src/areas/debug/debug.ts b/test/smoke/src/areas/debug/debug.ts index 86cd1ec88ab..529951db531 100644 --- a/test/smoke/src/areas/debug/debug.ts +++ b/test/smoke/src/areas/debug/debug.ts @@ -25,7 +25,7 @@ const DEBUG_STATUS_BAR = `.statusbar.debugging`; const NOT_DEBUG_STATUS_BAR = `.statusbar:not(debugging)`; const TOOLBAR_HIDDEN = `.debug-actions-widget.monaco-builder-hidden`; const STACK_FRAME = `${VIEWLET} .monaco-tree-row .stack-frame`; -const SPECIFIC_STACK_FRAME = filename => `${STACK_FRAME} .file[title$="${filename}"]`; +const SPECIFIC_STACK_FRAME = filename => `${STACK_FRAME} .file[title*="${filename}"]`; const VARIABLE = `${VIEWLET} .debug-variables .monaco-tree-row .expression`; const CONSOLE_OUTPUT = `.repl .output.expression .value`; const CONSOLE_INPUT_OUTPUT = `.repl .input-output-pair .output.expression .value`; diff --git a/test/smoke/src/areas/extensions/extensions.ts b/test/smoke/src/areas/extensions/extensions.ts index c904df46862..b3ad16827c8 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,13 +27,13 @@ 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 { await this.searchForExtension(name); const ariaLabel = `${name}. Press enter for extension details.`; await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${ariaLabel}"] .extension li[class='action-item'] .extension-action.install`); - await this.code.waitForElement(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${ariaLabel}"] .extension li[class='action-item'] .extension-action.reload`); + await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) .extension-action.reload`); } } \ No newline at end of file diff --git a/test/smoke/src/areas/preferences/settings.ts b/test/smoke/src/areas/preferences/settings.ts index ecdef71c033..b20102e1dbb 100644 --- a/test/smoke/src/areas/preferences/settings.ts +++ b/test/smoke/src/areas/preferences/settings.ts @@ -42,6 +42,6 @@ export class SettingsEditor { } private async openSettings(): Promise { - await this.quickopen.runCommand('Preferences: Open User Settings'); + await this.quickopen.runCommand('Preferences: Open Settings (JSON)'); } } \ 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..b2212a92797 100644 --- a/test/smoke/src/areas/statusbar/statusbar.test.ts +++ b/test/smoke/src/areas/statusbar/statusbar.test.ts @@ -35,17 +35,17 @@ export function setup() { await app.workbench.quickopen.openFile('app.js'); await app.workbench.statusbar.clickOn(StatusBarElement.INDENTATION_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.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(); + await app.workbench.quickinput.waitForQuickInputOpened(); + await app.workbench.quickinput.closeQuickInput(); }); it(`verifies that 'Problems View' appears when clicking on 'Problems' status element`, async function () { @@ -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/src/main.ts b/test/smoke/src/main.ts index 6ca50fe319c..dd17406a724 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -273,7 +273,7 @@ describe('Test', () => { const app = this.app as Application; const raw = await app.capturePage(); - const buffer = new Buffer(raw, 'base64'); + const buffer = Buffer.from(raw, 'base64'); const name = this.currentTest.fullTitle().replace(/[^a-z0-9\-]/ig, '_'); const screenshotPath = path.join(screenshotsPath, `${name}.png`); diff --git a/test/smoke/tools/copy-driver-definition.js b/test/smoke/tools/copy-driver-definition.js index fedf0c28432..643e60a67fe 100644 --- a/test/smoke/tools/copy-driver-definition.js +++ b/test/smoke/tools/copy-driver-definition.js @@ -7,7 +7,7 @@ const fs = require('fs'); const path = require('path'); const root = path.dirname(path.dirname(path.dirname(__dirname))); -const driverPath = path.join(root, 'src/vs/platform/driver/common/driver.ts'); +const driverPath = path.join(root, 'src/vs/platform/driver/node/driver.ts'); let contents = fs.readFileSync(driverPath, 'utf8'); contents = /\/\/\*START([\s\S]*)\/\/\*END/mi.exec(contents)[1].trim(); diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index c628b2089e2..57ac002dedb 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" @@ -1825,9 +1825,9 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34" +typescript@2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" uid-number@^0.0.6: version "0.0.6" diff --git a/test/tree/public/index.html b/test/tree/public/index.html index f676b7a8753..3716374c0de 100644 --- a/test/tree/public/index.html +++ b/test/tree/public/index.html @@ -13,6 +13,10 @@ .monaco-scrollable-element>.scrollbar>.slider { background: rgba(100, 100, 100, .4); } + + .tl-contents { + flex: 1; + } diff --git a/test/tree/server.js b/test/tree/server.js index b1105bfb741..11ac8e1ac9b 100644 --- a/test/tree/server.js +++ b/test/tree/server.js @@ -17,7 +17,7 @@ async function getTree(fsPath, level) { const element = path.basename(fsPath); const stat = await fs.stat(fsPath); - if (!stat.isDirectory() || element === '.git' || element === '.build' || level >= 3) { + if (!stat.isDirectory() || element === '.git' || element === '.build' || level >= 4) { return { element }; } diff --git a/yarn.lock b/yarn.lock index f3796744e72..cd85c0c3020 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,6 +43,161 @@ version "1.16.34" resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-1.16.34.tgz#a9761fff33d0f7b3fe61875b577778a2576a9a03" +"@types/tapable@*": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" + +"@types/uglify-js@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.3.tgz#801a5ca1dc642861f47c46d14b700ed2d610840b" + dependencies: + source-map "^0.6.1" + +"@types/webpack@^4.4.10": + version "4.4.10" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.10.tgz#2ecf12589142bc531549140612815b7d8b076358" + dependencies: + "@types/node" "*" + "@types/tapable" "*" + "@types/uglify-js" "*" + source-map "^0.6.0" + +"@webassemblyjs/ast@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" + dependencies: + "@webassemblyjs/helper-module-context" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/wast-parser" "1.5.13" + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/floating-point-hex-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298" + +"@webassemblyjs/helper-api-error@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59" + +"@webassemblyjs/helper-buffer@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e" + dependencies: + debug "^3.1.0" + +"@webassemblyjs/helper-code-frame@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58" + dependencies: + "@webassemblyjs/wast-printer" "1.5.13" + +"@webassemblyjs/helper-fsm@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924" + +"@webassemblyjs/helper-module-context@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5" + dependencies: + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747" + +"@webassemblyjs/helper-wasm-section@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/ieee754@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364" + dependencies: + ieee754 "^1.1.11" + +"@webassemblyjs/leb128@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee" + dependencies: + long "4.0.0" + +"@webassemblyjs/utf8@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469" + +"@webassemblyjs/wasm-edit@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/helper-wasm-section" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + "@webassemblyjs/wasm-opt" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + "@webassemblyjs/wast-printer" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/wasm-gen@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/ieee754" "1.5.13" + "@webassemblyjs/leb128" "1.5.13" + "@webassemblyjs/utf8" "1.5.13" + +"@webassemblyjs/wasm-opt@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/wasm-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-api-error" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/ieee754" "1.5.13" + "@webassemblyjs/leb128" "1.5.13" + "@webassemblyjs/utf8" "1.5.13" + +"@webassemblyjs/wast-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/floating-point-hex-parser" "1.5.13" + "@webassemblyjs/helper-api-error" "1.5.13" + "@webassemblyjs/helper-code-frame" "1.5.13" + "@webassemblyjs/helper-fsm" "1.5.13" + long "^3.2.0" + mamacro "^0.0.3" + +"@webassemblyjs/wast-printer@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/wast-parser" "1.5.13" + long "^3.2.0" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -58,6 +213,12 @@ accepts@~1.3.4: mime-types "~2.1.16" negotiator "0.6.1" +acorn-dynamic-import@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" + dependencies: + acorn "^5.0.0" + acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" @@ -72,6 +233,10 @@ acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" +acorn@^5.0.0, acorn@^5.6.2: + version "5.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" + acorn@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" @@ -86,6 +251,10 @@ ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" +ajv-keywords@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" + ajv@^4.7.0, ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -102,6 +271,15 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.1.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.1" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -122,6 +300,12 @@ amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" +ansi-colors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" + dependencies: + ansi-wrap "^0.1.0" + ansi-cyan@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" @@ -132,6 +316,10 @@ ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" +ansi-escapes@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" + ansi-gray@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" @@ -170,7 +358,13 @@ ansi-styles@^3.1.0: dependencies: color-convert "^1.9.0" -ansi-wrap@0.1.0: +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" @@ -181,11 +375,18 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + applicationinsights@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-0.18.0.tgz#162ebb48a383408bc4de44db32b417307f45bbc1" -aproba@^1.0.3: +aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -219,14 +420,26 @@ arr-diff@^2.0.0: dependencies: arr-flatten "^1.0.1" +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + arr-flatten@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + arr-union@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" @@ -273,7 +486,11 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" -arrify@^1.0.0: +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -294,9 +511,13 @@ 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.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" asn1@~0.2.3: version "0.2.3" @@ -306,14 +527,20 @@ 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" +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -336,14 +563,14 @@ 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" +atob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + atob@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" @@ -367,27 +594,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" @@ -423,6 +633,22 @@ base64-js@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -437,6 +663,10 @@ big-integer@^1.6.25: version "1.6.25" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.25.tgz#1de45a9f57542ac20121c682f8d642220a34e823" +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + binary-extensions@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" @@ -473,6 +703,14 @@ bl@~1.1.2: dependencies: readable-stream "~2.0.5" +bluebird@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + body-parser@1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" @@ -492,12 +730,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" @@ -531,14 +763,86 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +braces@^2.3.0, braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + "browser-request@>= 0.3.1 < 0.4.0": version "0.3.3" resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + browserify-mime@~1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f" +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: version "1.7.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" @@ -565,6 +869,22 @@ buffer-fill@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-0.1.1.tgz#76d825c4d6e50e06b7a31eb520c04d08cc235071" +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + buffers@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" @@ -579,10 +899,46 @@ builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" @@ -666,6 +1022,14 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0, chalk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" @@ -674,6 +1038,10 @@ chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chardet@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" + charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" @@ -689,6 +1057,25 @@ cheerio@^1.0.0-rc.1: lodash "^4.15.0" parse5 "^3.0.1" +chokidar@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + lodash.debounce "^4.0.8" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.5" + optionalDependencies: + fsevents "^1.2.2" + chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" @@ -700,6 +1087,12 @@ chrome-remote-interface@^0.25.3: commander "2.11.x" ws "3.3.x" +chrome-trace-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" + dependencies: + tslib "^1.9.0" + chromium-pickle-js@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" @@ -708,6 +1101,13 @@ ci-info@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.1.tgz#47b44df118c48d2597b56d342e7e25791060171a" +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + circular-json@^0.3.1: version "0.3.3" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" @@ -718,6 +1118,15 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + clean-css@3.4.6: version "3.4.6" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.6.tgz#fcb4f17057ddb7f8721616f70b07b294d95ffc45" @@ -731,6 +1140,12 @@ cli-cursor@^1.0.1: dependencies: restore-cursor "^1.0.1" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -801,6 +1216,13 @@ coffee-script@^1.10.0: version "1.12.7" resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53" +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^1.3.0, color-convert@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" @@ -851,12 +1273,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" @@ -879,7 +1295,7 @@ commander@2.8.x: dependencies: graceful-readlink ">= 1.0.0" -commander@^2.12.1: +commander@^2.12.1, commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -887,6 +1303,14 @@ commandpost@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/commandpost/-/commandpost-1.2.1.tgz#2e9c4c7508b9dc704afefaa91cab92ee6054cc68" +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -899,6 +1323,15 @@ concat-stream@1.6.0, concat-stream@^1.5.2: readable-stream "^2.2.2" typedarray "^0.0.6" +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + concat-with-sourcemaps@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.4.tgz#f55b3be2aeb47601b10a2d5259ccfb70fd2f1dd6" @@ -912,10 +1345,20 @@ config-chain@~1.1.5: ini "^1.3.4" proto-list "~1.2.1" +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -928,10 +1371,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" @@ -940,6 +1379,34 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +copy-webpack-plugin@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz#d53444a8fea2912d806e78937390ddd7e632ee5c" + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + globby "^7.1.1" + is-glob "^4.0.0" + loader-utils "^1.1.0" + minimatch "^3.0.4" + p-limit "^1.0.0" + serialize-javascript "^1.4.0" + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -954,6 +1421,34 @@ coveralls@^2.11.11: minimist "1.2.0" request "2.79.0" +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + cross-spawn@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" @@ -969,16 +1464,20 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" -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" @@ -991,6 +1490,22 @@ cryptiles@3.x.x: dependencies: boom "5.x.x" +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + cson-parser@^1.3.3: version "1.3.5" resolved "https://registry.yarnpkg.com/cson-parser/-/cson-parser-1.3.5.tgz#7ec675e039145533bf2a6a856073f1599d9c2d24" @@ -1077,10 +1592,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" @@ -1091,6 +1602,10 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" @@ -1103,6 +1618,10 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + dateformat@^1.0.11, dateformat@^1.0.7-1.2.3: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" @@ -1132,7 +1651,7 @@ debug@2.2.0, debug@~2.2.0: dependencies: ms "0.7.1" -debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0: +debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1148,6 +1667,16 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +decamelize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7" + dependencies: + xregexp "4.0.0" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" @@ -1188,6 +1717,25 @@ defaults@^1.0.0: dependencies: clone "^1.0.2" +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -1204,10 +1752,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" @@ -1232,6 +1776,13 @@ deprecated@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -1250,7 +1801,7 @@ detect-indent@^2.0.0: minimist "^1.1.0" repeating "^1.1.0" -detect-libc@^1.0.3: +detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -1266,6 +1817,21 @@ diff@^3.2.0: version "3.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + doctrine@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" @@ -1291,6 +1857,10 @@ dom-serializer@0, dom-serializer@~0.1.0: domelementtype "~1.1.1" entities "~1.1.1" +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + domelementtype@1, domelementtype@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" @@ -1338,6 +1908,15 @@ duplexify@^3.2.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + eachr@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/eachr/-/eachr-3.2.0.tgz#2c35e43ea086516f7997cf80b7aa64d55a4a4484" @@ -1395,6 +1974,22 @@ electron-to-chromium@^1.2.7: version "1.3.27" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" +elliptic@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" @@ -1411,6 +2006,14 @@ end-of-stream@~0.1.5: dependencies: once "~1.3.0" +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + tapable "^1.0.0" + entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" @@ -1419,6 +2022,12 @@ env-paths@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + dependencies: + prr "~1.0.1" + error-ex@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" @@ -1530,6 +2139,13 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint@^3.0.0, eslint@^3.4.0: version "3.19.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" @@ -1641,6 +2257,17 @@ event-stream@^3.1.7, event-stream@^3.3.1, event-stream@^3.3.4, event-stream@~3.3 stream-combiner "~0.0.4" through "~2.3.1" +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -1663,6 +2290,18 @@ expand-brackets@^0.1.4: dependencies: is-posix-bracket "^0.1.0" +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expand-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" @@ -1732,6 +2371,13 @@ extend-shallow@^2.0.1: dependencies: is-extendable "^0.1.0" +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -1740,12 +2386,33 @@ extend@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c" +external-editor@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6" + dependencies: + chardet "^0.5.0" + iconv-lite "^0.4.22" + tmp "^0.0.33" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" dependencies: is-extglob "^1.0.0" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + extract-opts@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/extract-opts/-/extract-opts-3.3.1.tgz#5abbedc98c0d5202e3278727f9192d7e086c6be1" @@ -1786,6 +2453,10 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -1815,6 +2486,12 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -1843,6 +2520,15 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + finalhandler@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" @@ -1855,6 +2541,14 @@ finalhandler@1.1.0: statuses "~1.3.1" unpipe "~1.0.0" +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + find-index@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" @@ -1876,6 +2570,12 @@ find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + dependencies: + locate-path "^3.0.0" + findup-sync@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" @@ -1916,11 +2616,18 @@ flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" +flush-write-stream@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.4" + for-in@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.5.tgz#007374e2b6d5c67420a1479bdb75a04872b738c4" -for-in@^1.0.1: +for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1936,22 +2643,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" @@ -1986,10 +2681,23 @@ forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" @@ -2019,10 +2727,32 @@ fs-extra@^2.0.0: graceful-fs "^4.1.2" jsonfile "^2.1.0" +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + dependencies: + minipass "^2.2.1" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" +fsevents@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + function-bind@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2076,6 +2806,10 @@ get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + getmac@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/getmac/-/getmac-1.4.1.tgz#cfefcb3ee7d7a73cba5292129cb100c19afbe17a" @@ -2115,7 +2849,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob-parent@^3.0.0: +glob-parent@^3.0.0, glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" dependencies: @@ -2213,6 +2947,10 @@ glob@~3.1.21: inherits "1" minimatch "~0.2.11" +global-modules-path@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.0.tgz#b0e2bac6beac39745f7db5c59d26a36a0b94f7dc" + global-modules@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" @@ -2244,6 +2982,17 @@ globby@^5.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + globule@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" @@ -2267,7 +3016,7 @@ gm@^1.14.2: cross-spawn "^4.0.0" debug "~2.2.0" -graceful-fs@4.1.11, graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@4.1.11, graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2683,6 +3432,10 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + has-gulplog@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" @@ -2693,20 +3446,52 @@ has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + has@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" dependencies: function-bind "^1.0.2" -hawk@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-1.0.0.tgz#b90bb169807285411da7ffcb8dd2598502d3b52d" +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" dependencies: - boom "0.4.x" - cryptiles "0.2.x" - hoek "0.9.x" - sntp "0.2.x" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" hawk@~3.1.3: version "3.1.3" @@ -2726,9 +3511,13 @@ 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" +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" hoek@2.x.x: version "2.16.3" @@ -2779,14 +3568,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" @@ -2803,6 +3584,10 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + 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" @@ -2823,16 +3608,41 @@ iconv-lite@0.4.19, iconv-lite@^0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" -iconv-lite@0.4.23: +iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" dependencies: safer-buffer ">= 2.1.2 < 3" +ieee754@^1.1.11, ieee754@^1.1.4: + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + dependencies: + minimatch "^3.0.4" + ignore@^3.2.0: version "3.3.7" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -2847,6 +3657,10 @@ indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2892,6 +3706,24 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" +inquirer@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.1.0.tgz#8f65c7b31c498285f4ddf3b742ad8c487892040b" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.0" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.1.0" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + int64-buffer@^0.1.9: version "0.1.9" resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.9.tgz#9e039da043b24f78b196b283e04653ef5e990f61" @@ -2900,6 +3732,10 @@ interpret@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.4.tgz#820cdd588b868ffb191a809506d6c9c8f212b1b0" +interpret@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -2923,6 +3759,18 @@ is-absolute@^0.2.3: is-relative "^0.2.1" is-windows "^0.2.0" +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -2937,7 +3785,7 @@ is-buffer@^1.0.2: version "1.1.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" -is-buffer@~1.1.1: +is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -2953,6 +3801,34 @@ is-ci@^1.0.9: dependencies: ci-info "^1.0.0" +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + is-dotfile@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" @@ -2967,11 +3843,17 @@ is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" -is-extglob@^2.1.0: +is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3003,6 +3885,12 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: version "2.16.1" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" @@ -3018,6 +3906,12 @@ is-number@^2.0.2, is-number@^2.1.0: dependencies: kind-of "^3.0.2" +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -3034,11 +3928,11 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" -is-plain-obj@^1.0.0: +is-plain-obj@^1.0.0, is-plain-obj@^1.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" -is-plain-object@^2.0.3: +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: @@ -3052,6 +3946,10 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" @@ -3100,6 +3998,10 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + is@^3.1.0, is@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" @@ -3247,10 +4149,18 @@ json-edm-parser@0.1.2: dependencies: jsonparse "~1.2.0" +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -3261,10 +4171,14 @@ 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" +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -3309,6 +4223,26 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.0.2" +kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + klaw@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" @@ -3387,6 +4321,18 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -3394,6 +4340,13 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -3469,6 +4422,14 @@ lodash._shimkeys@~2.4.1: dependencies: lodash._objecttypes "~2.4.1" +lodash.clone@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + lodash.defaults@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-2.4.1.tgz#a7e8885f05e68851144b6e12a8f3678026bc4c54" @@ -3552,6 +4513,10 @@ lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" +lodash.some@^4.2.2: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + lodash.template@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-2.4.1.tgz#9e611007edf629129a974ab3c48b817b3e1cf20d" @@ -3626,6 +4591,14 @@ lolex@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" +long@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + +long@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -3659,6 +4632,12 @@ macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + dependencies: + pify "^3.0.0" + make-error-cause@^1.1.1: version "1.2.2" resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" @@ -3669,7 +4648,11 @@ make-error@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96" -map-cache@^0.2.0: +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + +map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -3685,6 +4668,12 @@ map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + markdown-it@^8.3.1: version "8.4.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.0.tgz#e2400881bf171f7018ed1bd9da441dac8af6306d" @@ -3699,6 +4688,13 @@ math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + md5@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" @@ -3721,6 +4717,13 @@ mem@^1.1.0: dependencies: mimic-fn "^1.0.0" +memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + meow@^3.1.0, meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -3740,6 +4743,12 @@ merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" +merge-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-1.0.1.tgz#2a64b24457becd4e4dc608283247e94ce589aa32" + dependencies: + is-plain-obj "^1.1" + merge-stream@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" @@ -3768,6 +4777,31 @@ micromatch@^2.1.5, micromatch@^2.3.7: parse-glob "^3.0.4" regex-cache "^0.4.2" +micromatch@^3.1.4, micromatch@^3.1.8: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" @@ -3778,14 +4812,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" @@ -3794,6 +4828,14 @@ mimic-response@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + minimatch@0.3: version "0.3.0" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" @@ -3836,6 +4878,41 @@ minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mkdirp@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" @@ -3889,6 +4966,17 @@ mocha@^2.0.1, mocha@^2.2.5: supports-color "1.2.0" to-iso-string "0.0.2" +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -3916,11 +5004,11 @@ mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" -mute-stream@~0.0.4: +mute-stream@0.0.7, mute-stream@~0.0.4: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" -nan@2.10.0: +nan@2.10.0, nan@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -3944,6 +5032,22 @@ nan@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + native-is-elevated@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.2.1.tgz#70a2123a8575b9f624a3ef465d98cb74ae017385" @@ -3964,16 +5068,75 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" +needle@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +neo-async@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" + +nice-try@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + node-abi@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.4.1.tgz#7628c4d4ec4e9cd3764ceb3652f36b2e7f8d4923" dependencies: semver "^5.4.1" +node-libs-browser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + node-pty@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.7.6.tgz#bff6148c9c5836ca7e73c7aaaec067dcbdac2f7b" @@ -4006,6 +5169,13 @@ nopt@3.x, nopt@^3.0.1, nopt@~3.0.1: dependencies: abbrev "1" +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" @@ -4025,7 +5195,7 @@ normalize-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" -normalize-path@^2.0.0: +normalize-path@^2.0.0, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" dependencies: @@ -4048,13 +5218,24 @@ normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" +npm-bundled@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" + +npm-packlist@^1.1.6: + version "1.1.11" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" dependencies: path-key "^2.0.0" -npmlog@^4.0.1: +npmlog@^4.0.1, npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: @@ -4093,10 +5274,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" @@ -4113,10 +5290,24 @@ object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + object-keys@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + object.defaults@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" @@ -4133,7 +5324,7 @@ object.omit@^2.0.0: for-own "^0.1.3" is-extendable "^0.1.1" -object.pick@^1.2.0: +object.pick@^1.2.0, object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" dependencies: @@ -4161,6 +5352,12 @@ onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + oniguruma@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.0.0.tgz#cf258a8b1a2ec1d0d68964d6336df264008ebf4c" @@ -4221,6 +5418,10 @@ ordered-read-streams@^0.3.0: is-stream "^1.0.1" readable-stream "^2.0.1" +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -4233,7 +5434,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -4244,6 +5445,13 @@ osenv@^0.1.3: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + p-all@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-all/-/p-all-1.0.0.tgz#93bdf53a55a23821fdfa98b4174a99bf7f31df8d" @@ -4254,18 +5462,36 @@ p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" +p-limit@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + dependencies: + p-try "^1.0.0" + p-limit@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" dependencies: p-try "^1.0.0" +p-limit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + dependencies: + p-limit "^2.0.0" + p-map@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" @@ -4274,6 +5500,32 @@ p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" +p-try@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" + +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + +parse-asn1@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + parse-filepath@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.1.tgz#159d6155d43904d16c10ef698911da1e91969b73" @@ -4321,6 +5573,14 @@ parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -4343,7 +5603,7 @@ path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" -path-key@^2.0.0: +path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -4373,12 +5633,28 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" dependencies: through "~2.3" +pbkdf2@^3.0.3: + version "3.0.16" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -4395,6 +5671,10 @@ pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -4405,6 +5685,12 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + plist@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/plist/-/plist-1.2.0.tgz#084b5093ddc92506e259f874b8d9b1afb8c79593" @@ -4424,6 +5710,15 @@ plugin-error@^0.1.2: arr-union "^2.0.1" extend-shallow "^1.1.2" +plugin-error@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" + dependencies: + ansi-colors "^1.0.1" + arr-diff "^4.0.0" + arr-union "^3.1.0" + extend-shallow "^3.0.2" + plur@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" @@ -4434,6 +5729,10 @@ pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + postcss-calc@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" @@ -4707,6 +6006,10 @@ process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + progress-stream@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77" @@ -4718,6 +6021,10 @@ progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + promisify-node@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/promisify-node/-/promisify-node-0.3.0.tgz#b4b55acf90faa7d2b8b90ca396899086c03060cf" @@ -4735,10 +6042,24 @@ proxy-addr@~2.0.2: forwarded "~0.1.2" ipaddr.js "1.5.2" +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +public-encrypt@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + pump@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" @@ -4753,17 +6074,33 @@ pump@^1.0.1: end-of-stream "^1.1.0" once "^1.3.1" -pump@^2.0.1: +pump@^2.0.0, pump@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" dependencies: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^1.4.1: +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + q@^1.0.1, q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -4772,10 +6109,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" @@ -4795,6 +6128,14 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + queue@3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/queue/-/queue-3.0.6.tgz#66c0ffd0a1d9d28045adebda966a2d3946ab9f13" @@ -4814,6 +6155,19 @@ randomatic@^1.1.3: is-number "^2.0.2" kind-of "^3.0.2" +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -4836,7 +6190,7 @@ rc@^1.1.2: minimist "^1.2.0" strip-json-comments "~2.0.1" -rc@^1.1.6: +rc@^1.1.6, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" dependencies: @@ -4870,6 +6224,18 @@ read@^1.0.7: dependencies: mute-stream "~0.0.4" +"readable-stream@1 || 2", readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + "readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" @@ -4900,18 +6266,6 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.0.3" util-deprecate "~1.0.1" -readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - readable-stream@~2.0.0, readable-stream@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" @@ -4974,6 +6328,13 @@ regex-cache@^0.4.2: is-equal-shallow "^0.1.3" is-primitive "^2.0.0" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + remap-istanbul@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/remap-istanbul/-/remap-istanbul-0.6.4.tgz#ac551eff1aa641504b4f318d0303dda61e3bb695" @@ -4996,6 +6357,10 @@ repeat-string@^1.5.2: version "1.5.4" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.5.4.tgz#64ec0c91e0f4b475f90d5b643651e3e6e5b6c2d5" +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + repeating@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" @@ -5103,23 +6468,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" @@ -5161,6 +6509,12 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + dependencies: + resolve-from "^3.0.0" + resolve-dir@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" @@ -5172,7 +6526,11 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve-url@~0.2.1: +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-url@^0.2.1, resolve-url@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -5193,6 +6551,17 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -5205,7 +6574,7 @@ rimraf@^2.2.8: dependencies: glob "^7.0.5" -rimraf@^2.4.2: +rimraf@^2.4.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: @@ -5215,20 +6584,55 @@ rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" dependencies: once "^1.3.0" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + dependencies: + aproba "^1.1.1" + rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" +rxjs@^6.1.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.2.tgz#eb75fa3c186ff5289907d06483a77884586e1cf9" + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-buffer@^5.1.0, safe-buffer@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -5245,10 +6649,17 @@ sax@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.2.tgz#735ffaa39a1cff8ffb9598f0223abdb03a9fb2ea" -sax@>=0.6.0, sax@~1.2.1: +sax@>=0.6.0, sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" +schema-utils@^0.4.4, schema-utils@^0.4.5: + version "0.4.7" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + semaphore@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.0.5.tgz#b492576e66af193db95d65e25ec53f5f19798d60" @@ -5261,7 +6672,7 @@ semver@^4.1.0, semver@^4.3.4: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" -semver@^5.4.1, semver@^5.5.0: +semver@^5.0.1, semver@^5.4.1, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -5287,6 +6698,10 @@ sequencify@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" +serialize-javascript@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" + serve-static@1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" @@ -5304,6 +6719,28 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" @@ -5312,6 +6749,13 @@ setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -5334,7 +6778,7 @@ sigmund@^1.0.1, sigmund@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" -signal-exit@^3.0.0: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -5365,15 +6809,40 @@ sinon@^1.17.2: samsam "1.1.2" util ">=0.10.3 <1" +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" -sntp@0.2.x: - version "0.2.4" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-0.2.4.tgz#fb885f18b0f3aad189f824862536bceeec750900" +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" dependencies: - hoek "0.9.x" + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" sntp@1.x.x: version "1.0.9" @@ -5393,6 +6862,10 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + source-map-resolve@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.3.1.tgz#610f6122a445b8dd51535a2a71b783dfc1248761" @@ -5402,6 +6875,20 @@ source-map-resolve@^0.3.0: source-map-url "~0.3.0" urix "~0.1.0" +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + source-map-url@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" @@ -5412,7 +6899,7 @@ source-map@0.4.x, source-map@^0.4.4: dependencies: amdefine ">=0.0.4" -source-map@0.X, source-map@>=0.5.6, source-map@^0.6.1, source-map@~0.6.1: +source-map@0.X, source-map@>=0.5.6, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -5462,6 +6949,12 @@ speedometer@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + split@0.3: version "0.3.3" resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" @@ -5486,6 +6979,19 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" + dependencies: + safe-buffer "^5.1.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + "statuses@>= 1.3.1 < 2": version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" @@ -5494,6 +7000,13 @@ statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + stream-combiner@~0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" @@ -5504,6 +7017,23 @@ stream-consume@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" @@ -5530,13 +7060,19 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string_decoder@^1.0.0, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" @@ -5547,12 +7083,6 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - dependencies: - safe-buffer "~5.1.0" - stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -5647,6 +7177,12 @@ supports-color@^4.0.0: dependencies: has-flag "^2.0.0" +supports-color@^5.3.0, supports-color@^5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -5670,6 +7206,10 @@ table@^3.7.8: slice-ansi "0.0.4" string-width "^2.0.0" +tapable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" + tar-fs@^1.13.0: version "1.16.2" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.2.tgz#17e5239747e399f7e77344f5f53365f04af53577" @@ -5691,6 +7231,18 @@ tar-stream@^1.1.2: to-buffer "^1.1.0" xtend "^4.0.0" +tar@^4: + version "4.4.6" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + temp@^0.8.1, temp@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" @@ -5759,7 +7311,7 @@ through2@~0.2.3: readable-stream "~1.1.9" xtend "~2.1.1" -through@2, through@^2.3.4, through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.8: +through@2, through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -5773,6 +7325,12 @@ time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" +timers-browserify@^2.0.4: + version "2.0.10" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" + dependencies: + setimmediate "^1.0.4" + tmp@0.0.28: version "0.0.28" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" @@ -5785,12 +7343,22 @@ tmp@0.0.29: dependencies: os-tmpdir "~1.0.1" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + to-absolute-glob@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" dependencies: extend-shallow "^2.0.1" +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + to-buffer@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" @@ -5799,6 +7367,28 @@ to-iso-string@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + touch@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/touch/-/touch-0.0.3.tgz#51aef3d449571d4f287a5d87c9c8b49181a0db1d" @@ -5823,6 +7413,16 @@ tryit@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" +ts-loader@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-4.4.2.tgz#778d4464b24436873c78f7f9e914d88194c2a248" + dependencies: + chalk "^2.3.0" + enhanced-resolve "^4.0.0" + loader-utils "^1.0.2" + micromatch "^3.1.4" + semver "^5.0.1" + tslib@^1.7.1: version "1.8.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.0.tgz#dc604ebad64bcbf696d613da6c954aa0e7ea1eb6" @@ -5831,6 +7431,10 @@ tslib@^1.8.0: version "1.9.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" +tslib@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + tslint@^5.9.1: version "5.9.1" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae" @@ -5854,16 +7458,16 @@ tsutils@^2.12.1: dependencies: tslib "^1.7.1" +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" dependencies: safe-buffer "^5.0.1" -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" @@ -5932,6 +7536,13 @@ uglify-es@^3.0.18: commander "~2.11.0" source-map "~0.6.1" +uglify-es@^3.3.4: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + dependencies: + commander "~2.13.0" + source-map "~0.6.1" + uglify-js@^2.6: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" @@ -5952,6 +7563,19 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" +uglifyjs-webpack-plugin@^1.2.4: + version "1.2.7" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz#57638dd99c853a1ebfe9d97b42160a8a507f9d00" + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + schema-utils "^0.4.5" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + uglify-es "^3.3.4" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + ultron@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" @@ -5968,6 +7592,15 @@ underscore@~1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -5982,6 +7615,18 @@ uniqs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" +unique-filename@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" + dependencies: + imurmurhash "^0.1.4" + unique-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" @@ -5997,6 +7642,23 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" + +uri-js@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -6005,6 +7667,17 @@ url-join@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" @@ -6019,12 +7692,18 @@ util-deprecate@1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -"util@>=0.10.3 <1": +util@0.10.3, "util@>=0.10.3 <1": version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: inherits "2.0.1" +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + dependencies: + inherits "2.0.3" + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -6033,6 +7712,10 @@ uuid@^3.0.0, uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +v8-compile-cache@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" + v8-inspect-profiler@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/v8-inspect-profiler/-/v8-inspect-profiler-0.0.8.tgz#4d6bedb7c3d1bfc69e5bfdc2ded3d6784a5a76a6" @@ -6056,10 +7739,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" @@ -6173,6 +7852,12 @@ vinyl@~2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + vsce@1.33.2: version "1.33.2" resolved "https://registry.yarnpkg.com/vsce/-/vsce-1.33.2.tgz#3645f69aaf984e22f74ea49d35f38dd18d66ff5f" @@ -6210,9 +7895,9 @@ vscode-chokidar@1.6.2: optionalDependencies: vscode-fsevents "0.3.8" -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.8: version "0.3.8" @@ -6220,9 +7905,9 @@ vscode-fsevents@0.3.8: dependencies: nan "^2.3.0" -vscode-nls-dev@3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/vscode-nls-dev/-/vscode-nls-dev-3.0.7.tgz#8cfbb371cb3c8f47f247073d9f84a6af357bbfe0" +vscode-nls-dev@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/vscode-nls-dev/-/vscode-nls-dev-3.2.2.tgz#5855c9b3e566dd00fd6108f9c2e1bd02c925c153" dependencies: clone "^2.1.1" event-stream "^3.3.4" @@ -6246,9 +7931,9 @@ vscode-nsfw@1.0.17: nan "^2.0.0" promisify-node "^0.3.0" -vscode-ripgrep@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.0.1.tgz#eff2f2b2a49921ac0acd3ff8dfecaaeebf0184cf" +vscode-ripgrep@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.1.0.tgz#93c1e39d88342ee1b15530a12898ce930d511948" vscode-textmate@^4.0.1: version "4.0.1" @@ -6256,13 +7941,9 @@ vscode-textmate@^4.0.1: dependencies: oniguruma "^7.0.0" -vscode-uri@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.5.tgz#3b899a8ef71c37f3054d79bdbdda31c7bf36f20d" - -vscode-xterm@3.6.0-beta5: - version "3.6.0-beta5" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.6.0-beta5.tgz#b44fd70451944624f148bd9f0be4925b52b7a7e0" +vscode-xterm@3.7.0-beta5: + version "3.7.0-beta5" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.7.0-beta5.tgz#04c7564d1cc8d73b268f04046b3da050c6e59be7" vso-node-api@^6.1.2-preview: version "6.1.2-preview" @@ -6273,6 +7954,81 @@ vso-node-api@^6.1.2-preview: typed-rest-client "^0.9.0" underscore "^1.8.3" +watchpack@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +webpack-cli@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.1.0.tgz#d71a83687dcfeb758fdceeb0fe042f96bcf62994" + dependencies: + chalk "^2.4.1" + cross-spawn "^6.0.5" + enhanced-resolve "^4.0.0" + global-modules-path "^2.1.0" + import-local "^1.0.0" + inquirer "^6.0.0" + interpret "^1.1.0" + loader-utils "^1.1.0" + supports-color "^5.4.0" + v8-compile-cache "^2.0.0" + yargs "^12.0.1" + +webpack-sources@^1.0.1, webpack-sources@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-stream@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/webpack-stream/-/webpack-stream-5.1.1.tgz#15b1d91da6887a37f6832128383ae0282bd7d0e7" + dependencies: + fancy-log "^1.3.2" + lodash.clone "^4.3.2" + lodash.some "^4.2.2" + memory-fs "^0.4.1" + plugin-error "^1.0.1" + supports-color "^5.3.0" + through "^2.3.8" + vinyl "^2.1.0" + webpack "^4.7.0" + +webpack@^4.16.5, webpack@^4.7.0: + version "4.16.5" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.5.tgz#29fb39462823d7eb8aefcab8b45f7f241db0d092" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-module-context" "1.5.13" + "@webassemblyjs/wasm-edit" "1.5.13" + "@webassemblyjs/wasm-opt" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + acorn "^5.6.2" + acorn-dynamic-import "^3.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.0" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^0.4.4" + tapable "^1.0.0" + uglifyjs-webpack-plugin "^1.2.4" + watchpack "^1.5.0" + webpack-sources "^1.0.1" + whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" @@ -6318,6 +8074,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" @@ -6330,6 +8090,12 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" +worker-farm@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" + dependencies: + errno "~0.1.7" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -6398,6 +8164,10 @@ xmldom@0.1.x: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" +xregexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" + "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -6416,10 +8186,24 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + +yargs-parser@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + dependencies: + camelcase "^4.1.0" + yargs-parser@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" @@ -6443,6 +8227,23 @@ yargs@^10.1.1: y18n "^3.2.1" yargs-parser "^8.1.0" +yargs@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.1.tgz#6432e56123bb4e7c3562115401e98374060261c2" + dependencies: + cliui "^4.0.0" + decamelize "^2.0.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^10.1.0" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" @@ -6465,7 +8266,7 @@ yauzl@^2.2.1, yauzl@^2.3.1, yauzl@^2.9.1: buffer-crc32 "~0.2.3" fd-slicer "~1.0.1" -yazl@^2.2.1, yazl@^2.2.2: +yazl@^2.2.1, yazl@^2.2.2, yazl@^2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071" dependencies: