diff --git a/.editorconfig b/.editorconfig index b4180a87dd7..e7e99b5bcb5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# EditorConfig is awesome: http://EditorConfig.org +# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true @@ -6,7 +6,6 @@ root = true # Tab indentation [*] indent_style = tab -indent_size = 4 trim_trailing_whitespace = true # The indent size used in the `package.json` file cannot be changed diff --git a/.gitattributes b/.gitattributes index 0a56ba75f93..5a817c30b6d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,4 +6,5 @@ ThirdPartyNotices.txt eol=crlf *.bat eol=crlf *.cmd eol=crlf *.ps1 eol=lf -*.sh eol=lf \ No newline at end of file +*.sh eol=lf +*.rtf -text \ No newline at end of file diff --git a/.github/calendar.yml b/.github/calendar.yml index 307c3db379a..f149579600b 100644 --- a/.github/calendar.yml +++ b/.github/calendar.yml @@ -31,5 +31,7 @@ '2018-09-24 18:00, US/Pacific': 'endgame', '2018-10-08 09:00, US/Pacific': 'development', # 1.28.0 released '2018-10-29 18:00, US/Pacific': 'endgame', - # '2018-11-07 09:00, US/Pacific': 'development', # 1.29.0 released + '2018-11-12 11:00, US/Pacific': 'development', # 1.29.0 released + '2018-12-03 18:00, US/Pacific': 'endgame', + '2018-12-12 13:00, US/Pacific': 'development', # 1.30.0 released } diff --git a/.github/classifier.yml b/.github/classifier.yml index 07b85720764..3236507a472 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -12,7 +12,7 @@ cli: [], color-palette: [], config: [], - css-less-scss: [ aeschli ], + css-less-scss: [], debug-console: [], debug: { assignees: [ weinand ], @@ -52,7 +52,7 @@ editor-symbols: [], editor-textbuffer: [], editor-wrapping: [], - emmet: [ ramya-rao-a ], + emmet: [ octref, ramya-rao-a ], error-list: [], explorer-custom: [], extension-host: [], @@ -76,12 +76,12 @@ assignLabel: false }, formatting: [], - git: [ joaomoreno ], + git: [], grammar: [], hot-exit: [], - html: [ aeschli ], + html: [], install-update: [], - integrated-terminal: [], + integrated-terminal: [ Tyriar ], integration-test: [], intellisense-config: [], issue-reporter: [ RMacfarlane ], @@ -131,7 +131,7 @@ assignLabel: false }, suggest: [], - tasks: [ dbaeumer ], + tasks: [ alexr00 ], telemetry: [], themes: [], tokenization: [], diff --git a/.github/commands.yml b/.github/commands.yml index 95a3df06c5b..f20079caab8 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -1,9 +1,17 @@ { perform: true, commands: [ + { + type: 'comment', + name: 'question', + allowUsers: ['cleidigh', 'usernamehw'], + action: 'updateLabels', + addLabel: '*question' + }, { type: 'label', name: '*question', + allowTriggerByBot: true, action: 'close', comment: "Please ask your question on [StackOverflow](https://aka.ms/vscodestackoverflow). We have a great community over [there](https://aka.ms/vscodestackoverflow). They have already answered thousands of questions and are happy to answer yours as well. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" }, @@ -43,6 +51,12 @@ action: 'close', comment: "The described behavior is how it is expected to work. If you disagree, please explain what is expected and what is not in more detail. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" }, + { + type: 'label', + name: '*english-please', + action: 'close', + comment: "This issue is being closed because its description is not in English, that makes it hard for us to work on it. Please open a new issue with an English description. You might find [Bing Translator](https://www.bing.com/translator) useful." + }, { type: 'comment', name: 'duplicate', @@ -62,7 +76,16 @@ name: 'confirm', allowUsers: ['cleidigh', 'usernamehw'], action: 'updateLabels', - addLabel: 'confirmed' + addLabel: 'confirmed', + removeLabel: 'confirmation-pending' + }, + { + type: 'comment', + name: 'confirmationPending', + allowUsers: ['cleidigh', 'usernamehw'], + action: 'updateLabels', + addLabel: 'confirmation-pending', + removeLabel: 'confirmed' }, { type: 'comment', diff --git a/.nvmrc b/.nvmrc index 32c861f970d..f599e28b8ab 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8.9.2 +10 diff --git a/.vscode/cgmanifest.schema.json b/.vscode/cgmanifest.schema.json index f5424e3b226..2e719b02396 100644 --- a/.vscode/cgmanifest.schema.json +++ b/.vscode/cgmanifest.schema.json @@ -120,7 +120,7 @@ "type": "string", "description": "The name of the license" }, - "isOnlyDevelopmentDependency": { + "developmentDependency": { "type": "boolean", "description": "This component is inlined in the vscode repo and **is not shipped**." }, diff --git a/.vscode/launch.json b/.vscode/launch.json index 89dc16fb181..bd9e290ab21 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,13 +22,11 @@ ] }, { - "type": "node", + "type": "chrome", "request": "attach", "name": "Attach to Shared Process", - "port": 5871, - "outFiles": [ - "${workspaceFolder}/out/**/*.js" - ] + "port": 9222, + "urlFilter": "*" }, { "type": "node", @@ -125,7 +123,8 @@ "request": "launch", "name": "Launch VS Code", "windows": { - "runtimeExecutable": "${workspaceFolder}/scripts/code.bat" + "runtimeExecutable": "${workspaceFolder}/scripts/code.bat", + "timeout": 20000 }, "osx": { "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" @@ -133,26 +132,28 @@ "linux": { "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" }, + "env": { + "VSCODE_EXTHOST_WILL_SEND_SOCKET": null + }, + "breakOnLoad": false, "urlFilter": "*workbench.html*", "runtimeArgs": [ - "--inspect=5875", "--no-cached-data" - ], - "skipFiles": [ - "**/winjs*.js" + "--inspect=5875", + "--no-cached-data" ], "webRoot": "${workspaceFolder}" }, { - "type": "node", - "request": "launch", - "name": "Launch VS Code (Main Process)", - "runtimeExecutable": "${workspaceFolder}/scripts/code.sh", - "runtimeArgs": [ - "--no-cached-data" - ], - "outFiles": [ - "${workspaceFolder}/out/**/*.js" - ] + "type": "node", + "request": "launch", + "name": "Launch VS Code (Main Process)", + "runtimeExecutable": "${workspaceFolder}/scripts/code.sh", + "runtimeArgs": [ + "--no-cached-data" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] }, { "type": "node", @@ -196,8 +197,8 @@ { "type": "node", "request": "launch", - "name": "Unit Tests", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "name": "Run Unit Tests", + "program": "${workspaceFolder}/test/electron/index.js", "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron", "windows": { "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.exe" @@ -207,14 +208,9 @@ }, "outputCapture": "std", "args": [ - "--delay", - "--timeout", - "2000" + "--remote-debugging-port=9222" ], "cwd": "${workspaceFolder}", - "env": { - "ELECTRON_RUN_AS_NODE": "true" - }, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -271,6 +267,13 @@ "Launch VS Code", "Attach to Extension Host" ] - } + }, + { + "name": "Debug Unit Tests", + "configurations": [ + "Attach to VS Code", + "Run Unit Tests" + ] + }, ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 8b9ca6cd07b..af8b3803709 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -42,11 +42,19 @@ "emmet.excludeLanguages": [], "typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.quoteStyle": "single", - "json.schemas": [{ - "fileMatch": [ "cgmanifest.json" ], - "url": "./.vscode/cgmanifest.schema.json" - }, { - "fileMatch": [ "cglicenses.json" ], - "url": "./.vscode/cglicenses.schema.json" - }] -} + "json.schemas": [ + { + "fileMatch": [ + "cgmanifest.json" + ], + "url": "./.vscode/cgmanifest.schema.json" + }, + { + "fileMatch": [ + "cglicenses.json" + ], + "url": "./.vscode/cglicenses.schema.json" + } + ], + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/.vscode/shared.code-snippets b/.vscode/shared.code-snippets index 6d6690a0282..fb3df23dd42 100644 --- a/.vscode/shared.code-snippets +++ b/.vscode/shared.code-snippets @@ -23,11 +23,13 @@ "description": "Insert Copyright Statement" }, "TS -> Inject Service": { + "scope": "typescript", "description": "Constructor Injection Pattern", "prefix": "@inject", "body": "@$1 private readonly _$2: ${1},$0" }, "TS -> Event & Emitter": { + "scope": "typescript", "prefix": "emitter", "description": "Add emitter and event properties", "body": [ diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 42366a1b720..89aba8e5fe0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -30,15 +30,15 @@ }, { "type": "npm", - "script": "strict-null-check-watch", - "label": "TS - Strict Null Checks", + "script": "strict-initialization-watch", + "label": "TS - Strict Initialization", "isBackground": true, "presentation": { "reveal": "never" }, "problemMatcher": { "base": "$tsc-watch", - "owner": "typescript-strict-null", + "owner": "typescript-strict-initialization", "applyTo": "allDocuments" } }, @@ -83,4 +83,4 @@ "problemMatcher": [] } ] -} +} \ No newline at end of file diff --git a/.yarnrc b/.yarnrc index 8e1ac6b6cac..beab9f70b95 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "2.0.12" +target "3.1.8" runtime "electron" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7775753e8bc..34703fae665 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,6 @@ If you find your issue already exists, make relevant comments and add your [reac * 👍 - upvote * 👎 - downvote - If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below. ### Writing Good Bug Reports and Feature Requests @@ -52,7 +51,9 @@ The built-in tool for reporting an issue, which you can access by using `Report Please include the following with each issue: -* Version of VS Code +* Version of VS Code + +* Your operating system * List of extensions that you have installed diff --git a/README.md b/README.md index a7294e71b9b..29b3270f1e5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Visual Studio Code - Open Source -[![Build Status](https://vscode.visualstudio.com/_apis/public/build/definitions/a4cdce18-a05c-4bb8-9476-5d07e63bfd76/1/badge?branchName=master)](https://aka.ms/vscode-builds) + +[![Build Status](https://dev.azure.com/vscode/VSCode/_apis/build/status/VS%20Code?branchName=master)](https://dev.azure.com/vscode/VSCode/_build/latest?definitionId=12) [![Feature Requests](https://img.shields.io/github/issues/Microsoft/vscode/feature-request.svg)](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) [![Bugs](https://img.shields.io/github/issues/Microsoft/vscode/bug.svg)](https://github.com/Microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) [![Gitter](https://img.shields.io/badge/chat-on%20gitter-yellow.svg)](https://gitter.im/Microsoft/vscode) @@ -14,7 +15,7 @@ VS Code is updated monthly with new features and bug fixes. You can download it VS Code in action

-The [`vscode`](https://github.com/microsoft/vscode) repository is where VS Code is developed and there are many ways you can participate in the project, for example: +The [`vscode`](https://github.com/microsoft/vscode) repository is where VS Code is developed and there are many ways in which you can participate in the project, for example: * [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues) and help us verify as they are checked in. * Review [source code changes](https://github.com/microsoft/vscode/pulls). diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 757d3800253..69d9050d4a4 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -5,106 +5,70 @@ Do Not Translate or Localize This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. -1. atom/language-c (https://github.com/atom/language-c) -2. atom/language-clojure (https://github.com/atom/language-clojure) -3. atom/language-coffee-script (https://github.com/atom/language-coffee-script) -4. atom/language-css (https://github.com/atom/language-css) -5. atom/language-java (https://github.com/atom/language-java) -6. atom/language-objective-c (https://github.com/atom/language-objective-c) -7. atom/language-sass version 0.52.0 (https://github.com/atom/language-sass) -8. atom/language-shellscript (https://github.com/atom/language-shellscript) -9. atom/language-xml (https://github.com/atom/language-xml) -10. chriskempson/tomorrow-theme (https://github.com/chriskempson/tomorrow-theme) -11. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) -12. daaain/Handlebars (https://github.com/daaain/Handlebars) -13. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) -14. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) -15. demyte/language-cshtml (https://github.com/demyte/language-cshtml) -16. Document Object Model () -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. marked version 0.5.0 (https://github.com/markedjs/marked) -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) +1. atom/language-clojure version 0.22.6 (https://github.com/atom/language-clojure) +2. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script) +3. atom/language-java version 0.31.1 (https://github.com/atom/language-java) +4. atom/language-objective-c version 0.15.0 (https://github.com/atom/language-objective-c) +5. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass) +6. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript) +7. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml) +8. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) +9. daaain/Handlebars version 1.7.1 (https://github.com/daaain/Handlebars) +10. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) +11. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) +12. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml) +13. Document Object Model version 4.0.0 (https://www.w3.org/DOM/) +14. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) +15. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) +16. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) +17. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) +18. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) +19. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R) +20. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) +21. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) +22. jeff-hykin/cpp-textmate-grammar version 1.8.8 (https://github.com/jeff-hykin/cpp-textmate-grammar) +23. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) +24. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) +25. language-docker (https://github.com/moby/moby) +26. language-go version 0.44.3 (https://github.com/atom/language-go) +27. language-less version 0.34.2 (https://github.com/atom/language-less) +28. language-php version 0.44.1 (https://github.com/atom/language-php) +29. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) +30. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) +31. marked version 0.6.2 (https://github.com/markedjs/marked) +32. mdn-data version 1.1.12 (https://github.com/mdn/data) +33. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage) +34. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage) +35. Microsoft/vscode-mssql version 1.4.0 (https://github.com/Microsoft/vscode-mssql) +36. mmims/language-batchfile version 0.7.5 (https://github.com/mmims/language-batchfile) +37. octicons version 8.3.0 (https://github.com/primer/octicons) +38. octref/language-css version 0.42.11 (https://github.com/octref/language-css) +39. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax) +40. promise-polyfill version 8.0.0 (https://github.com/taylorhakes/promise-polyfill) +41. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) +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. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage) +58. Unicode version 12.0.0 (http://www.unicode.org/) +59. vscode-logfile-highlighter version 2.4.1 (https://github.com/emilast/vscode-logfile-highlighter) +60. vscode-octicons-font version 1.1.0 (https://github.com/Microsoft/vscode-octicons-font) +61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +62. Web Background Synchronization (https://github.com/WICG/BackgroundSync) -%% atom/language-c NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2014 GitHub Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -This package was derived from a TextMate bundle located at -https://github.com/textmate/c.tmbundle and distributed under the following -license, located in `README.mdown`: - -Permission to copy, use, modify, sell and distribute this -software is granted. This software is provided "as is" without -express or implied warranty, and with no claim as to its -suitability for any purpose. -========================================= -END OF atom/language-c NOTICES AND INFORMATION - %% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2014 GitHub Inc. @@ -212,43 +176,6 @@ OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF atom/language-coffee-script NOTICES AND INFORMATION -%% atom/language-css NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2014 GitHub Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -This package was derived from a TextMate bundle located at -https://github.com/textmate/css.tmbundle and distributed under the following -license, located in `README.mdown`: - -Permission to copy, use, modify, sell and distribute this -software is granted. This software is provided "as is" without -express or implied warranty, and with no claim as to its -suitability for any purpose. -========================================= -END OF atom/language-css NOTICES AND INFORMATION - %% atom/language-java NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) @@ -450,22 +377,6 @@ suitability for any purpose. ========================================= END OF atom/language-xml NOTICES AND INFORMATION -%% chriskempson/tomorrow-theme NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (C) 2013 Chris Kempson - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -========================================= -END OF chriskempson/tomorrow-theme NOTICES AND INFORMATION - %% Colorsublime-Themes NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2015 Colorsublime.com @@ -825,6 +736,32 @@ THE SOFTWARE. ========================================= END OF ionide/ionide-fsgrammar NOTICES AND INFORMATION +%% jeff-hykin/cpp-textmate-grammar NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + +Copyright (c) 2019 Jeff Hykin + +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. +========================================= +END OF jeff-hykin/cpp-textmate-grammar NOTICES AND INFORMATION + %% js-beautify NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) @@ -1800,11 +1737,11 @@ THE SOFTWARE. ========================================= END OF mmims/language-batchfile NOTICES AND INFORMATION -%% octicons-code NOTICES AND INFORMATION BEGIN HERE +%% octicons NOTICES AND INFORMATION BEGIN HERE ========================================= -The MIT License (MIT) +MIT License -(c) 2012-2015 GitHub +Copyright (c) 2019 GitHub Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1813,109 +1750,55 @@ 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 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. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. ========================================= -END OF octicons-code NOTICES AND INFORMATION +END OF octicons NOTICES AND INFORMATION -%% octicons-font NOTICES AND INFORMATION BEGIN HERE +%% octref/language-css NOTICES AND INFORMATION BEGIN HERE ========================================= -(c) 2012-2015 GitHub +The MIT License (MIT) -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +Copyright (c) 2014 GitHub Inc. -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: +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: -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. +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. -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. +This package was derived from a TextMate bundle located at +https://github.com/textmate/css.tmbundle and distributed under the following +license, located in `README.mdown`: -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Permission to copy, use, modify, sell and distribute this +software is granted. This software is provided "as is" without +express or implied warranty, and with no claim as to its +suitability for any purpose. ========================================= -END OF octicons-font NOTICES AND INFORMATION +END OF octref/language-css NOTICES AND INFORMATION %% PowerShell/EditorSyntax NOTICES AND INFORMATION BEGIN HERE ========================================= @@ -1945,6 +1828,33 @@ SOFTWARE. ========================================= END OF PowerShell/EditorSyntax NOTICES AND INFORMATION +%% promise-polyfill NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2014 Taylor Hakes +Copyright (c) 2014 Forbes Lindesay + +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. +========================================= +END OF promise-polyfill NOTICES AND INFORMATION + %% seti-ui NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2014 Jesse Weed @@ -2357,6 +2267,32 @@ SOFTWARE. ========================================= END OF vscode-logfile-highlighter NOTICES AND INFORMATION +%% vscode-octicons-font NOTICES AND INFORMATION BEGIN HERE +========================================= +MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + 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 +========================================= +END OF vscode-octicons-font NOTICES AND INFORMATION + %% vscode-swift NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) diff --git a/build/.nativeignore b/build/.nativeignore new file mode 100644 index 00000000000..cc88ae0d878 --- /dev/null +++ b/build/.nativeignore @@ -0,0 +1,111 @@ +# cleanup rules for native node modules, .gitignore style + +fsevents/binding.gyp +fsevents/fsevents.cc +fsevents/build/** +fsevents/src/** +fsevents/test/** +!fsevents/**/*.node + +vscode-sqlite3/binding.gyp +vscode-sqlite3/benchmark/** +vscode-sqlite3/cloudformation/** +vscode-sqlite3/deps/** +vscode-sqlite3/test/** +vscode-sqlite3/build/** +vscode-sqlite3/src/** +!vscode-sqlite3/build/Release/*.node + +oniguruma/binding.gyp +oniguruma/build/** +oniguruma/src/** +oniguruma/deps/** +!oniguruma/build/Release/*.node +!oniguruma/src/*.js + +windows-mutex/binding.gyp +windows-mutex/build/** +windows-mutex/src/** +!windows-mutex/**/*.node + +native-keymap/binding.gyp +native-keymap/build/** +native-keymap/src/** +native-keymap/deps/** +!native-keymap/build/Release/*.node + +native-is-elevated/binding.gyp +native-is-elevated/build/** +native-is-elevated/src/** +native-is-elevated/deps/** +!native-is-elevated/build/Release/*.node + +native-watchdog/binding.gyp +native-watchdog/build/** +native-watchdog/src/** +!native-watchdog/build/Release/*.node + +spdlog/binding.gyp +spdlog/build/** +spdlog/deps/** +spdlog/src/** +spdlog/test/** +!spdlog/build/Release/*.node + +jschardet/dist/** + +windows-foreground-love/binding.gyp +windows-foreground-love/build/** +windows-foreground-love/src/** +!windows-foreground-love/**/*.node + +windows-process-tree/binding.gyp +windows-process-tree/build/** +windows-process-tree/src/** +!windows-process-tree/**/*.node + +gc-signals/binding.gyp +gc-signals/build/** +gc-signals/src/** +gc-signals/deps/** + +!gc-signals/build/Release/*.node +!gc-signals/src/index.js + +keytar/binding.gyp +keytar/build/** +keytar/src/** +keytar/script/** +keytar/node_modules/** +!keytar/**/*.node + +node-pty/binding.gyp +node-pty/build/** +node-pty/src/** +node-pty/tools/** +!node-pty/build/Release/*.exe +!node-pty/build/Release/*.dll +!node-pty/build/Release/*.node + +vscode-nsfw/binding.gyp +vscode-nsfw/build/** +vscode-nsfw/src/** +vscode-nsfw/openpa/** +vscode-nsfw/includes/** +!vscode-nsfw/build/Release/*.node +!vscode-nsfw/**/*.a + +vsda/binding.gyp +vsda/README.md +vsda/build/** +vsda/*.bat +vsda/*.sh +vsda/*.cpp +vsda/*.h +!vsda/build/Release/vsda.node + +vscode-windows-ca-certs/**/* +!vscode-windows-ca-certs/package.json +!vscode-windows-ca-certs/**/*.node + +node-addon-api/**/* \ No newline at end of file diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index 2095c2d2532..724939da318 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -6,7 +6,6 @@ 'use strict'; import * as fs from 'fs'; -import { execSync } from 'child_process'; import { Readable } from 'stream'; import * as crypto from 'crypto'; import * as azure from 'azure-storage'; @@ -65,7 +64,7 @@ interface Asset { platform: string; type: string; url: string; - mooncakeUrl: string; + mooncakeUrl?: string; hash: string; sha256hash: string; size: number; @@ -152,9 +151,13 @@ async function publish(commit: string, quality: string, platform: string, type: const queuedBy = process.env['BUILD_QUEUEDBY']!; const sourceBranch = process.env['BUILD_SOURCEBRANCH']!; - const isReleased = quality === 'insider' - && /^master$|^refs\/heads\/master$/.test(sourceBranch) - && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy); + const isReleased = ( + // Insiders: nightly build from master + (quality === 'insider' && /^master$|^refs\/heads\/master$/.test(sourceBranch) && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy)) || + + // Exploration: any build from electron-4.0.x branch + (quality === 'exploration' && /^electron-4.0.x$|^refs\/heads\/electron-4.0.x$/.test(sourceBranch)) + ); console.log('Publishing...'); console.log('Quality:', quality); @@ -184,40 +187,18 @@ async function publish(commit: string, quality: string, platform: string, type: const blobService = azure.createBlobService(storageAccount, process.env['AZURE_STORAGE_ACCESS_KEY_2']!) .withFilter(new azure.ExponentialRetryPolicyFilter(20)); - const mooncakeBlobService = azure.createBlobService(storageAccount, process.env['MOONCAKE_STORAGE_ACCESS_KEY']!, `${storageAccount}.blob.core.chinacloudapi.cn`) - .withFilter(new azure.ExponentialRetryPolicyFilter(20)); + await assertContainer(blobService, quality); - // mooncake is fussy and far away, this is needed! - mooncakeBlobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000; + const blobExists = await doesAssetExist(blobService, quality, blobName); - await Promise.all([ - assertContainer(blobService, quality), - assertContainer(mooncakeBlobService, quality) - ]); - - const [blobExists, moooncakeBlobExists] = await Promise.all([ - doesAssetExist(blobService, quality, blobName), - doesAssetExist(mooncakeBlobService, quality, blobName) - ]); - - const promises: Array> = []; - - if (!blobExists) { - promises.push(uploadBlob(blobService, quality, blobName, file)); - } - - if (!moooncakeBlobExists) { - promises.push(uploadBlob(mooncakeBlobService, quality, blobName, file)); - } - - if (promises.length === 0) { + if (blobExists) { console.log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); return; } console.log('Uploading blobs to Azure storage...'); - await Promise.all(promises); + await uploadBlob(blobService, quality, blobName, file); console.log('Blobs successfully uploaded.'); @@ -229,7 +210,6 @@ async function publish(commit: string, quality: string, platform: string, type: platform: platform, type: type, url: `${process.env['AZURE_CDN_URL']}/${quality}/${blobName}`, - mooncakeUrl: `${process.env['MOONCAKE_CDN_URL']}/${quality}/${blobName}`, hash: sha1hash, sha256hash, size @@ -270,12 +250,18 @@ function main(): void { return; } + const commit = process.env['BUILD_SOURCEVERSION']; + + if (!commit) { + console.warn('Skipping publish due to missing BUILD_SOURCEVERSION'); + return; + } + const opts = minimist(process.argv.slice(2), { boolean: ['upload-only'] }); const [quality, platform, type, name, version, _isUpdate, file] = opts._; - const commit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); publish(commit, quality, platform, type, name, version, _isUpdate, file, opts).catch(err => { console.error(err); diff --git a/build/azure-pipelines/common/sync-mooncake.ts b/build/azure-pipelines/common/sync-mooncake.ts new file mode 100644 index 00000000000..af0db530683 --- /dev/null +++ b/build/azure-pipelines/common/sync-mooncake.ts @@ -0,0 +1,176 @@ +/*--------------------------------------------------------------------------------------------- + * 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 url from 'url'; +import * as azure from 'azure-storage'; +import * as mime from 'mime'; +import { DocumentClient, RetrievedDocument } from 'documentdb'; + +function log(...args: any[]) { + console.log(...[`[${new Date().toISOString()}]`, ...args]); +} + +function error(...args: any[]) { + console.error(...[`[${new Date().toISOString()}]`, ...args]); +} + +if (process.argv.length < 3) { + error('Usage: node sync-mooncake.js '); + process.exit(-1); +} + +interface Build extends RetrievedDocument { + assets: Asset[]; +} + +interface Asset { + platform: string; + type: string; + url: string; + mooncakeUrl: string; + hash: string; + sha256hash: string; + size: number; + supportsFastUpdate?: boolean; +} + +function updateBuild(commit: string, quality: string, platform: string, type: string, asset: Asset): Promise { + const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/' + quality; + const updateQuery = { + query: 'SELECT TOP 1 * FROM c WHERE c.id = @id', + parameters: [{ name: '@id', value: commit }] + }; + + let updateTries = 0; + + function _update(): Promise { + updateTries++; + + return new Promise((c, e) => { + client.queryDocuments(collection, updateQuery).toArray((err, results) => { + if (err) { return e(err); } + if (results.length !== 1) { return e(new Error('No documents')); } + + const release = results[0]; + + release.assets = [ + ...release.assets.filter((a: any) => !(a.platform === platform && a.type === type)), + asset + ]; + + client.replaceDocument(release._self, release, err => { + if (err && err.code === 409 && updateTries < 5) { return c(_update()); } + if (err) { return e(err); } + + log('Build successfully updated.'); + c(); + }); + }); + }); + } + + return _update(); +} + +async function sync(commit: string, quality: string): Promise { + log(`Synchronizing Mooncake assets for ${quality}, ${commit}...`); + + const cosmosdb = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = `dbs/builds/colls/${quality}`; + const query = { + query: 'SELECT TOP 1 * FROM c WHERE c.id = @id', + parameters: [{ name: '@id', value: commit }] + }; + + const build = await new Promise((c, e) => { + cosmosdb.queryDocuments(collection, query).toArray((err, results) => { + if (err) { return e(err); } + if (results.length !== 1) { return e(new Error('No documents')); } + c(results[0] as Build); + }); + }); + + log(`Found build for ${commit}, with ${build.assets.length} assets`); + + const storageAccount = process.env['AZURE_STORAGE_ACCOUNT_2']!; + + const blobService = azure.createBlobService(storageAccount, process.env['AZURE_STORAGE_ACCESS_KEY_2']!) + .withFilter(new azure.ExponentialRetryPolicyFilter(20)); + + const mooncakeBlobService = azure.createBlobService(storageAccount, process.env['MOONCAKE_STORAGE_ACCESS_KEY']!, `${storageAccount}.blob.core.chinacloudapi.cn`) + .withFilter(new azure.ExponentialRetryPolicyFilter(20)); + + // mooncake is fussy and far away, this is needed! + blobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000; + mooncakeBlobService.defaultClientRequestTimeoutInMs = 10 * 60 * 1000; + + for (const asset of build.assets) { + try { + const blobPath = url.parse(asset.url).path; + + if (!blobPath) { + throw new Error(`Failed to parse URL: ${asset.url}`); + } + + const blobName = blobPath.replace(/^\/\w+\//, ''); + + log(`Found ${blobName}`); + + if (asset.mooncakeUrl) { + log(` Already in Mooncake ✔️`); + continue; + } + + const readStream = blobService.createReadStream(quality, blobName, undefined!); + const blobOptions: azure.BlobService.CreateBlockBlobRequestOptions = { + contentSettings: { + contentType: mime.lookup(blobPath), + cacheControl: 'max-age=31536000, public' + } + }; + + const writeStream = mooncakeBlobService.createWriteStreamToBlockBlob(quality, blobName, blobOptions, undefined); + + log(` Uploading to Mooncake...`); + await new Promise((c, e) => readStream.pipe(writeStream).on('finish', c).on('error', e)); + + log(` Updating build in DB...`); + asset.mooncakeUrl = `${process.env['MOONCAKE_CDN_URL']}${blobPath}`; + await updateBuild(commit, quality, asset.platform, asset.type, asset); + + log(` Done ✔️`); + } catch (err) { + error(err); + } + } + + log(`All done ✔️`); +} + +function main(): void { + if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) { + error('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH'); + return; + } + + const commit = process.env['BUILD_SOURCEVERSION']; + + if (!commit) { + error('Skipping publish due to missing BUILD_SOURCEVERSION'); + return; + } + + const quality = process.argv[2]; + + sync(commit, quality).catch(err => { + error(err); + process.exit(1); + }); +} + +main(); diff --git a/build/azure-pipelines/darwin/build.sh b/build/azure-pipelines/darwin/build.sh new file mode 100755 index 00000000000..af558b8c145 --- /dev/null +++ b/build/azure-pipelines/darwin/build.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -e +yarn gulp vscode-darwin-min +yarn gulp upload-vscode-sourcemaps \ No newline at end of file diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 99e17e940a6..776c1172bea 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -1,13 +1,25 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "8.12.0" + versionSpec: "10.15.1" +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" - script: | yarn displayName: Install Dependencies + condition: ne(variables['CacheRestored'], 'true') +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - script: | yarn gulp electron-x64 displayName: Download Electron @@ -17,9 +29,6 @@ steps: - script: | yarn monaco-compile-check displayName: Run Monaco Editor Checks -- script: | - yarn strict-null-check - displayName: Run Strict Null Checks - script: | yarn compile displayName: Compile Sources diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index c510636efc9..5d6ec8c2cff 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -1,29 +1,48 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "8.12.0" + versionSpec: "10.15.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e - echo "machine monacotools.visualstudio.com password $(VSO_PAT)" > ~/.netrc + + cat << EOF > ~/.netrc + machine monacotools.visualstudio.com + password $(devops-pat) + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + yarn - yarn gulp -- hygiene + yarn gulp mixin + yarn gulp hygiene yarn monaco-compile-check - yarn strict-null-check - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" yarn gulp -- mixin node build/azure-pipelines/common/installDistro.js node build/lib/builtInExtensions.js displayName: Prepare build - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - yarn gulp -- vscode-darwin-min upload-vscode-sourcemaps + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + ./build/azure-pipelines/darwin/build.sh displayName: Build - script: | @@ -33,6 +52,11 @@ steps: # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME" displayName: Run unit tests +- script: | + set -e + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests + - script: | set -e pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin.zip * && popd @@ -59,32 +83,14 @@ steps: - script: | set -e - - # remove pkg from archive - zip -d ../VSCode-darwin.zip "*.pkg" - - # publish the build - PACKAGEJSON=`ls ../VSCode-darwin/*.app/Contents/Resources/app/package.json` - VERSION=`node -p "require(\"$PACKAGEJSON\").version"` - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/azure-pipelines/common/publish.js \ - "$(VSCODE_QUALITY)" \ - darwin \ - archive \ - "VSCode-darwin-$(VSCODE_QUALITY).zip" \ - $VERSION \ - true \ - ../VSCode-darwin.zip - - # publish hockeyapp symbols - node build/azure-pipelines/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_MACOS)" - - # upload configuration - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - yarn gulp -- upload-vscode-configuration + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ + ./build/azure-pipelines/darwin/publish.sh displayName: Publish - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: 'Component Detection' + continueOnError: true diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh new file mode 100755 index 00000000000..96d5967ea4b --- /dev/null +++ b/build/azure-pipelines/darwin/publish.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# remove pkg from archive +zip -d ../VSCode-darwin.zip "*.pkg" + +# publish the build +PACKAGEJSON=`ls ../VSCode-darwin/*.app/Contents/Resources/app/package.json` +VERSION=`node -p "require(\"$PACKAGEJSON\").version"` +node build/azure-pipelines/common/publish.js \ + "$VSCODE_QUALITY" \ + darwin \ + archive \ + "VSCode-darwin-$VSCODE_QUALITY.zip" \ + $VERSION \ + true \ + ../VSCode-darwin.zip + +# publish hockeyapp symbols +node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_MACOS" + +# upload configuration +yarn gulp upload-vscode-configuration diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml new file mode 100644 index 00000000000..639456ad4ce --- /dev/null +++ b/build/azure-pipelines/distro-build.yml @@ -0,0 +1,36 @@ +trigger: + branches: + include: ['master', 'release/*'] +pr: + branches: + include: ['master', 'release/*'] + +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- script: | + set -e + + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + + git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" + git fetch distro + git push distro origin/master:refs/heads/master + git merge $(node -p "require('./package.json').distro") + + displayName: Sync & Merge Distro \ No newline at end of file diff --git a/build/azure-pipelines/linux/build.sh b/build/azure-pipelines/linux/build.sh new file mode 100755 index 00000000000..bd251ebc789 --- /dev/null +++ b/build/azure-pipelines/linux/build.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +yarn gulp "vscode-linux-$VSCODE_ARCH-min" \ No newline at end of file diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index 91891e6da94..ee47a8d4320 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -2,20 +2,32 @@ steps: - script: | set -e sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 libgconf-2-4 dbus xvfb libgtk-3-0 + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb sudo chmod +x /etc/init.d/xvfb sudo update-rc.d xvfb defaults sudo service xvfb start - task: NodeTool@0 inputs: - versionSpec: "8.12.0" + versionSpec: "10.15.1" +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" - script: | yarn displayName: Install Dependencies + condition: ne(variables['CacheRestored'], 'true') +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - script: | yarn gulp electron-x64 displayName: Download Electron @@ -25,9 +37,6 @@ steps: - script: | yarn monaco-compile-check displayName: Run Monaco Editor Checks -- script: | - yarn strict-null-check - displayName: Run Strict Null Checks - script: | yarn compile displayName: Compile Sources diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index ff877d440b7..f727e30d252 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -1,12 +1,18 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "8.12.0" + versionSpec: "10.15.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e export npm_config_arch="$(VSCODE_ARCH)" @@ -14,95 +20,57 @@ steps: export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig" fi - echo "machine monacotools.visualstudio.com password $(VSO_PAT)" > ~/.netrc + cat << EOF > ~/.netrc + machine monacotools.visualstudio.com + password $(devops-pat) + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + CHILD_CONCURRENCY=1 yarn - npm run gulp -- hygiene - npm run monaco-compile-check - npm run strict-null-check - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- mixin + yarn gulp mixin + yarn gulp hygiene + yarn monaco-compile-check node build/azure-pipelines/common/installDistro.js node build/lib/builtInExtensions.js + displayName: Prepare build - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- vscode-linux-$(VSCODE_ARCH)-min - name: build + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + ./build/azure-pipelines/linux/build.sh + displayName: Build - script: | set -e - npm run gulp -- "electron-$(VSCODE_ARCH)" + yarn gulp "electron-$(VSCODE_ARCH)" # xvfb seems to be crashing often, let's make sure it's always up service xvfb start DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)" - name: test + displayName: Run unit tests - script: | set -e - REPO="$(pwd)" - ROOT="$REPO/.." - ARCH="$(VSCODE_ARCH)" - - # Publish tarball - PLATFORM_LINUX="linux-$(VSCODE_ARCH)" - [[ "$ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" - [[ "$ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" - BUILDNAME="VSCode-$PLATFORM_LINUX" - BUILD="$ROOT/$BUILDNAME" - BUILD_VERSION="$(date +%s)" - [ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.tar.gz" - TARBALL_PATH="$ROOT/$TARBALL_FILENAME" - PACKAGEJSON="$BUILD/resources/app/package.json" - VERSION=$(node -p "require(\"$PACKAGEJSON\").version") - - rm -rf $ROOT/code-*.tar.* - (cd $ROOT && tar -czf $TARBALL_PATH $BUILDNAME) - - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH" - - # Publish hockeyapp symbols - node build/azure-pipelines/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_LINUX64)" - - # Publish DEB - npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb" - PLATFORM_DEB="linux-deb-$ARCH" - [[ "$ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" - DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" - DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" - - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" package "$DEB_FILENAME" "$VERSION" true "$DEB_PATH" - - # Publish RPM - npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" - PLATFORM_RPM="linux-rpm-$ARCH" - [[ "$ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" - RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" - RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" - - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH" - - # Publish Snap - npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-prepare-snap" - - # Pack snap tarball artifact, in order to preserve file perms - mkdir -p $REPO/.build/linux/snap-tarball - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" - rm -rf $SNAP_TARBALL_PATH - (cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ + ./build/azure-pipelines/linux/publish.sh + displayName: Publish - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: 'Component Detection' + continueOnError: true - task: PublishPipelineArtifact@0 displayName: 'Publish Pipeline Artifact' diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh new file mode 100755 index 00000000000..ca338a6fc5f --- /dev/null +++ b/build/azure-pipelines/linux/publish.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -e +REPO="$(pwd)" +ROOT="$REPO/.." + +# Publish tarball +PLATFORM_LINUX="linux-$VSCODE_ARCH" +[[ "$VSCODE_ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" +[[ "$VSCODE_ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" +BUILDNAME="VSCode-$PLATFORM_LINUX" +BUILD="$ROOT/$BUILDNAME" +BUILD_VERSION="$(date +%s)" +[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.tar.gz" +TARBALL_PATH="$ROOT/$TARBALL_FILENAME" +PACKAGEJSON="$BUILD/resources/app/package.json" +VERSION=$(node -p "require(\"$PACKAGEJSON\").version") + +rm -rf $ROOT/code-*.tar.* +(cd $ROOT && tar -czf $TARBALL_PATH $BUILDNAME) + +node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH" + +# Publish hockeyapp symbols +node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_LINUX64" + +# Publish DEB +yarn gulp "vscode-linux-$VSCODE_ARCH-build-deb" +PLATFORM_DEB="linux-deb-$VSCODE_ARCH" +[[ "$VSCODE_ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" +DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" +DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" + +node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" package "$DEB_FILENAME" "$VERSION" true "$DEB_PATH" + +# Publish RPM +yarn gulp "vscode-linux-$VSCODE_ARCH-build-rpm" +PLATFORM_RPM="linux-rpm-$VSCODE_ARCH" +[[ "$VSCODE_ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" +RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" +RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" + +node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH" + +# Publish Snap +yarn gulp "vscode-linux-$VSCODE_ARCH-prepare-snap" + +# Pack snap tarball artifact, in order to preserve file perms +mkdir -p $REPO/.build/linux/snap-tarball +SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" +rm -rf $SNAP_TARBALL_PATH +(cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 29252107f46..9d98e9fa472 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -1,12 +1,18 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "8.12.0" + versionSpec: "10.15.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - task: DownloadPipelineArtifact@0 displayName: 'Download Pipeline Artifact' inputs: @@ -16,6 +22,14 @@ steps: - script: | set -e + # Get snapcraft version + snapcraft --version + + # Make sure we get latest packages + sudo apt-get update + sudo apt-get upgrade -y + + # Define variables REPO="$(pwd)" ARCH="$(VSCODE_ARCH)" SNAP_ROOT="$REPO/.build/linux/snap/$ARCH" @@ -33,10 +47,9 @@ steps: PACKAGEJSON="$(ls $SNAP_ROOT/code*/usr/share/code*/resources/app/package.json)" VERSION=$(node -p "require(\"$PACKAGEJSON\").version") SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" - (cd $SNAP_ROOT/code-* && snapcraft snap --output "$SNAP_PATH") + (cd $SNAP_ROOT/code-* && sudo snapcraft snap --output "$SNAP_PATH") # Publish snap package - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH" \ No newline at end of file diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index afebc973ccc..c8bedfbffc0 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -1,9 +1,11 @@ resources: containers: - container: vscode-x64 - image: joaomoreno/vscode-linux-build-agent:x64 + endpoint: VSCodeHub + image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 - container: vscode-ia32 - image: joaomoreno/vscode-linux-build-agent:ia32 + endpoint: VSCodeHub + image: vscodehub.azurecr.io/vscode-linux-build-agent:ia32 - container: snapcraft image: snapcore/snapcraft @@ -62,4 +64,18 @@ jobs: pool: vmImage: macOS 10.13 steps: - - template: darwin/product-build-darwin.yml \ No newline at end of file + - template: darwin/product-build-darwin.yml + +- job: Mooncake + pool: + vmImage: 'Ubuntu-16.04' + condition: true + dependsOn: + - Windows + - Windows32 + - Linux + - LinuxSnap + - Linux32 + - macOS + steps: + - template: sync-mooncake.yml \ No newline at end of file diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml new file mode 100644 index 00000000000..f3e8bc07856 --- /dev/null +++ b/build/azure-pipelines/sync-mooncake.yml @@ -0,0 +1,24 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- script: | + set -e + + (cd build ; yarn) + + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \ + node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" diff --git a/build/azure-pipelines/win32/build.ps1 b/build/azure-pipelines/win32/build.ps1 new file mode 100644 index 00000000000..60325ba54ca --- /dev/null +++ b/build/azure-pipelines/win32/build.ps1 @@ -0,0 +1,4 @@ +. build/azure-pipelines/win32/exec.ps1 +$ErrorActionPreference = "Stop" +exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min" } +exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } \ No newline at end of file diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 7145e67e2ad..36336276814 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "8.12.0" + versionSpec: "10.15.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.10.1" @@ -9,9 +9,21 @@ steps: inputs: versionSpec: '2.x' addToPath: true +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' - powershell: | yarn displayName: Install Dependencies + condition: ne(variables['CacheRestored'], 'true') +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules' + vstsFeed: '$(ArtifactFeed)' + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - powershell: | yarn gulp electron displayName: Download Electron @@ -21,9 +33,6 @@ steps: - powershell: | yarn monaco-compile-check displayName: Run Monaco Editor Checks -- script: | - yarn strict-null-check - displayName: Run Strict Null Checks - powershell: | yarn compile displayName: Compile Sources diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index c4922708cbd..8a6a6abb658 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,7 +1,7 @@ steps: - task: NodeTool@0 inputs: - versionSpec: "8.12.0" + versionSpec: "10.15.1" - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: @@ -12,41 +12,59 @@ steps: versionSpec: '2.x' addToPath: true +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - "machine monacotools.visualstudio.com password $(VSO_PAT)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII + "machine monacotools.visualstudio.com`npassword $(devops-pat)`nmachine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII $env:npm_config_arch="$(VSCODE_ARCH)" $env:CHILD_CONCURRENCY="1" - $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" + + exec { git config user.email "vscode@microsoft.com" } + exec { git config user.name "VSCode" } + exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } + exec { git fetch distro } + exec { git merge $(node -p "require('./package.json').distro") } + exec { yarn } - exec { npm run gulp -- hygiene } - exec { npm run monaco-compile-check } - exec { npm run strict-null-check } - exec { npm run gulp -- mixin } + exec { yarn gulp mixin } + exec { yarn gulp hygiene } + exec { yarn monaco-compile-check } exec { node build/azure-pipelines/common/installDistro.js } exec { node build/lib/builtInExtensions.js } + displayName: Prepare build - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" - exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-min" } - exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-inno-updater" } - name: build + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" + .\build\azure-pipelines\win32\build.ps1 + displayName: Build - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { npm run gulp -- "electron-$(VSCODE_ARCH)" } + exec { yarn gulp "electron-$(VSCODE_ARCH)" } exec { .\scripts\test.bat --build --tfs "Unit Tests" } # yarn smoketest -- --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - name: test + displayName: Run unit tests + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp "electron-$(VSCODE_ARCH)" } + exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } + displayName: Run integration tests - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 inputs: ConnectedServiceName: 'ESRP CodeSign' - FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)' + FolderPath: '$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)' Pattern: '*.dll,*.exe,*.node' signConfigType: inlineSignParams inlineOperation: | @@ -114,38 +132,18 @@ steps: - powershell: | $ErrorActionPreference = "Stop" - .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) + .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key) displayName: Import ESRP Auth Certificate - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-archive" "vscode-win32-$(VSCODE_ARCH)-system-setup" "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } - - $Repo = "$(pwd)" - $Root = "$Repo\.." - $SystemExe = "$Repo\.build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe" - $UserExe = "$Repo\.build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe" - $Zip = "$Repo\.build\win32-$(VSCODE_ARCH)\archive\VSCode-win32-$(VSCODE_ARCH).zip" - $Build = "$Root\VSCode-win32-$(VSCODE_ARCH)" - - # get version - $PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json - $Version = $PackageJson.version - $Quality = "$env:VSCODE_QUALITY" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)" - $env:MOONCAKE_STORAGE_ACCESS_KEY = "$(MOONCAKE_STORAGE_ACCESS_KEY)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(AZURE_DOCUMENTDB_MASTERKEY)" - - $assetPlatform = if ("$(VSCODE_ARCH)" -eq "ia32") { "win32" } else { "win32-x64" } - - exec { node build/azure-pipelines/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$(VSCODE_ARCH)-$Version.zip" $Version true $Zip } - exec { node build/azure-pipelines/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $SystemExe } - exec { node build/azure-pipelines/common/publish.js $Quality "$global:assetPlatform-user" setup "VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $UserExe } - - # publish hockeyapp symbols - $hockeyAppId = if ("$(VSCODE_ARCH)" -eq "ia32") { "$(VSCODE_HOCKEYAPP_ID_WIN32)" } else { "$(VSCODE_HOCKEYAPP_ID_WIN64)" } - exec { node build/azure-pipelines/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId } + $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" + $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" + $env:VSCODE_HOCKEYAPP_TOKEN = "$(vscode-hockeyapp-token)" + .\build\azure-pipelines\win32\publish.ps1 + displayName: Publish - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: 'Component Detection' + continueOnError: true diff --git a/build/azure-pipelines/win32/publish.ps1 b/build/azure-pipelines/win32/publish.ps1 new file mode 100644 index 00000000000..8e6fa988adf --- /dev/null +++ b/build/azure-pipelines/win32/publish.ps1 @@ -0,0 +1,28 @@ +. build/azure-pipelines/win32/exec.ps1 +$ErrorActionPreference = "Stop" + +$Arch = "$env:VSCODE_ARCH" + +exec { yarn gulp "vscode-win32-$Arch-archive" "vscode-win32-$Arch-system-setup" "vscode-win32-$Arch-user-setup" --sign } + +$Repo = "$(pwd)" +$Root = "$Repo\.." +$SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe" +$UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe" +$Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip" +$Build = "$Root\VSCode-win32-$Arch" + +# get version +$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json +$Version = $PackageJson.version +$Quality = "$env:VSCODE_QUALITY" + +$AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" } + +exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Version true $Zip } +exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $Version true $SystemExe } +exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $Version true $UserExe } + +# publish hockeyapp symbols +$hockeyAppId = if ("$Arch" -eq "ia32") { "$env:VSCODE_HOCKEYAPP_ID_WIN32" } else { "$env:VSCODE_HOCKEYAPP_ID_WIN64" } +exec { node build/azure-pipelines/common/symbols.js "$env:VSCODE_MIXIN_PASSWORD" "$env:VSCODE_HOCKEYAPP_TOKEN" "$Arch" $hockeyAppId } diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 23c7b13f5df..b460362095f 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.30.1", + "version": "1.33.3", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.30.1", + "version": "1.33.0", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.9", + "version": "0.0.27", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", diff --git a/build/download/download.js b/build/download/download.js new file mode 100644 index 00000000000..c70bae336a6 --- /dev/null +++ b/build/download/download.js @@ -0,0 +1,91 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const https = require("https"); +const fs = require("fs"); +const path = require("path"); +const cp = require("child_process"); +function ensureDir(filepath) { + if (!fs.existsSync(filepath)) { + ensureDir(path.dirname(filepath)); + fs.mkdirSync(filepath); + } +} +function download(options, destination) { + ensureDir(path.dirname(destination)); + return new Promise((c, e) => { + const fd = fs.openSync(destination, 'w'); + const req = https.get(options, (res) => { + res.on('data', (chunk) => { + fs.writeSync(fd, chunk); + }); + res.on('end', () => { + fs.closeSync(fd); + c(); + }); + }); + req.on('error', (reqErr) => { + console.error(`request to ${options.host}${options.path} failed.`); + console.error(reqErr); + e(reqErr); + }); + }); +} +const MARKER_ARGUMENT = `_download_fork_`; +function base64encode(str) { + return Buffer.from(str, 'utf8').toString('base64'); +} +function base64decode(str) { + return Buffer.from(str, 'base64').toString('utf8'); +} +function downloadInExternalProcess(options) { + const url = `https://${options.requestOptions.host}${options.requestOptions.path}`; + console.log(`Downloading ${url}...`); + return new Promise((c, e) => { + const child = cp.fork(__filename, [MARKER_ARGUMENT, base64encode(JSON.stringify(options))], { + stdio: ['pipe', 'pipe', 'pipe', 'ipc'] + }); + let stderr = []; + child.stderr.on('data', (chunk) => { + stderr.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); + }); + child.on('exit', (code) => { + if (code === 0) { + // normal termination + console.log(`Finished downloading ${url}.`); + c(); + } + else { + // abnormal termination + console.error(Buffer.concat(stderr).toString()); + e(new Error(`Download of ${url} failed.`)); + } + }); + }); +} +exports.downloadInExternalProcess = downloadInExternalProcess; +function _downloadInExternalProcess() { + let options; + try { + options = JSON.parse(base64decode(process.argv[3])); + } + catch (err) { + console.error(`Cannot read arguments`); + console.error(err); + process.exit(-1); + return; + } + download(options.requestOptions, options.destinationPath).then(() => { + process.exit(0); + }, (err) => { + console.error(err); + process.exit(-2); + }); +} +if (process.argv.length >= 4 && process.argv[2] === MARKER_ARGUMENT) { + // running as forked download script + _downloadInExternalProcess(); +} diff --git a/build/download/download.ts b/build/download/download.ts new file mode 100644 index 00000000000..01ac8864d0b --- /dev/null +++ b/build/download/download.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as https from 'https'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as cp from 'child_process'; + +function ensureDir(filepath: string) { + if (!fs.existsSync(filepath)) { + ensureDir(path.dirname(filepath)); + fs.mkdirSync(filepath); + } +} + +function download(options: https.RequestOptions, destination: string): Promise { + ensureDir(path.dirname(destination)); + + return new Promise((c, e) => { + const fd = fs.openSync(destination, 'w'); + const req = https.get(options, (res) => { + res.on('data', (chunk) => { + fs.writeSync(fd, chunk); + }); + res.on('end', () => { + fs.closeSync(fd); + c(); + }); + }); + req.on('error', (reqErr) => { + console.error(`request to ${options.host}${options.path} failed.`); + console.error(reqErr); + e(reqErr); + }); + }); +} + +const MARKER_ARGUMENT = `_download_fork_`; + +function base64encode(str: string): string { + return Buffer.from(str, 'utf8').toString('base64'); +} + +function base64decode(str: string): string { + return Buffer.from(str, 'base64').toString('utf8'); +} + +export interface IDownloadRequestOptions { + host: string; + path: string; +} + +export interface IDownloadOptions { + requestOptions: IDownloadRequestOptions; + destinationPath: string; +} + +export function downloadInExternalProcess(options: IDownloadOptions): Promise { + const url = `https://${options.requestOptions.host}${options.requestOptions.path}`; + console.log(`Downloading ${url}...`); + return new Promise((c, e) => { + const child = cp.fork( + __filename, + [MARKER_ARGUMENT, base64encode(JSON.stringify(options))], + { + stdio: ['pipe', 'pipe', 'pipe', 'ipc'] + } + ); + let stderr: Buffer[] = []; + child.stderr.on('data', (chunk) => { + stderr.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk); + }); + child.on('exit', (code) => { + if (code === 0) { + // normal termination + console.log(`Finished downloading ${url}.`); + c(); + } else { + // abnormal termination + console.error(Buffer.concat(stderr).toString()); + e(new Error(`Download of ${url} failed.`)); + } + }); + }); +} + +function _downloadInExternalProcess() { + let options: IDownloadOptions; + try { + options = JSON.parse(base64decode(process.argv[3])); + } catch (err) { + console.error(`Cannot read arguments`); + console.error(err); + process.exit(-1); + return; + } + + download(options.requestOptions, options.destinationPath).then(() => { + process.exit(0); + }, (err) => { + console.error(err); + process.exit(-2); + }); +} + +if (process.argv.length >= 4 && process.argv[2] === MARKER_ARGUMENT) { + // running as forked download script + _downloadInExternalProcess(); +} diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js new file mode 100644 index 00000000000..0dd2e5abf19 --- /dev/null +++ b/build/gulpfile.compile.js @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const util = require('./lib/util'); +const task = require('./lib/task'); +const compilation = require('./lib/compilation'); +const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); + +// Full compile, including nls and inline sources in sourcemaps, for build +const compileClientBuildTask = task.define('compile-client-build', task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true))); + +// All Build +const compileBuildTask = task.define('compile-build', task.parallel(compileClientBuildTask, compileExtensionsBuildTask)); +exports.compileBuildTask = compileBuildTask; \ No newline at end of file diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 1e19fe65e1d..7bbf246a8dc 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -6,6 +6,7 @@ const gulp = require('gulp'); const path = require('path'); const util = require('./lib/util'); +const task = require('./lib/task'); const common = require('./lib/optimize'); const es = require('event-stream'); const File = require('vinyl'); @@ -48,9 +49,6 @@ var editorResources = [ '!**/test/**' ]; -var editorOtherSources = [ -]; - var BUNDLED_FILE_HEADER = [ '/*!-----------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', @@ -63,8 +61,7 @@ var BUNDLED_FILE_HEADER = [ const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); -gulp.task('clean-editor-src', util.rimraf('out-editor-src')); -gulp.task('extract-editor-src', ['clean-editor-src'], function () { +const extractEditorSrcTask = task.define('extract-editor-src', () => { console.log(`If the build fails, consider tweaking shakeLevel below to a lower value.`); const apiusages = monacoapi.execute().usageContent; const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString(); @@ -84,6 +81,7 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function () { 'typings/thenable.d.ts', 'typings/es6-promise.d.ts', 'typings/require-monaco.d.ts', + "typings/lib.es2018.promise.d.ts", 'vs/monaco.d.ts' ], libs: [ @@ -95,20 +93,16 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function () { 'vs/base/browser/ui/octiconLabel/octiconLabel': 'vs/base/browser/ui/octiconLabel/octiconLabel.mock', }, shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers - importIgnorePattern: /^vs\/css!/, + importIgnorePattern: /(^vs\/css!)|(promise-polyfill\/polyfill)/, destRoot: path.join(root, 'out-editor-src') }); }); -// Full compile, including nls and inline sources in sourcemaps, for build -gulp.task('clean-editor-build', util.rimraf('out-editor-build')); -gulp.task('compile-editor-build', ['clean-editor-build', 'extract-editor-src'], compilation.compileTask('out-editor-src', 'out-editor-build', true)); +const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true)); -gulp.task('clean-optimized-editor', util.rimraf('out-editor')); -gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-editor-build'], common.optimizeTask({ +const optimizeEditorAMDTask = task.define('optimize-editor-amd', common.optimizeTask({ src: 'out-editor-build', entryPoints: editorEntryPoints, - otherSources: editorOtherSources, resources: editorResources, loaderConfig: { paths: { @@ -125,11 +119,9 @@ gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-editor-build'], languages: languages })); -gulp.task('clean-minified-editor', util.rimraf('out-editor-min')); -gulp.task('minify-editor', ['clean-minified-editor', 'optimize-editor'], common.minifyTask('out-editor')); +const minifyEditorAMDTask = task.define('minify-editor-amd', 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', 'extract-editor-src'], function () { +const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => { standalone.createESMSourcesAndResources2({ srcFolder: './out-editor-src', outFolder: './out-editor-esm', @@ -151,7 +143,8 @@ gulp.task('extract-editor-esm', ['clean-editor-esm', 'clean-editor-distro', 'ext } }); }); -gulp.task('compile-editor-esm', ['extract-editor-esm', 'clean-editor-distro'], function () { + +const compileEditorESMTask = task.define('compile-editor-esm', () => { if (process.platform === 'win32') { const result = cp.spawnSync(`..\\node_modules\\.bin\\tsc.cmd`, { cwd: path.join(__dirname, '../out-editor-esm') @@ -202,8 +195,16 @@ function toExternalDTS(contents) { return lines.join('\n'); } -gulp.task('clean-editor-distro', util.rimraf('out-monaco-editor-core')); -gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify-editor', 'optimize-editor'], function () { +function filterStream(testFunc) { + return es.through(function (data) { + if (!testFunc(data.relative)) { + return; + } + this.emit('data', data); + }); +} + +const finalEditorResourcesTask = task.define('final-editor-resources', () => { return es.merge( // other assets es.merge( @@ -233,6 +234,14 @@ gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify })) .pipe(gulp.dest('out-monaco-editor-core')), + // version.txt + gulp.src('build/monaco/version.txt') + .pipe(es.through(function (data) { + data.contents = Buffer.from(`monaco-editor-core: https://github.com/Microsoft/vscode/tree/${sha1}`); + this.emit('data', data); + })) + .pipe(gulp.dest('out-monaco-editor-core')), + // README.md gulp.src('build/monaco/README-npm.md') .pipe(es.through(function (data) { @@ -266,7 +275,7 @@ gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify var strContents = data.contents.toString(); var newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/'); - strContents = strContents.replace(/\/\/\# sourceMappingURL=[^ ]+$/, newStr); + strContents = strContents.replace(/\/\/# sourceMappingURL=[^ ]+$/, newStr); data.contents = Buffer.from(strContents); this.emit('data', data); @@ -282,59 +291,31 @@ gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify ); }); -gulp.task('analyze-editor-distro', function () { - // @ts-ignore - var bundleInfo = require('../out-editor/bundleInfo.json'); - var graph = bundleInfo.graph; - var bundles = bundleInfo.bundles; - - var inverseGraph = {}; - Object.keys(graph).forEach(function (module) { - var dependencies = graph[module]; - dependencies.forEach(function (dep) { - inverseGraph[dep] = inverseGraph[dep] || []; - inverseGraph[dep].push(module); - }); - }); - - var detailed = {}; - Object.keys(bundles).forEach(function (entryPoint) { - var included = bundles[entryPoint]; - var includedMap = {}; - included.forEach(function (included) { - includedMap[included] = true; - }); - - var explanation = []; - included.map(function (included) { - if (included.indexOf('!') >= 0) { - return; - } - - var reason = (inverseGraph[included] || []).filter(function (mod) { - return !!includedMap[mod]; - }); - explanation.push({ - module: included, - reason: reason - }); - }); - - detailed[entryPoint] = explanation; - }); - - console.log(JSON.stringify(detailed, null, '\t')); -}); - -function filterStream(testFunc) { - return es.through(function (data) { - if (!testFunc(data.relative)) { - return; - } - this.emit('data', data); - }); -} - +gulp.task('editor-distro', + task.series( + task.parallel( + util.rimraf('out-editor-src'), + util.rimraf('out-editor-build'), + util.rimraf('out-editor-esm'), + util.rimraf('out-monaco-editor-core'), + util.rimraf('out-editor'), + util.rimraf('out-editor-min') + ), + extractEditorSrcTask, + task.parallel( + task.series( + compileEditorAMDTask, + optimizeEditorAMDTask, + minifyEditorAMDTask + ), + task.series( + createESMSourcesAndResourcesTask, + compileEditorESMTask + ) + ), + finalEditorResourcesTask + ) +); //#region monaco type checking @@ -354,6 +335,7 @@ function createTscCompileTask(watch) { let errors = []; let reporter = createReporter(); let report; + // eslint-disable-next-line no-control-regex let magic = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings child.stdout.on('data', data => { @@ -387,7 +369,10 @@ function createTscCompileTask(watch) { }; } -gulp.task('monaco-typecheck-watch', createTscCompileTask(true)); -gulp.task('monaco-typecheck', createTscCompileTask(false)); +const monacoTypecheckWatchTask = task.define('monaco-typecheck-watch', createTscCompileTask(true)); +exports.monacoTypecheckWatchTask = monacoTypecheckWatchTask; + +const monacoTypecheckTask = task.define('monaco-typecheck', createTscCompileTask(false)); +exports.monacoTypecheckTask = monacoTypecheckTask; //#endregion diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 1488f96ec44..c72b26cafed 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -11,8 +11,8 @@ const path = require('path'); const tsb = require('gulp-tsb'); const es = require('event-stream'); const filter = require('gulp-filter'); -const rimraf = require('rimraf'); const util = require('./lib/util'); +const task = require('./lib/task'); const watcher = require('./lib/watch'); const createReporter = require('./lib/reporter').createReporter; const glob = require('glob'); @@ -43,16 +43,6 @@ const tasks = compilations.map(function (tsconfigFile) { const name = relativeDirname.replace(/\//g, '-'); - // Tasks - const clean = 'clean-extension:' + name; - const compile = 'compile-extension:' + name; - const watch = 'watch-extension:' + name; - - // Build Tasks - const cleanBuild = 'clean-extension-build:' + name; - const compileBuild = 'compile-extension-build:' + name; - const watchBuild = 'watch-extension-build:' + name; - const root = path.join('extensions', relativeDirname); const srcBase = path.join(root, 'src'); const src = path.join(srcBase, '**'); @@ -111,18 +101,18 @@ const tasks = compilations.map(function (tsconfigFile) { const srcOpts = { cwd: path.dirname(__dirname), base: srcBase }; - gulp.task(clean, cb => rimraf(out, cb)); + const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out)); - gulp.task(compile, [clean], () => { + const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false, true); const input = gulp.src(src, srcOpts); return input .pipe(pipeline()) .pipe(gulp.dest(out)); - }); + })); - gulp.task(watch, [clean], () => { + const watchTask = task.define(`watch-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false); const input = gulp.src(src, srcOpts); const watchInput = watcher(src, srcOpts); @@ -130,43 +120,35 @@ const tasks = compilations.map(function (tsconfigFile) { return watchInput .pipe(util.incremental(pipeline, input)) .pipe(gulp.dest(out)); - }); + })); - gulp.task(cleanBuild, cb => rimraf(out, cb)); - - gulp.task(compileBuild, [clean], () => { + const compileBuildTask = task.define(`compile-build-extension-${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(true, true); const input = gulp.src(src, srcOpts); return input .pipe(pipeline()) .pipe(gulp.dest(out)); - }); + })); - gulp.task(watchBuild, [clean], () => { - const pipeline = createPipeline(true); - const input = gulp.src(src, srcOpts); - const watchInput = watcher(src, srcOpts); - - return watchInput - .pipe(util.incremental(() => pipeline(), input)) - .pipe(gulp.dest(out)); - }); + // Tasks + gulp.task(compileTask); + gulp.task(watchTask); return { - clean: clean, - compile: compile, - watch: watch, - cleanBuild: cleanBuild, - compileBuild: compileBuild, - watchBuild: watchBuild + compileTask: compileTask, + watchTask: watchTask, + compileBuildTask: compileBuildTask }; }); -gulp.task('clean-extensions', tasks.map(t => t.clean)); -gulp.task('compile-extensions', tasks.map(t => t.compile)); -gulp.task('watch-extensions', tasks.map(t => t.watch)); +const compileExtensionsTask = task.define('compile-extensions', task.parallel(...tasks.map(t => t.compileTask))); +gulp.task(compileExtensionsTask); +exports.compileExtensionsTask = compileExtensionsTask; -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)); +const watchExtensionsTask = task.define('watch-extensions', task.parallel(...tasks.map(t => t.watchTask))); +gulp.task(watchExtensionsTask); +exports.watchExtensionsTask = watchExtensionsTask; + +const compileExtensionsBuildTask = task.define('compile-extensions-build', task.parallel(...tasks.map(t => t.compileBuildTask))); +exports.compileExtensionsBuildTask = compileExtensionsBuildTask; diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 16d187ffc2c..50082693334 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -42,7 +42,8 @@ const indentationFilter = [ // except specific files '!ThirdPartyNotices.txt', - '!LICENSE.txt', + '!LICENSE.{txt,rtf}', + '!LICENSES.chromium.html', '!**/LICENSE', '!src/vs/nls.js', '!src/vs/nls.build.js', @@ -50,7 +51,6 @@ const indentationFilter = [ '!src/vs/css.build.js', '!src/vs/loader.js', '!src/vs/base/common/marked/marked.js', - '!src/vs/base/common/winjs.base.js', '!src/vs/base/node/terminateProcess.sh', '!src/vs/base/node/cpuUsage.sh', '!test/assert.js', @@ -82,11 +82,12 @@ const indentationFilter = [ '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns}', - '!build/{lib,tslintRules}/**/*.js', + '!build/{lib,tslintRules,download}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', '!build/azure-pipelines/**/*.config', '!**/Dockerfile', + '!**/Dockerfile.*', '!**/*.Dockerfile', '!**/*.dockerfile', '!extensions/markdown-language-features/media/*.js' @@ -110,10 +111,12 @@ const copyrightFilter = [ '!**/*.opts', '!**/*.disabled', '!**/*.code-workspace', + '!**/promise-polyfill/polyfill.js', '!build/**/*.init', '!resources/linux/snap/snapcraft.yaml', '!resources/linux/snap/electron-launch', '!resources/win32/bin/code.js', + '!resources/completions/**', '!extensions/markdown-language-features/media/highlight.css', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*' @@ -127,7 +130,6 @@ const eslintFilter = [ '!src/vs/nls.js', '!src/vs/css.build.js', '!src/vs/nls.build.js', - '!src/**/winjs.base.js', '!src/**/marked.js', '!**/test/**' ]; @@ -228,7 +230,7 @@ function hygiene(some) { let formatted = result.dest.replace(/\r\n/gm, '\n'); if (original !== formatted) { - console.error('File not formatted:', file.relative); + console.error("File not formatted. Run the 'Format Document' command to fix it:", file.relative); errorCount++; } cb(null, file); diff --git a/build/gulpfile.mixin.js b/build/gulpfile.mixin.js index 0b032152cf9..690cc874899 100644 --- a/build/gulpfile.mixin.js +++ b/build/gulpfile.mixin.js @@ -10,20 +10,11 @@ const json = require('gulp-json-editor'); const buffer = require('gulp-buffer'); const filter = require('gulp-filter'); const es = require('event-stream'); -const util = require('./lib/util'); -const remote = require('gulp-remote-src'); -const zip = require('gulp-vinyl-zip'); - -const pkg = require('../package.json'); +const vfs = require('vinyl-fs'); +const fancyLog = require('fancy-log'); +const ansiColors = require('ansi-colors'); gulp.task('mixin', function () { - const repo = process.env['VSCODE_MIXIN_REPO']; - - if (!repo) { - console.log('Missing VSCODE_MIXIN_REPO, skipping mixin'); - return; - } - const quality = process.env['VSCODE_QUALITY']; if (!quality) { @@ -31,38 +22,18 @@ gulp.task('mixin', function () { return; } - const url = `https://github.com/${repo}/archive/${pkg.distro}.zip`; - const opts = { base: url }; - const username = process.env['VSCODE_MIXIN_USERNAME']; - const password = process.env['VSCODE_MIXIN_PASSWORD']; + const productJsonFilter = filter('product.json', { restore: true }); - if (username || password) { - opts.auth = { user: username || '', pass: password || '' }; - } - - console.log('Mixing in sources from \'' + url + '\':'); - - let all = remote('', opts) - .pipe(zip.src()) + fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); + return vfs + .src(`quality/${quality}/**`, { base: `quality/${quality}` }) .pipe(filter(function (f) { return !f.isDirectory(); })) - .pipe(util.rebase(1)); - - if (quality) { - const productJsonFilter = filter('product.json', { restore: true }); - const mixin = all - .pipe(filter(['quality/' + quality + '/**'])) - .pipe(util.rebase(2)) - .pipe(productJsonFilter) - .pipe(buffer()) - .pipe(json(o => Object.assign({}, require('../product.json'), o))) - .pipe(productJsonFilter.restore); - - all = es.merge(mixin); - } - - return all + .pipe(productJsonFilter) + .pipe(buffer()) + .pipe(json(o => Object.assign({}, require('../product.json'), o))) + .pipe(productJsonFilter.restore) .pipe(es.mapSync(function (f) { - console.log(f.relative); + fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); return f; })) .pipe(gulp.dest('.')); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 326fac6bc24..57ca292ef73 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -20,6 +20,7 @@ const filter = require('gulp-filter'); const json = require('gulp-json-editor'); const _ = require('underscore'); const util = require('./lib/util'); +const task = require('./lib/task'); const ext = require('./lib/extensions'); const buildfile = require('../src/buildfile'); const common = require('./lib/optimize'); @@ -32,6 +33,8 @@ const i18n = require('./lib/i18n'); const deps = require('./dependencies'); const getElectronVersion = require('./lib/electron').getElectronVersion; const createAsar = require('./lib/asar').createAsar; +const minimist = require('minimist'); +const { compileBuildTask } = require('./gulpfile.compile'); const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); // @ts-ignore @@ -60,16 +63,19 @@ const vscodeResources = [ 'out-build/bootstrap-window.js', 'out-build/paths.js', 'out-build/vs/**/*.{svg,png,cur,html}', + '!out-build/vs/code/browser/**/*.html', 'out-build/vs/base/common/performance.js', - 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh}', + 'out-build/vs/base/node/languagePacks.js', + 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', 'out-build/vs/base/browser/ui/octiconLabel/octicons/**', 'out-build/vs/workbench/browser/media/*-theme.css', - 'out-build/vs/workbench/parts/debug/**/*.json', - 'out-build/vs/workbench/parts/execution/**/*.scpt', - 'out-build/vs/workbench/parts/webview/electron-browser/webview-pre.js', + 'out-build/vs/workbench/contrib/debug/**/*.json', + 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', + 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', + 'out-build/vs/workbench/contrib/webview/electron-browser/pre/*.js', 'out-build/vs/**/markdown.css', - 'out-build/vs/workbench/parts/tasks/**/*.json', - 'out-build/vs/workbench/parts/welcome/walkThrough/**/*.md', + 'out-build/vs/workbench/contrib/tasks/**/*.json', + 'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md', 'out-build/vs/workbench/services/files/**/*.exe', 'out-build/vs/workbench/services/files/**/*.md', 'out-build/vs/code/electron-browser/workbench/**', @@ -85,29 +91,41 @@ const BUNDLED_FILE_HEADER = [ ' *--------------------------------------------------------*/' ].join('\n'); -gulp.task('clean-optimized-vscode', util.rimraf('out-vscode')); -gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({ - src: 'out-build', - entryPoints: vscodeEntryPoints, - otherSources: [], - resources: vscodeResources, - loaderConfig: common.loaderConfig(nodeModules), - header: BUNDLED_FILE_HEADER, - out: 'out-vscode', - bundleInfo: undefined -})); +const optimizeVSCodeTask = task.define('optimize-vscode', task.series( + task.parallel( + util.rimraf('out-vscode'), + compileBuildTask + ), + common.optimizeTask({ + src: 'out-build', + entryPoints: vscodeEntryPoints, + resources: vscodeResources, + loaderConfig: common.loaderConfig(nodeModules), + header: BUNDLED_FILE_HEADER, + out: 'out-vscode', + bundleInfo: undefined + }) +)); -gulp.task('optimize-index-js', ['optimize-vscode'], () => { - const fullpath = path.join(process.cwd(), 'out-vscode/vs/code/electron-browser/workbench/workbench.js'); - const contents = fs.readFileSync(fullpath).toString(); - const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); - fs.writeFileSync(fullpath, newContents); -}); +const optimizeIndexJSTask = task.define('optimize-index-js', task.series( + optimizeVSCodeTask, + () => { + const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js'); + const contents = fs.readFileSync(fullpath).toString(); + const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); + fs.writeFileSync(fullpath, newContents); + } +)); 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', `${sourceMappingURLBase}/core`)); +const minifyVSCodeTask = task.define('minify-vscode', task.series( + task.parallel( + util.rimraf('out-vscode-min'), + optimizeIndexJSTask + ), + common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`) +)); // Package @@ -128,7 +146,7 @@ const config = { version: getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', - copyright: 'Copyright (C) 2018 Microsoft. All rights reserved', + copyright: 'Copyright (C) 2019 Microsoft. All rights reserved', darwinIcon: 'resources/darwin/code.icns', darwinBundleIdentifier: product.darwinBundleIdentifier, darwinApplicationCategoryType: 'public.app-category.developer-tools', @@ -170,13 +188,13 @@ const config = { urlSchemes: [product.urlProtocol] }], darwinForceDarkModeSupport: true, - darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : void 0, + darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined, linuxExecutableName: product.applicationName, winIcon: 'resources/win32/code.ico', - token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || void 0, + token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || undefined, // @ts-ignore JSON checking: electronRepository is optional - repo: product.electronRepository || void 0 + repo: product.electronRepository || undefined }; function getElectron(arch) { @@ -196,13 +214,11 @@ function getElectron(arch) { }; } -gulp.task('clean-electron', util.rimraf('.build/electron')); -gulp.task('electron', ['clean-electron'], getElectron(process.arch)); -gulp.task('electron-ia32', ['clean-electron'], getElectron('ia32')); -gulp.task('electron-x64', ['clean-electron'], getElectron('x64')); -gulp.task('electron-arm', ['clean-electron'], getElectron('arm')); -gulp.task('electron-arm64', ['clean-electron'], getElectron('arm64')); - +gulp.task(task.define('electron', task.series(util.rimraf('.build/electron'), getElectron(process.arch)))); +gulp.task(task.define('electron-ia32', task.series(util.rimraf('.build/electron'), getElectron('ia32')))); +gulp.task(task.define('electron-x64', task.series(util.rimraf('.build/electron'), getElectron('x64')))); +gulp.task(task.define('electron-arm', task.series(util.rimraf('.build/electron'), getElectron('armv7l')))); +gulp.task(task.define('electron-arm64', task.series(util.rimraf('.build/electron'), getElectron('arm64')))); /** * Compute checksums for some files. @@ -238,14 +254,14 @@ function computeChecksum(filename) { return hash; } -function packageTask(platform, arch, opts) { +function packageTask(platform, arch, sourceFolderName, destinationFolderName, opts) { opts = opts || {}; - const destination = path.join(path.dirname(root), 'VSCode') + (platform ? '-' + platform : '') + (arch ? '-' + arch : ''); + const destination = path.join(path.dirname(root), destinationFolderName); platform = platform || process.platform; return () => { - const out = opts.minified ? 'out-vscode-min' : 'out-vscode'; + const out = sourceFolderName; const checksums = computeChecksums(out, [ 'vs/workbench/workbench.main.js', @@ -294,9 +310,7 @@ function packageTask(platform, arch, opts) { const productJsonStream = gulp.src(['product.json'], { base: '.' }) .pipe(json(productJsonUpdate)); - const license = gulp.src(['LICENSES.chromium.html', 'LICENSE.txt', 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.' }); - - const watermark = gulp.src(['resources/letterpress.svg', 'resources/letterpress-dark.svg', 'resources/letterpress-hc.svg'], { base: '.' }); + const license = gulp.src(['LICENSES.chromium.html', product.licenseFileName, 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.', allowEmpty: true }); // TODO the API should be copied to `out` during compile, not here const api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts')); @@ -309,29 +323,13 @@ function packageTask(platform, arch, opts) { const deps = gulp.src(depsSrc, { base: '.', dot: true }) .pipe(filter(['**', '!**/package-lock.json'])) - .pipe(util.cleanNodeModule('fsevents', ['binding.gyp', 'fsevents.cc', 'build/**', 'src/**', 'test/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('vscode-sqlite3', ['binding.gyp', 'benchmark/**', 'cloudformation/**', 'deps/**', 'test/**', 'build/**', 'src/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/*.js'])) - .pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('jschardet', ['dist/**'])) - .pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('windows-process-tree', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/index.js'])) - .pipe(util.cleanNodeModule('keytar', ['binding.gyp', 'build/**', 'src/**', 'script/**', 'node_modules/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/*.exe', 'build/Release/*.dll', 'build/Release/*.node'])) - .pipe(util.cleanNodeModule('vscode-nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['build/Release/*.node', '**/*.a'])) - .pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node'])) + .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))) .pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar')); let all = es.merge( - packageJsonStream, + packageJsonStream, productJsonStream, license, - watermark, api, sources, deps @@ -384,8 +382,10 @@ function packageTask(platform, arch, opts) { .pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true }))) .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); + // result = es.merge(result, gulp.src('resources/completions/**', { base: '.' })); + if (platform === 'win32') { - result = es.merge(result, gulp.src('resources/win32/bin/code.js', { base: 'resources/win32' })); + result = es.merge(result, gulp.src('resources/win32/bin/code.js', { base: 'resources/win32', allowEmpty: true })); result = es.merge(result, gulp.src('resources/win32/bin/code.cmd', { base: 'resources/win32' }) .pipe(replace('@@NAME@@', product.nameShort)) @@ -393,14 +393,18 @@ function packageTask(platform, arch, opts) { result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' }) .pipe(replace('@@NAME@@', product.nameShort)) + .pipe(replace('@@PRODNAME@@', product.nameLong)) + .pipe(replace('@@VERSION@@', version)) .pipe(replace('@@COMMIT@@', commit)) .pipe(replace('@@APPNAME@@', product.applicationName)) + .pipe(replace('@@QUALITY@@', quality)) .pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; }))); result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' }) .pipe(rename(product.nameShort + '.VisualElementsManifest.xml'))); } else if (platform === 'linux') { result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) + .pipe(replace('@@PRODNAME@@', product.nameLong)) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(rename('bin/' + product.applicationName))); } @@ -420,29 +424,35 @@ function packageTask(platform, arch, opts) { const buildRoot = path.dirname(root); -gulp.task('clean-vscode-win32-ia32', util.rimraf(path.join(buildRoot, 'VSCode-win32-ia32'))); -gulp.task('clean-vscode-win32-x64', util.rimraf(path.join(buildRoot, 'VSCode-win32-x64'))); -gulp.task('clean-vscode-darwin', util.rimraf(path.join(buildRoot, 'VSCode-darwin'))); -gulp.task('clean-vscode-linux-ia32', util.rimraf(path.join(buildRoot, 'VSCode-linux-ia32'))); -gulp.task('clean-vscode-linux-x64', util.rimraf(path.join(buildRoot, 'VSCode-linux-x64'))); -gulp.task('clean-vscode-linux-arm', util.rimraf(path.join(buildRoot, 'VSCode-linux-arm'))); -gulp.task('clean-vscode-linux-arm64', util.rimraf(path.join(buildRoot, 'VSCode-linux-arm64'))); +const BUILD_TARGETS = [ + { platform: 'win32', arch: 'ia32' }, + { platform: 'win32', arch: 'x64' }, + { platform: 'darwin', arch: null, opts: { stats: true } }, + { platform: 'linux', arch: 'ia32' }, + { platform: 'linux', arch: 'x64' }, + { platform: 'linux', arch: 'arm' }, + { platform: 'linux', arch: 'arm64' }, +]; +BUILD_TARGETS.forEach(buildTarget => { + const dashed = (str) => (str ? `-${str}` : ``); + const platform = buildTarget.platform; + const arch = buildTarget.arch; + const opts = buildTarget.opts; -gulp.task('vscode-win32-ia32', ['optimize-vscode', 'clean-vscode-win32-ia32'], packageTask('win32', 'ia32')); -gulp.task('vscode-win32-x64', ['optimize-vscode', 'clean-vscode-win32-x64'], packageTask('win32', 'x64')); -gulp.task('vscode-darwin', ['optimize-vscode', 'clean-vscode-darwin'], packageTask('darwin', null, { stats: true })); -gulp.task('vscode-linux-ia32', ['optimize-vscode', 'clean-vscode-linux-ia32'], packageTask('linux', 'ia32')); -gulp.task('vscode-linux-x64', ['optimize-vscode', 'clean-vscode-linux-x64'], packageTask('linux', 'x64')); -gulp.task('vscode-linux-arm', ['optimize-vscode', 'clean-vscode-linux-arm'], packageTask('linux', 'arm')); -gulp.task('vscode-linux-arm64', ['optimize-vscode', 'clean-vscode-linux-arm64'], packageTask('linux', 'arm64')); + ['', 'min'].forEach(minified => { + const sourceFolderName = `out-vscode${dashed(minified)}`; + const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`; -gulp.task('vscode-win32-ia32-min', ['minify-vscode', 'clean-vscode-win32-ia32'], packageTask('win32', 'ia32', { minified: true })); -gulp.task('vscode-win32-x64-min', ['minify-vscode', 'clean-vscode-win32-x64'], packageTask('win32', 'x64', { minified: true })); -gulp.task('vscode-darwin-min', ['minify-vscode', 'clean-vscode-darwin'], packageTask('darwin', null, { minified: true, stats: true })); -gulp.task('vscode-linux-ia32-min', ['minify-vscode', 'clean-vscode-linux-ia32'], packageTask('linux', 'ia32', { minified: true })); -gulp.task('vscode-linux-x64-min', ['minify-vscode', 'clean-vscode-linux-x64'], packageTask('linux', 'x64', { minified: true })); -gulp.task('vscode-linux-arm-min', ['minify-vscode', 'clean-vscode-linux-arm'], packageTask('linux', 'arm', { minified: true })); -gulp.task('vscode-linux-arm64-min', ['minify-vscode', 'clean-vscode-linux-arm64'], packageTask('linux', 'arm64', { minified: true })); + const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( + task.parallel( + minified ? minifyVSCodeTask : optimizeVSCodeTask, + util.rimraf(path.join(buildRoot, destinationFolderName)) + ), + packageTask(platform, arch, sourceFolderName, destinationFolderName, opts) + )); + gulp.task(vscodeTask); + }); +}); // Transifex Localizations @@ -465,42 +475,60 @@ const apiHostname = process.env.TRANSIFEX_API_URL; const apiName = process.env.TRANSIFEX_API_NAME; const apiToken = process.env.TRANSIFEX_API_TOKEN; -gulp.task('vscode-translations-push', ['optimize-vscode'], function () { - const pathToMetadata = './out-vscode/nls.metadata.json'; - const pathToExtensions = './extensions/*'; - const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; +gulp.task(task.define( + 'vscode-translations-push', + task.series( + optimizeVSCodeTask, + function () { + const pathToMetadata = './out-vscode/nls.metadata.json'; + const pathToExtensions = './extensions/*'; + const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; - return es.merge( - gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), - gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), - gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) - ).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken) - ).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken)); -}); + return es.merge( + gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), + gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), + gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) + ).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken) + ).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken)); + } + ) +)); -gulp.task('vscode-translations-push-test', ['optimize-vscode'], function () { - const pathToMetadata = './out-vscode/nls.metadata.json'; - const pathToExtensions = './extensions/*'; - const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; +gulp.task(task.define( + 'vscode-translations-export', + task.series( + optimizeVSCodeTask, + function () { + const pathToMetadata = './out-vscode/nls.metadata.json'; + const pathToExtensions = './extensions/*'; + const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; - return es.merge( - gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), - gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), - gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) - ).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken) - ).pipe(vfs.dest('../vscode-transifex-input')); -}); + return es.merge( + gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), + gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), + gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) + ).pipe(vfs.dest('../vscode-translations-export')); + } + ) +)); gulp.task('vscode-translations-pull', function () { return es.merge([...i18n.defaultLanguages, ...i18n.extraLanguages].map(language => { let includeDefault = !!innoSetupConfig[language.id].defaultInfo; - return i18n.pullSetupXlfFiles(apiHostname, apiName, apiToken, language, includeDefault).pipe(vfs.dest(`../vscode-localization/${language.id}/setup`)); + return i18n.pullSetupXlfFiles(apiHostname, apiName, apiToken, language, includeDefault).pipe(vfs.dest(`../vscode-translations-import/${language.id}/setup`)); })); }); gulp.task('vscode-translations-import', function () { + var options = minimist(process.argv.slice(2), { + string: 'location', + default: { + location: '../vscode-translations-import' + } + }); return es.merge([...i18n.defaultLanguages, ...i18n.extraLanguages].map(language => { - return gulp.src(`../vscode-localization/${language.id}/setup/*/*.xlf`) + let id = language.transifexId || language.id; + return gulp.src(`${options.location}/${id}/setup/*/*.xlf`) .pipe(i18n.prepareIslFiles(language, innoSetupConfig[language.id])) .pipe(vfs.dest(`./build/win32/i18n`)); })); @@ -508,7 +536,7 @@ gulp.task('vscode-translations-import', function () { // Sourcemaps -gulp.task('upload-vscode-sourcemaps', ['vscode-darwin-min', 'minify-vscode'], () => { +gulp.task('upload-vscode-sourcemaps', () => { const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) .pipe(es.mapSync(f => { f.path = `${f.base}/core/${f.relative}`; @@ -532,57 +560,8 @@ gulp.task('upload-vscode-sourcemaps', ['vscode-darwin-min', 'minify-vscode'], () })); }); -const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json'); -gulp.task('upload-vscode-configuration', ['generate-vscode-configuration'], () => { - if (!shouldSetupSettingsSearch()) { - const branch = process.env.BUILD_SOURCEBRANCH; - console.log(`Only runs on master and release branches, not ${branch}`); - return; - } - - if (!fs.existsSync(allConfigDetailsPath)) { - throw new Error(`configuration file at ${allConfigDetailsPath} does not exist`); - } - - const settingsSearchBuildId = getSettingsSearchBuildId(packageJson); - if (!settingsSearchBuildId) { - throw new Error('Failed to compute build number'); - } - - return gulp.src(allConfigDetailsPath) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'configuration', - prefix: `${settingsSearchBuildId}/${commit}/` - })); -}); - -function shouldSetupSettingsSearch() { - const branch = process.env.BUILD_SOURCEBRANCH; - return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0); -} - -function getSettingsSearchBuildId(packageJson) { - try { - const branch = process.env.BUILD_SOURCEBRANCH; - const branchId = branch.indexOf('/release/') >= 0 ? 0 : - /\/master$/.test(branch) ? 1 : - 2; // Some unexpected branch - - const out = cp.execSync(`git rev-list HEAD --count`); - const count = parseInt(out.toString()); - - // - // 1.25.1, 1,234,567 commits, master = 1250112345671 - return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; - } catch (e) { - throw new Error('Could not determine build number: ' + e.toString()); - } -} - // This task is only run for the MacOS build -gulp.task('generate-vscode-configuration', () => { +const generateVSCodeConfigurationTask = task.define('generate-vscode-configuration', () => { return new Promise((resolve, reject) => { const buildDir = process.env['AGENT_BUILDDIRECTORY']; if (!buildDir) { @@ -618,3 +597,59 @@ gulp.task('generate-vscode-configuration', () => { }); }); }); + +const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json'); +gulp.task(task.define( + 'upload-vscode-configuration', + task.series( + generateVSCodeConfigurationTask, + () => { + if (!shouldSetupSettingsSearch()) { + const branch = process.env.BUILD_SOURCEBRANCH; + console.log(`Only runs on master and release branches, not ${branch}`); + return; + } + + if (!fs.existsSync(allConfigDetailsPath)) { + throw new Error(`configuration file at ${allConfigDetailsPath} does not exist`); + } + + const settingsSearchBuildId = getSettingsSearchBuildId(packageJson); + if (!settingsSearchBuildId) { + throw new Error('Failed to compute build number'); + } + + return gulp.src(allConfigDetailsPath) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + key: process.env.AZURE_STORAGE_ACCESS_KEY, + container: 'configuration', + prefix: `${settingsSearchBuildId}/${commit}/` + })); + } + ) +)); + +function shouldSetupSettingsSearch() { + const branch = process.env.BUILD_SOURCEBRANCH; + return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0); +} + +function getSettingsSearchBuildId(packageJson) { + try { + const branch = process.env.BUILD_SOURCEBRANCH; + const branchId = branch.indexOf('/release/') >= 0 ? 0 : + /\/master$/.test(branch) ? 1 : + 2; // Some unexpected branch + + const out = cp.execSync(`git rev-list HEAD --count`); + const count = parseInt(out.toString()); + + // + // 1.25.1, 1,234,567 commits, master = 1250112345671 + return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; + } catch (e) { + throw new Error('Could not determine build number: ' + e.toString()); + } +} + diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 50258a98fb3..9b0abd18d9a 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -12,9 +12,13 @@ const shell = require('gulp-shell'); const es = require('event-stream'); const vfs = require('vinyl-fs'); const util = require('./lib/util'); +const task = require('./lib/task'); const packageJson = require('../package.json'); const product = require('../product.json'); const rpmDependencies = require('../resources/linux/rpm/dependencies.json'); +const path = require('path'); +const root = path.dirname(__dirname); +const commit = util.getVersion(root); const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); @@ -38,7 +42,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', product.applicationName)) + .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) @@ -48,7 +52,13 @@ function prepareDebPackage(arch) { .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); const icon = gulp.src('resources/linux/code.png', { base: '.' }) - .pipe(rename('usr/share/pixmaps/' + product.applicationName + '.png')); + .pipe(rename('usr/share/pixmaps/' + product.linuxIconName + '.png')); + + // const bash_completion = gulp.src('resources/completions/bash/code') + // .pipe(rename('usr/share/bash-completion/completions/code')); + + // const zsh_completion = gulp.src('resources/completions/zsh/_code') + // .pipe(rename('usr/share/zsh/vendor-completions/_code')); const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = 'usr/share/' + product.applicationName + '/' + p.dirname; })); @@ -84,7 +94,7 @@ function prepareDebPackage(arch) { .pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@')) .pipe(rename('DEBIAN/postinst')); - const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, icon, code); + const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, icon, /* bash_completion, zsh_completion, */ code); return all.pipe(vfs.dest(destination)); }; @@ -122,7 +132,7 @@ function prepareRpmPackage(arch) { .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', product.applicationName)) + .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) @@ -132,7 +142,13 @@ function prepareRpmPackage(arch) { .pipe(rename('usr/share/appdata/' + product.applicationName + '.appdata.xml')); const icon = gulp.src('resources/linux/code.png', { base: '.' }) - .pipe(rename('BUILD/usr/share/pixmaps/' + product.applicationName + '.png')); + .pipe(rename('BUILD/usr/share/pixmaps/' + product.linuxIconName + '.png')); + + // const bash_completion = gulp.src('resources/completions/bash/code') + // .pipe(rename('BUILD/usr/share/bash-completion/completions/code')); + + // const zsh_completion = gulp.src('resources/completions/zsh/_code') + // .pipe(rename('BUILD/usr/share/zsh/site-functions/_code')); const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = 'BUILD/usr/share/' + product.applicationName + '/' + p.dirname; })); @@ -140,6 +156,7 @@ function prepareRpmPackage(arch) { const spec = gulp.src('resources/linux/rpm/code.spec.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@ICON@@', product.linuxIconName)) .pipe(replace('@@VERSION@@', packageJson.version)) .pipe(replace('@@RELEASE@@', linuxPackageRevision)) .pipe(replace('@@ARCHITECTURE@@', rpmArch)) @@ -154,7 +171,7 @@ function prepareRpmPackage(arch) { const specIcon = gulp.src('resources/linux/rpm/code.xpm', { base: '.' }) .pipe(rename('SOURCES/' + product.applicationName + '.xpm')); - const all = es.merge(code, desktops, appdata, icon, spec, specIcon); + const all = es.merge(code, desktops, appdata, icon, /* bash_completion, zsh_completion, */ spec, specIcon); return all.pipe(vfs.dest(getRpmBuildPath(rpmArch))); }; @@ -183,31 +200,33 @@ function prepareSnapPackage(arch) { return function () { const desktop = gulp.src('resources/linux/code.desktop', { base: '.' }) + .pipe(rename(`usr/share/applications/${product.applicationName}.desktop`)); + + const desktopUrlHandler = gulp.src('resources/linux/code-url-handler.desktop', { base: '.' }) + .pipe(rename(`usr/share/applications/${product.applicationName}-url-handler.desktop`)); + + const desktops = es.merge(desktop, desktopUrlHandler) .pipe(replace('@@NAME_LONG@@', product.nameLong)) .pipe(replace('@@NAME_SHORT@@', product.nameShort)) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.applicationName}.png`)) - .pipe(rename(`usr/share/applications/${product.applicationName}.desktop`)); + .pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.linuxIconName}.png`)) + .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const icon = gulp.src('resources/linux/code.png', { base: '.' }) - .pipe(rename(`usr/share/pixmaps/${product.applicationName}.png`)); + .pipe(rename(`usr/share/pixmaps/${product.linuxIconName}.png`)); const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = `usr/share/${product.applicationName}/${p.dirname}`; })); const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', `${packageJson.version}-${linuxPackageRevision}`)) + .pipe(replace('@@VERSION@@', commit.substr(0, 8))) .pipe(rename('snap/snapcraft.yaml')); - const snapUpdate = gulp.src('resources/linux/snap/snapUpdate.sh', { base: '.' }) - .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(rename(`usr/share/${product.applicationName}/snapUpdate.sh`)); - const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' }) .pipe(rename('electron-launch')); - const all = es.merge(desktop, icon, code, snapcraft, electronLaunch, snapUpdate); + const all = es.merge(desktops, icon, code, snapcraft, electronLaunch); return all.pipe(vfs.dest(destination)); }; @@ -218,42 +237,36 @@ function buildSnapPackage(arch) { return shell.task(`cd ${snapBuildPath} && snapcraft build`); } -gulp.task('clean-vscode-linux-ia32-deb', util.rimraf('.build/linux/deb/i386')); -gulp.task('clean-vscode-linux-x64-deb', util.rimraf('.build/linux/deb/amd64')); -gulp.task('clean-vscode-linux-arm-deb', util.rimraf('.build/linux/deb/armhf')); -gulp.task('clean-vscode-linux-arm64-deb', util.rimraf('.build/linux/deb/arm64')); -gulp.task('clean-vscode-linux-ia32-rpm', util.rimraf('.build/linux/rpm/i386')); -gulp.task('clean-vscode-linux-x64-rpm', util.rimraf('.build/linux/rpm/x86_64')); -gulp.task('clean-vscode-linux-arm-rpm', util.rimraf('.build/linux/rpm/armhf')); -gulp.task('clean-vscode-linux-arm64-rpm', util.rimraf('.build/linux/rpm/arm64')); -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-arm64-snap', util.rimraf('.build/linux/snap/x64')); +const BUILD_TARGETS = [ + { arch: 'ia32' }, + { arch: 'x64' }, + { arch: 'arm' }, + { arch: 'arm64' }, +]; -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')); -gulp.task('vscode-linux-arm-prepare-deb', ['clean-vscode-linux-arm-deb'], prepareDebPackage('arm')); -gulp.task('vscode-linux-arm64-prepare-deb', ['clean-vscode-linux-arm64-deb'], prepareDebPackage('arm64')); -gulp.task('vscode-linux-ia32-build-deb', ['vscode-linux-ia32-prepare-deb'], buildDebPackage('ia32')); -gulp.task('vscode-linux-x64-build-deb', ['vscode-linux-x64-prepare-deb'], buildDebPackage('x64')); -gulp.task('vscode-linux-arm-build-deb', ['vscode-linux-arm-prepare-deb'], buildDebPackage('arm')); -gulp.task('vscode-linux-arm64-build-deb', ['vscode-linux-arm64-prepare-deb'], buildDebPackage('arm64')); +BUILD_TARGETS.forEach((buildTarget) => { + const arch = buildTarget.arch; -gulp.task('vscode-linux-ia32-prepare-rpm', ['clean-vscode-linux-ia32-rpm'], prepareRpmPackage('ia32')); -gulp.task('vscode-linux-x64-prepare-rpm', ['clean-vscode-linux-x64-rpm'], prepareRpmPackage('x64')); -gulp.task('vscode-linux-arm-prepare-rpm', ['clean-vscode-linux-arm-rpm'], prepareRpmPackage('arm')); -gulp.task('vscode-linux-arm64-prepare-rpm', ['clean-vscode-linux-arm64-rpm'], prepareRpmPackage('arm64')); -gulp.task('vscode-linux-ia32-build-rpm', ['vscode-linux-ia32-prepare-rpm'], buildRpmPackage('ia32')); -gulp.task('vscode-linux-x64-build-rpm', ['vscode-linux-x64-prepare-rpm'], buildRpmPackage('x64')); -gulp.task('vscode-linux-arm-build-rpm', ['vscode-linux-arm-prepare-rpm'], buildRpmPackage('arm')); -gulp.task('vscode-linux-arm64-build-rpm', ['vscode-linux-arm64-prepare-rpm'], buildRpmPackage('arm64')); + { + const debArch = getDebPackageArch(arch); + const prepareDebTask = task.define(`vscode-linux-${arch}-prepare-deb`, task.series(util.rimraf(`.build/linux/deb/${debArch}`), prepareDebPackage(arch))); + // gulp.task(prepareDebTask); + const buildDebTask = task.define(`vscode-linux-${arch}-build-deb`, task.series(prepareDebTask, buildDebPackage(arch))); + gulp.task(buildDebTask); + } -gulp.task('vscode-linux-ia32-prepare-snap', ['clean-vscode-linux-ia32-snap'], prepareSnapPackage('ia32')); -gulp.task('vscode-linux-x64-prepare-snap', ['clean-vscode-linux-x64-snap'], prepareSnapPackage('x64')); -gulp.task('vscode-linux-arm-prepare-snap', ['clean-vscode-linux-arm-snap'], prepareSnapPackage('arm')); -gulp.task('vscode-linux-arm64-prepare-snap', ['clean-vscode-linux-arm64-snap'], prepareSnapPackage('arm64')); -gulp.task('vscode-linux-ia32-build-snap', ['vscode-linux-ia32-prepare-snap'], buildSnapPackage('ia32')); -gulp.task('vscode-linux-x64-build-snap', ['vscode-linux-x64-prepare-snap'], buildSnapPackage('x64')); -gulp.task('vscode-linux-arm-build-snap', ['vscode-linux-arm-prepare-snap'], buildSnapPackage('arm')); -gulp.task('vscode-linux-arm64-build-snap', ['vscode-linux-arm64-prepare-snap'], buildSnapPackage('arm64')); + { + const rpmArch = getRpmPackageArch(arch); + const prepareRpmTask = task.define(`vscode-linux-${arch}-prepare-rpm`, task.series(util.rimraf(`.build/linux/rpm/${rpmArch}`), prepareRpmPackage(arch))); + // gulp.task(prepareRpmTask); + const buildRpmTask = task.define(`vscode-linux-${arch}-build-rpm`, task.series(prepareRpmTask, buildRpmPackage(arch))); + gulp.task(buildRpmTask); + } + + { + const prepareSnapTask = task.define(`vscode-linux-${arch}-prepare-snap`, task.series(util.rimraf(`.build/linux/snap/${arch}`), prepareSnapPackage(arch))); + gulp.task(prepareSnapTask); + const buildSnapTask = task.define(`vscode-linux-${arch}-build-snap`, task.series(prepareSnapTask, buildSnapPackage(arch))); + gulp.task(buildSnapTask); + } +}); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 1cfc5b4c4f7..46543316de9 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -12,6 +12,7 @@ const assert = require('assert'); const cp = require('child_process'); const _7z = require('7zip')['7z']; const util = require('./lib/util'); +const task = require('./lib/task'); const pkg = require('../package.json'); const product = require('../product.json'); const vfs = require('vinyl-fs'); @@ -105,8 +106,8 @@ function buildWin32Setup(arch, target) { } function defineWin32SetupTasks(arch, target) { - gulp.task(`clean-vscode-win32-${arch}-${target}-setup`, util.rimraf(setupDir(arch, target))); - gulp.task(`vscode-win32-${arch}-${target}-setup`, [`clean-vscode-win32-${arch}-${target}-setup`], buildWin32Setup(arch, target)); + const cleanTask = util.rimraf(setupDir(arch, target)); + gulp.task(task.define(`vscode-win32-${arch}-${target}-setup`, task.series(cleanTask, buildWin32Setup(arch, target)))); } defineWin32SetupTasks('ia32', 'system'); @@ -124,11 +125,8 @@ function archiveWin32Setup(arch) { }; } -gulp.task('clean-vscode-win32-ia32-archive', util.rimraf(zipDir('ia32'))); -gulp.task('vscode-win32-ia32-archive', ['clean-vscode-win32-ia32-archive'], archiveWin32Setup('ia32')); - -gulp.task('clean-vscode-win32-x64-archive', util.rimraf(zipDir('x64'))); -gulp.task('vscode-win32-x64-archive', ['clean-vscode-win32-x64-archive'], archiveWin32Setup('x64')); +gulp.task(task.define('vscode-win32-ia32-archive', task.series(util.rimraf(zipDir('ia32')), archiveWin32Setup('ia32')))); +gulp.task(task.define('vscode-win32-x64-archive', task.series(util.rimraf(zipDir('x64')), archiveWin32Setup('x64')))); function copyInnoUpdater(arch) { return () => { @@ -137,9 +135,6 @@ function copyInnoUpdater(arch) { }; } -gulp.task('vscode-win32-ia32-copy-inno-updater', copyInnoUpdater('ia32')); -gulp.task('vscode-win32-x64-copy-inno-updater', copyInnoUpdater('x64')); - function patchInnoUpdater(arch) { return cb => { const icon = path.join(repoPath, 'resources', 'win32', 'code.ico'); @@ -147,5 +142,5 @@ function patchInnoUpdater(arch) { }; } -gulp.task('vscode-win32-ia32-inno-updater', ['vscode-win32-ia32-copy-inno-updater'], patchInnoUpdater('ia32')); -gulp.task('vscode-win32-x64-inno-updater', ['vscode-win32-x64-copy-inno-updater'], patchInnoUpdater('x64')); \ No newline at end of file +gulp.task(task.define('vscode-win32-ia32-inno-updater', task.series(copyInnoUpdater('ia32'), patchInnoUpdater('ia32')))); +gulp.task(task.define('vscode-win32-x64-inno-updater', task.series(copyInnoUpdater('x64'), patchInnoUpdater('x64')))); \ No newline at end of file diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index c5797c78b61..a687bbccb4c 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -14,7 +14,8 @@ const es = require('event-stream'); const rename = require('gulp-rename'); const vfs = require('vinyl-fs'); const ext = require('./extensions'); -const util = require('gulp-util'); +const fancyLog = require('fancy-log'); +const ansiColors = require('ansi-colors'); const root = path.dirname(path.dirname(__dirname)); const builtInExtensions = require('../builtInExtensions.json'); @@ -43,7 +44,7 @@ function isUpToDate(extension) { function syncMarketplaceExtension(extension) { if (isUpToDate(extension)) { - util.log(util.colors.blue('[marketplace]'), `${extension.name}@${extension.version}`, util.colors.green('✔︎')); + fancyLog(ansiColors.blue('[marketplace]'), `${extension.name}@${extension.version}`, ansiColors.green('✔︎')); return es.readArray([]); } @@ -52,13 +53,13 @@ function syncMarketplaceExtension(extension) { return ext.fromMarketplace(extension.name, extension.version, extension.metadata) .pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`)) .pipe(vfs.dest('.build/builtInExtensions')) - .on('end', () => util.log(util.colors.blue('[marketplace]'), extension.name, util.colors.green('✔︎'))); + .on('end', () => fancyLog(ansiColors.blue('[marketplace]'), extension.name, ansiColors.green('✔︎'))); } function syncExtension(extension, controlState) { switch (controlState) { case 'disabled': - util.log(util.colors.blue('[disabled]'), util.colors.gray(extension.name)); + fancyLog(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name)); return es.readArray([]); case 'marketplace': @@ -66,15 +67,15 @@ function syncExtension(extension, controlState) { default: if (!fs.existsSync(controlState)) { - util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); + fancyLog(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); return es.readArray([]); } else if (!fs.existsSync(path.join(controlState, 'package.json'))) { - util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); + fancyLog(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); return es.readArray([]); } - util.log(util.colors.blue('[local]'), `${extension.name}: ${util.colors.cyan(controlState)}`, util.colors.green('✔︎')); + fancyLog(ansiColors.blue('[local]'), `${extension.name}: ${ansiColors.cyan(controlState)}`, ansiColors.green('✔︎')); return es.readArray([]); } } @@ -93,8 +94,8 @@ function writeControlFile(control) { } function main() { - util.log('Syncronizing built-in extensions...'); - util.log(`You can manage built-in extensions with the ${util.colors.cyan('--builtin')} flag`); + fancyLog('Syncronizing built-in extensions...'); + fancyLog(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); const streams = []; diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 9cc9846336a..592a5d087ce 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -16,7 +16,8 @@ const monacodts = require("../monaco/api"); const nls = require("./nls"); const reporter_1 = require("./reporter"); const util = require("./util"); -const util2 = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const watch = require('./watch'); const reporter = reporter_1.createReporter(); function getTypeScriptCompilerOptions(src) { @@ -126,6 +127,20 @@ class MonacoGenerator { this._declarationResolver.invalidateCache(moduleId); this._executeSoon(); }); + watcher.addListener('error', (err) => { + console.error(`Encountered error while watching ${filePath}.`); + console.log(err); + delete this._watchedFiles[filePath]; + for (let i = 0; i < this._watchers.length; i++) { + if (this._watchers[i] === watcher) { + this._watchers.splice(i, 1); + break; + } + } + watcher.close(); + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); + }); this._watchers.push(watcher); }; this._fsProvider = new class extends monacodts.FSProvider { @@ -165,7 +180,7 @@ class MonacoGenerator { return r; } _log(message, ...rest) { - util2.log(util2.colors.cyan('[monaco.d.ts]'), message, ...rest); + fancyLog(ansiColors.cyan('[monaco.d.ts]'), message, ...rest); } execute() { const startTime = Date.now(); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 393bc56bd16..b431a134f6c 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -17,7 +17,9 @@ import * as monacodts from '../monaco/api'; import * as nls from './nls'; import { createReporter } from './reporter'; import * as util from './util'; -import * as util2 from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; + const watch = require('./watch'); const reporter = createReporter(); @@ -159,6 +161,20 @@ class MonacoGenerator { this._declarationResolver.invalidateCache(moduleId); this._executeSoon(); }); + watcher.addListener('error', (err) => { + console.error(`Encountered error while watching ${filePath}.`); + console.log(err); + delete this._watchedFiles[filePath]; + for (let i = 0; i < this._watchers.length; i++) { + if (this._watchers[i] === watcher) { + this._watchers.splice(i, 1); + break; + } + } + watcher.close(); + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); + }); this._watchers.push(watcher); }; this._fsProvider = new class extends monacodts.FSProvider { @@ -204,7 +220,7 @@ class MonacoGenerator { } private _log(message: any, ...rest: any[]): void { - util2.log(util2.colors.cyan('[monaco.d.ts]'), message, ...rest); + fancyLog(ansiColors.cyan('[monaco.d.ts]'), message, ...rest); } public execute(): void { diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 994d1336e6b..18c668d6432 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -17,7 +17,8 @@ const remote = require("gulp-remote-src"); const vzip = require('gulp-vinyl-zip'); const filter = require("gulp-filter"); const rename = require("gulp-rename"); -const util = require('gulp-util'); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const buffer = require('gulp-buffer'); const json = require("gulp-json-editor"); const webpack = require('webpack'); @@ -79,7 +80,7 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { .pipe(packageJsonFilter.restore); const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { const webpackDone = (err, stats) => { - util.log(`Bundled extension: ${util.colors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); + fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); if (err) { result.emit('error', err); } @@ -157,7 +158,7 @@ const baseHeaders = { function fromMarketplace(extensionName, version, metadata) { const [publisher, name] = extensionName.split('.'); const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`; - util.log('Downloading extension:', util.colors.yellow(`${extensionName}@${version}`), '...'); + fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...'); const options = { base: url, requestOptions: { @@ -179,6 +180,7 @@ exports.fromMarketplace = fromMarketplace; const excludedExtensions = [ 'vscode-api-tests', 'vscode-colorize-tests', + 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', ]; diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 844b711a829..188deaf1a1f 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -17,7 +17,8 @@ import remote = require('gulp-remote-src'); const vzip = require('gulp-vinyl-zip'); import filter = require('gulp-filter'); import rename = require('gulp-rename'); -const util = require('gulp-util'); +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; const buffer = require('gulp-buffer'); import json = require('gulp-json-editor'); const webpack = require('webpack'); @@ -93,7 +94,7 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { const webpackDone = (err: any, stats: any) => { - util.log(`Bundled extension: ${util.colors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); + fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); if (err) { result.emit('error', err); } @@ -187,7 +188,7 @@ export function fromMarketplace(extensionName: string, version: string, metadata const [publisher, name] = extensionName.split('.'); const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`; - util.log('Downloading extension:', util.colors.yellow(`${extensionName}@${version}`), '...'); + fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...'); const options = { base: url, @@ -220,6 +221,7 @@ interface IPackageExtensionsOptions { const excludedExtensions = [ 'vscode-api-tests', 'vscode-colorize-tests', + 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', ]; diff --git a/build/lib/git.js b/build/lib/git.js index 29da49fdd84..da5d66fd8d2 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -17,14 +17,14 @@ function getVersion(repo) { head = fs.readFileSync(headPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } if (/^[0-9a-f]{40}$/i.test(head)) { return head; } const refMatch = /^ref: (.*)$/.exec(head); if (!refMatch) { - return void 0; + return undefined; } const ref = refMatch[1]; const refPath = path.join(git, ref); @@ -40,7 +40,7 @@ function getVersion(repo) { refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; let refsMatch; diff --git a/build/lib/git.ts b/build/lib/git.ts index 482c39e9af1..dc9c667c21b 100644 --- a/build/lib/git.ts +++ b/build/lib/git.ts @@ -18,7 +18,7 @@ export function getVersion(repo: string): string | undefined { try { head = fs.readFileSync(headPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } if (/^[0-9a-f]{40}$/i.test(head)) { @@ -28,7 +28,7 @@ export function getVersion(repo: string): string | undefined { const refMatch = /^ref: (.*)$/.exec(head); if (!refMatch) { - return void 0; + return undefined; } const ref = refMatch[1]; @@ -46,7 +46,7 @@ export function getVersion(repo: string): string | undefined { try { refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); } catch (e) { - return void 0; + return undefined; } const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm; diff --git a/build/lib/i18n.js b/build/lib/i18n.js index e026eaacb3e..c40b2988e28 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -13,15 +13,16 @@ const xml2js = require("xml2js"); const glob = require("glob"); const https = require("https"); const gulp = require("gulp"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const iconv = require("iconv-lite"); const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; function log(message, ...rest) { - util.log(util.colors.green('[i18n]'), message, ...rest); + fancyLog(ansiColors.green('[i18n]'), message, ...rest); } exports.defaultLanguages = [ - { id: 'zh-tw', folderName: 'cht', transifexId: 'zh-hant' }, - { id: 'zh-cn', folderName: 'chs', transifexId: 'zh-hans' }, + { id: 'zh-tw', folderName: 'cht', translationId: 'zh-hant' }, + { id: 'zh-cn', folderName: 'chs', translationId: 'zh-hans' }, { id: 'ja', folderName: 'jpn' }, { id: 'ko', folderName: 'kor' }, { id: 'de', folderName: 'deu' }, @@ -37,7 +38,7 @@ exports.extraLanguages = [ { id: 'tr', folderName: 'trk' } ]; // non built-in extensions also that are transifex and need to be part of the language packs -const externalExtensionsWithTranslations = { +exports.externalExtensionsWithTranslations = { 'vscode-chrome-debug': 'msjsdiag.debugger-for-chrome', 'vscode-node-debug': 'ms-vscode.node-debug', 'vscode-node-debug2': 'ms-vscode.node-debug2' @@ -229,12 +230,15 @@ XLF.parse = function (xlfString) { if (!unit.target) { return; // No translation available } - const val = unit.target.toString(); + let val = unit.target[0]; + if (typeof val !== 'string') { + val = val._; + } if (key && val) { messages[key] = decodeEntities(val); } else { - reject(new Error(`XLF parsing error: XLIFF file does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.`)); + reject(new Error(`XLF parsing error: XLIFF file ${originalFilePath} does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.`)); } }); files.push({ messages: messages, originalFilePath: originalFilePath, language: language.toLowerCase() }); @@ -368,7 +372,11 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { } }); }); - let languageDirectory = path.join(__dirname, '..', '..', 'i18n'); + let languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); + if (!fs.existsSync(languageDirectory)) { + log(`No VS Code localization repository found. Looking at ${languageDirectory}`); + log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); + } let sortedLanguages = sortLanguages(languages); sortedLanguages.forEach((language) => { if (process.env['VSCODE_BUILD_VERBOSE']) { @@ -376,22 +384,25 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { } statistics[language.id] = 0; let localizedModules = Object.create(null); - let languageFolderName = language.folderName || language.id; - let cwd = path.join(languageDirectory, languageFolderName, 'src'); + let languageFolderName = language.translationId || language.id; + let i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); + let allMessages; + if (fs.existsSync(i18nFile)) { + let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + allMessages = JSON.parse(content); + } modules.forEach((module) => { let order = keysSection[module]; - let i18nFile = path.join(cwd, module) + '.i18n.json'; - let messages = null; - if (fs.existsSync(i18nFile)) { - let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); - messages = JSON.parse(content); + let moduleMessage; + if (allMessages) { + moduleMessage = allMessages.contents[module]; } - else { + if (!moduleMessage) { if (process.env['VSCODE_BUILD_VERBOSE']) { log(`No localized messages found for module ${module}. Using default messages.`); } - messages = defaultMessages[module]; - statistics[language.id] = statistics[language.id] + Object.keys(messages).length; + moduleMessage = defaultMessages[module]; + statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length; } let localizedMessages = []; order.forEach((keyInfo) => { @@ -402,7 +413,7 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { else { key = keyInfo.key; } - let message = messages[key]; + let message = moduleMessage[key]; if (!message) { if (process.env['VSCODE_BUILD_VERBOSE']) { log(`No localized message found for key ${key} in module ${module}. Using default message.`); @@ -485,7 +496,7 @@ function getResource(sourceFile) { else if (/^vs\/code/.test(sourceFile)) { return { name: 'vs/code', project: workbenchProject }; } - else if (/^vs\/workbench\/parts/.test(sourceFile)) { + else if (/^vs\/workbench\/contrib/.test(sourceFile)) { resource = sourceFile.split('/', 4).join('/'); return { name: resource, project: workbenchProject }; } @@ -569,7 +580,7 @@ function createXlfFilesForExtensions() { } return _xlf; } - gulp.src([`./extensions/${extensionName}/package.nls.json`, `./extensions/${extensionName}/**/nls.metadata.json`]).pipe(event_stream_1.through(function (file) { + gulp.src([`./extensions/${extensionName}/package.nls.json`, `./extensions/${extensionName}/**/nls.metadata.json`], { allowEmpty: true }).pipe(event_stream_1.through(function (file) { if (file.isBuffer()) { const buffer = file.contents; const basename = path.basename(file.path); @@ -743,7 +754,7 @@ function getAllResources(project, apiHostname, username, password) { } function findObsoleteResources(apiHostname, username, password) { let resourcesByProject = Object.create(null); - resourcesByProject[extensionsProject] = [].concat(externalExtensionsWithTranslations); // clone + resourcesByProject[extensionsProject] = [].concat(exports.externalExtensionsWithTranslations); // clone return event_stream_1.through(function (file) { const project = path.dirname(file.relative); const fileName = path.basename(file.path); @@ -946,7 +957,7 @@ function retrieveResource(language, resource, apiHostname, credentials) { return limiter.queue(() => new Promise((resolve, reject) => { const slug = resource.name.replace(/\//g, '_'); const project = resource.project; - let transifexLanguageId = language.id === 'ps' ? 'en' : language.transifexId || language.id; + let transifexLanguageId = language.id === 'ps' ? 'en' : language.translationId || language.id; const options = { hostname: apiHostname, path: `/api/2/project/${project}/resource/${slug}/translation/${transifexLanguageId}?file&mode=onlyreviewed`, @@ -1019,17 +1030,18 @@ function createI18nFile(originalFilePath, messages) { } const i18nPackVersion = "1.0.0"; function pullI18nPackFiles(apiHostname, username, password, language, resultingTranslationPaths) { - return pullCoreAndExtensionsXlfFiles(apiHostname, username, password, language, externalExtensionsWithTranslations) - .pipe(prepareI18nPackFiles(externalExtensionsWithTranslations, resultingTranslationPaths, language.id === 'ps')); + return pullCoreAndExtensionsXlfFiles(apiHostname, username, password, language, exports.externalExtensionsWithTranslations) + .pipe(prepareI18nPackFiles(exports.externalExtensionsWithTranslations, resultingTranslationPaths, language.id === 'ps')); } exports.pullI18nPackFiles = pullI18nPackFiles; function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pseudo = false) { let parsePromises = []; let mainPack = { version: i18nPackVersion, contents: {} }; let extensionsPacks = {}; + let errors = []; return event_stream_1.through(function (xlf) { - let project = path.dirname(xlf.path); - let resource = path.basename(xlf.path, '.xlf'); + let project = path.basename(path.dirname(xlf.relative)); + let resource = path.basename(xlf.relative, '.xlf'); let contents = xlf.contents.toString(); let parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents); parsePromises.push(parsePromise); @@ -1055,10 +1067,15 @@ function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pse mainPack.contents[path.substr(firstSlash + 1)] = file.messages; } }); + }).catch(reason => { + errors.push(reason); }); }, function () { Promise.all(parsePromises) .then(() => { + if (errors.length > 0) { + throw errors; + } const translatedMainFile = createI18nFile('./main', mainPack); resultingTranslationPaths.push({ id: 'vscode', resourceName: 'main.i18n.json' }); this.queue(translatedMainFile); @@ -1075,7 +1092,9 @@ function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pse } this.queue(null); }) - .catch(reason => { throw new Error(reason); }); + .catch((reason) => { + this.emit('error', reason); + }); }); } exports.prepareI18nPackFiles = prepareI18nPackFiles; @@ -1093,11 +1112,15 @@ function prepareIslFiles(language, innoSetupConfig) { let translatedFile = createIslFile(file.originalFilePath, file.messages, language, innoSetupConfig); stream.queue(translatedFile); }); + }).catch(reason => { + this.emit('error', reason); }); }, function () { Promise.all(parsePromises) .then(() => { this.queue(null); }) - .catch(reason => { throw new Error(reason); }); + .catch(reason => { + this.emit('error', reason); + }); }); } exports.prepareIslFiles = prepareIslFiles; diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 296d1286fbb..ac557db625e 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -27,135 +27,155 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/cli", + "name": "vs/workbench/api/common", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/codeEditor", + "name": "vs/workbench/contrib/cli", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/comments", + "name": "vs/workbench/contrib/codeEditor", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/debug", + "name": "vs/workbench/contrib/codeinset", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/emmet", + "name": "vs/workbench/contrib/callHierarchy", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/execution", + "name": "vs/workbench/contrib/comments", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/extensions", + "name": "vs/workbench/contrib/debug", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/feedback", + "name": "vs/workbench/contrib/emmet", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/files", + "name": "vs/workbench/contrib/extensions", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/html", + "name": "vs/workbench/contrib/externalTerminal", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/markers", + "name": "vs/workbench/contrib/feedback", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/localizations", + "name": "vs/workbench/contrib/files", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/logs", + "name": "vs/workbench/contrib/html", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/output", + "name": "vs/workbench/contrib/issue", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/performance", + "name": "vs/workbench/contrib/markers", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/preferences", + "name": "vs/workbench/contrib/localizations", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/quickopen", + "name": "vs/workbench/contrib/logs", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/relauncher", + "name": "vs/workbench/contrib/output", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/scm", + "name": "vs/workbench/contrib/performance", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/search", + "name": "vs/workbench/contrib/preferences", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/snippets", + "name": "vs/workbench/contrib/quickopen", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/stats", + "name": "vs/workbench/contrib/relauncher", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/surveys", + "name": "vs/workbench/contrib/scm", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/tasks", + "name": "vs/workbench/contrib/search", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/terminal", + "name": "vs/workbench/contrib/snippets", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/themes", + "name": "vs/workbench/contrib/format", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/trust", + "name": "vs/workbench/contrib/stats", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/update", + "name": "vs/workbench/contrib/surveys", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/url", + "name": "vs/workbench/contrib/tasks", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/watermark", + "name": "vs/workbench/contrib/terminal", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/webview", + "name": "vs/workbench/contrib/themes", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/welcome", + "name": "vs/workbench/contrib/trust", "project": "vscode-workbench" }, { - "name": "vs/workbench/parts/outline", + "name": "vs/workbench/contrib/update", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/url", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/watermark", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/webview", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/welcome", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/outline", "project": "vscode-workbench" }, { @@ -195,13 +215,17 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/services/jsonschemas", + "name": "vs/workbench/services/extensionManagement", "project": "vscode-workbench" }, { "name": "vs/workbench/services/files", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/integrity", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/keybinding", "project": "vscode-workbench" @@ -238,6 +262,10 @@ "name": "vs/workbench/services/decorations", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/label", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/preferences", "project": "vscode-preferences" diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 26e512d95b6..ee75ac1ec01 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -13,19 +13,19 @@ import * as xml2js from 'xml2js'; import * as glob from 'glob'; import * as https from 'https'; import * as gulp from 'gulp'; - -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as iconv from 'iconv-lite'; const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; function log(message: any, ...rest: any[]): void { - util.log(util.colors.green('[i18n]'), message, ...rest); + fancyLog(ansiColors.green('[i18n]'), message, ...rest); } export interface Language { - id: string; // laguage id, e.g. zh-tw, de - transifexId?: string; // language id used in transifex, e.g zh-hant, de (optional, if not set, the id is used) + id: string; // language id, e.g. zh-tw, de + translationId?: string; // language id used in translation tools, e.g zh-hant, de (optional, if not set, the id is used) folderName?: string; // language specific folder name, e.g. cht, deu (optional, if not set, the id is used) } @@ -38,8 +38,8 @@ export interface InnoSetup { } export const defaultLanguages: Language[] = [ - { id: 'zh-tw', folderName: 'cht', transifexId: 'zh-hant' }, - { id: 'zh-cn', folderName: 'chs', transifexId: 'zh-hans' }, + { id: 'zh-tw', folderName: 'cht', translationId: 'zh-hant' }, + { id: 'zh-cn', folderName: 'chs', translationId: 'zh-hans' }, { id: 'ja', folderName: 'jpn' }, { id: 'ko', folderName: 'kor' }, { id: 'de', folderName: 'deu' }, @@ -57,7 +57,7 @@ export const extraLanguages: Language[] = [ ]; // non built-in extensions also that are transifex and need to be part of the language packs -const externalExtensionsWithTranslations = { +export const externalExtensionsWithTranslations = { 'vscode-chrome-debug': 'msjsdiag.debugger-for-chrome', 'vscode-node-debug': 'ms-vscode.node-debug', 'vscode-node-debug2': 'ms-vscode.node-debug2' @@ -144,6 +144,15 @@ interface BundledExtensionFormat { }; } +interface I18nFormat { + version: string; + contents: { + [module: string]: { + [messageKey: string]: string; + }; + }; +} + export class Line { private buffer: string[] = []; @@ -325,11 +334,14 @@ export class XLF { return; // No translation available } - const val = unit.target.toString(); + let val = unit.target[0]; + if (typeof val !== 'string') { + val = val._; + } if (key && val) { messages[key] = decodeEntities(val); } else { - reject(new Error(`XLF parsing error: XLIFF file does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.`)); + reject(new Error(`XLF parsing error: XLIFF file ${originalFilePath} does not contain full localization data. ID or target translation for one of the trans-unit nodes is not present.`)); } }); files.push({ messages: messages, originalFilePath: originalFilePath, language: language.toLowerCase() }); @@ -483,7 +495,11 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json }); }); - let languageDirectory = path.join(__dirname, '..', '..', 'i18n'); + let languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); + if (!fs.existsSync(languageDirectory)) { + log(`No VS Code localization repository found. Looking at ${languageDirectory}`); + log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); + } let sortedLanguages = sortLanguages(languages); sortedLanguages.forEach((language) => { if (process.env['VSCODE_BUILD_VERBOSE']) { @@ -492,21 +508,25 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json statistics[language.id] = 0; let localizedModules: Map = Object.create(null); - let languageFolderName = language.folderName || language.id; - let cwd = path.join(languageDirectory, languageFolderName, 'src'); + let languageFolderName = language.translationId || language.id; + let i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); + let allMessages: I18nFormat | undefined; + if (fs.existsSync(i18nFile)) { + let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + allMessages = JSON.parse(content); + } modules.forEach((module) => { let order = keysSection[module]; - let i18nFile = path.join(cwd, module) + '.i18n.json'; - let messages: Map | null = null; - if (fs.existsSync(i18nFile)) { - let content = stripComments(fs.readFileSync(i18nFile, 'utf8')); - messages = JSON.parse(content); - } else { + let moduleMessage: { [messageKey: string]: string } | undefined; + if (allMessages) { + moduleMessage = allMessages.contents[module]; + } + if (!moduleMessage) { if (process.env['VSCODE_BUILD_VERBOSE']) { log(`No localized messages found for module ${module}. Using default messages.`); } - messages = defaultMessages[module]; - statistics[language.id] = statistics[language.id] + Object.keys(messages).length; + moduleMessage = defaultMessages[module]; + statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length; } let localizedMessages: string[] = []; order.forEach((keyInfo) => { @@ -516,7 +536,7 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json } else { key = keyInfo.key; } - let message: string = messages![key]; + let message: string = moduleMessage![key]; if (!message) { if (process.env['VSCODE_BUILD_VERBOSE']) { log(`No localized message found for key ${key} in module ${module}. Using default message.`); @@ -599,7 +619,7 @@ export function getResource(sourceFile: string): Resource { return { name: 'vs/base', project: editorProject }; } else if (/^vs\/code/.test(sourceFile)) { return { name: 'vs/code', project: workbenchProject }; - } else if (/^vs\/workbench\/parts/.test(sourceFile)) { + } else if (/^vs\/workbench\/contrib/.test(sourceFile)) { resource = sourceFile.split('/', 4).join('/'); return { name: resource, project: workbenchProject }; } else if (/^vs\/workbench\/services/.test(sourceFile)) { @@ -681,7 +701,7 @@ export function createXlfFilesForExtensions(): ThroughStream { } return _xlf; } - gulp.src([`./extensions/${extensionName}/package.nls.json`, `./extensions/${extensionName}/**/nls.metadata.json`]).pipe(through(function (file: File) { + gulp.src([`./extensions/${extensionName}/package.nls.json`, `./extensions/${extensionName}/**/nls.metadata.json`], { allowEmpty: true }).pipe(through(function (file: File) { if (file.isBuffer()) { const buffer: Buffer = file.contents as Buffer; const basename = path.basename(file.path); @@ -1082,7 +1102,7 @@ function retrieveResource(language: Language, resource: Resource, apiHostname: s return limiter.queue(() => new Promise((resolve, reject) => { const slug = resource.name.replace(/\//g, '_'); const project = resource.project; - let transifexLanguageId = language.id === 'ps' ? 'en' : language.transifexId || language.id; + let transifexLanguageId = language.id === 'ps' ? 'en' : language.translationId || language.id; const options = { hostname: apiHostname, path: `/api/2/project/${project}/resource/${slug}/translation/${transifexLanguageId}?file&mode=onlyreviewed`, @@ -1181,9 +1201,10 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT let parsePromises: Promise[] = []; let mainPack: I18nPack = { version: i18nPackVersion, contents: {} }; let extensionsPacks: Map = {}; + let errors: any[] = []; return through(function (this: ThroughStream, xlf: File) { - let project = path.dirname(xlf.path); - let resource = path.basename(xlf.path, '.xlf'); + let project = path.basename(path.dirname(xlf.relative)); + let resource = path.basename(xlf.relative, '.xlf'); let contents = xlf.contents.toString(); let parsePromise = pseudo ? XLF.parsePseudo(contents) : XLF.parse(contents); parsePromises.push(parsePromise); @@ -1210,10 +1231,15 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT } }); } - ); + ).catch(reason => { + errors.push(reason); + }); }, function () { Promise.all(parsePromises) .then(() => { + if (errors.length > 0) { + throw errors; + } const translatedMainFile = createI18nFile('./main', mainPack); resultingTranslationPaths.push({ id: 'vscode', resourceName: 'main.i18n.json' }); @@ -1232,7 +1258,9 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT } this.queue(null); }) - .catch(reason => { throw new Error(reason); }); + .catch((reason) => { + this.emit('error', reason); + }); }); } @@ -1253,11 +1281,15 @@ export function prepareIslFiles(language: Language, innoSetupConfig: InnoSetup): stream.queue(translatedFile); }); } - ); + ).catch(reason => { + this.emit('error', reason); + }); }, function () { Promise.all(parsePromises) .then(() => { this.queue(null); }) - .catch(reason => { throw new Error(reason); }); + .catch(reason => { + this.emit('error', reason); + }); }); } @@ -1338,4 +1370,4 @@ function decodeEntities(value: string): string { function pseudify(message: string) { return '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; -} \ No newline at end of file +} diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 07cdebe30ee..e4783e18569 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -13,7 +13,8 @@ const flatmap = require("gulp-flatmap"); const sourcemaps = require("gulp-sourcemaps"); const uglify = require("gulp-uglify"); const composer = require("gulp-uglify/composer"); -const gulpUtil = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const path = require("path"); const pump = require("pump"); const uglifyes = require("uglify-es"); @@ -24,7 +25,7 @@ const stats_1 = require("./stats"); const util = require("./util"); const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix, message) { - gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); + fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } function loaderConfig(emptyPaths) { const result = { @@ -113,7 +114,6 @@ function toBundleStream(src, bundledFileHeader, bundles) { function optimizeTask(opts) { const src = opts.src; const entryPoints = opts.entryPoints; - const otherSources = opts.otherSources; const resources = opts.resources; const loaderConfig = opts.loaderConfig; const bundledFileHeader = opts.header; @@ -136,7 +136,7 @@ function optimizeTask(opts) { } filteredResources.push('!' + resource); }); - gulp.src(filteredResources, { base: `${src}` }).pipe(resourcesStream); + gulp.src(filteredResources, { base: `${src}`, allowEmpty: true }).pipe(resourcesStream); const bundleInfoArray = []; if (opts.bundleInfo) { bundleInfoArray.push(new VinylFile({ @@ -147,20 +147,7 @@ function optimizeTask(opts) { } es.readArray(bundleInfoArray).pipe(bundleInfoStream); }); - const otherSourcesStream = es.through(); - const otherSourcesStreamArr = []; - gulp.src(otherSources, { base: `${src}` }) - .pipe(es.through(function (data) { - otherSourcesStreamArr.push(toConcatStream(src, bundledFileHeader, [data], data.relative)); - }, function () { - if (!otherSourcesStreamArr.length) { - setTimeout(function () { otherSourcesStream.emit('end'); }, 0); - } - else { - es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); - } - })); - const result = es.merge(loader(src, bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); + const result = es.merge(loader(src, bundledFileHeader, bundleLoader), bundlesStream, resourcesStream, bundleInfoStream); return result .pipe(sourcemaps.write('./', { sourceRoot: undefined, @@ -223,7 +210,12 @@ function minifyTask(src, sourceMapBaseUrl) { return cb => { const jsFilter = filter('**/*.js', { restore: true }); const cssFilter = filter('**/*.css', { restore: true }); - pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.write('./', { + pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.mapSources((sourcePath) => { + if (sourcePath === 'bootstrap-fork.js') { + return 'bootstrap-fork.orig.js'; + } + return sourcePath; + }), sourcemaps.write('./', { sourceMappingURL, sourceRoot: undefined, includeContent: true, diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 85d15b23543..d15659ca6ea 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -14,7 +14,8 @@ import * as flatmap from 'gulp-flatmap'; import * as sourcemaps from 'gulp-sourcemaps'; import * as uglify from 'gulp-uglify'; import * as composer from 'gulp-uglify/composer'; -import * as gulpUtil from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as path from 'path'; import * as pump from 'pump'; import * as sm from 'source-map'; @@ -28,7 +29,7 @@ import * as util from './util'; const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix: string, message: string): void { - gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); + fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } export function loaderConfig(emptyPaths?: string[]) { @@ -141,10 +142,6 @@ export interface IOptimizeTaskOpts { * (for AMD files, will get bundled and get Copyright treatment) */ entryPoints: bundle.IEntryPoint[]; - /** - * (for non-AMD files that should get Copyright treatment) - */ - otherSources: string[]; /** * (svg, etc.) */ @@ -175,7 +172,6 @@ export interface IOptimizeTaskOpts { export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream { const src = opts.src; const entryPoints = opts.entryPoints; - const otherSources = opts.otherSources; const resources = opts.resources; const loaderConfig = opts.loaderConfig; const bundledFileHeader = opts.header; @@ -200,7 +196,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr } filteredResources.push('!' + resource); }); - gulp.src(filteredResources, { base: `${src}` }).pipe(resourcesStream); + gulp.src(filteredResources, { base: `${src}`, allowEmpty: true }).pipe(resourcesStream); const bundleInfoArray: VinylFile[] = []; if (opts.bundleInfo) { @@ -213,24 +209,9 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr es.readArray(bundleInfoArray).pipe(bundleInfoStream); }); - const otherSourcesStream = es.through(); - const otherSourcesStreamArr: NodeJS.ReadWriteStream[] = []; - - gulp.src(otherSources, { base: `${src}` }) - .pipe(es.through(function (data) { - otherSourcesStreamArr.push(toConcatStream(src, bundledFileHeader, [data], data.relative)); - }, function () { - if (!otherSourcesStreamArr.length) { - setTimeout(function () { otherSourcesStream.emit('end'); }, 0); - } else { - es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); - } - })); - const result = es.merge( loader(src, bundledFileHeader, bundleLoader), bundlesStream, - otherSourcesStream, resourcesStream, bundleInfoStream ); @@ -317,6 +298,13 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, + (sourcemaps).mapSources((sourcePath: string) => { + if (sourcePath === 'bootstrap-fork.js') { + return 'bootstrap-fork.orig.js'; + } + + return sourcePath; + }), sourcemaps.write('./', { sourceMappingURL, sourceRoot: undefined, diff --git a/build/lib/reporter.js b/build/lib/reporter.js index 598aabec244..e0461dc6d9d 100644 --- a/build/lib/reporter.js +++ b/build/lib/reporter.js @@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); const es = require("event-stream"); const _ = require("underscore"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const fs = require("fs"); const path = require("path"); const allErrors = []; @@ -17,7 +18,7 @@ function onStart() { return; } startTime = new Date().getTime(); - util.log(`Starting ${util.colors.green('compilation')}...`); + fancyLog(`Starting ${ansiColors.green('compilation')}...`); } function onEnd() { if (--count > 0) { @@ -38,7 +39,7 @@ function log() { errors.map(err => { if (!seen.has(err)) { seen.add(err); - util.log(`${util.colors.red('Error')}: ${err}`); + fancyLog(`${ansiColors.red('Error')}: ${err}`); } }); const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; @@ -53,7 +54,7 @@ function log() { catch (err) { //noop } - util.log(`Finished ${util.colors.green('compilation')} with ${errors.length} errors after ${util.colors.magenta((new Date().getTime() - startTime) + ' ms')}`); + fancyLog(`Finished ${ansiColors.green('compilation')} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - startTime) + ' ms')}`); } function createReporter() { const errors = []; diff --git a/build/lib/reporter.ts b/build/lib/reporter.ts index 68e037e9224..ec908817518 100644 --- a/build/lib/reporter.ts +++ b/build/lib/reporter.ts @@ -7,7 +7,8 @@ import * as es from 'event-stream'; import * as _ from 'underscore'; -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as fs from 'fs'; import * as path from 'path'; @@ -21,7 +22,7 @@ function onStart(): void { } startTime = new Date().getTime(); - util.log(`Starting ${util.colors.green('compilation')}...`); + fancyLog(`Starting ${ansiColors.green('compilation')}...`); } function onEnd(): void { @@ -47,7 +48,7 @@ function log(): void { errors.map(err => { if (!seen.has(err)) { seen.add(err); - util.log(`${util.colors.red('Error')}: ${err}`); + fancyLog(`${ansiColors.red('Error')}: ${err}`); } }); @@ -65,7 +66,7 @@ function log(): void { //noop } - util.log(`Finished ${util.colors.green('compilation')} with ${errors.length} errors after ${util.colors.magenta((new Date().getTime() - startTime!) + ' ms')}`); + fancyLog(`Finished ${ansiColors.green('compilation')} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - startTime!) + ' ms')}`); } export interface IReporter { diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 121a5e19220..da5987963b6 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -27,7 +27,7 @@ function writeFile(filePath, contents) { fs.writeFileSync(filePath, contents); } function extractEditor(options) { - const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); + const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString()); let compilerOptions; if (tsConfig.extends) { compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); @@ -36,13 +36,12 @@ function extractEditor(options) { compilerOptions = tsConfig.compilerOptions; } tsConfig.compilerOptions = compilerOptions; + compilerOptions.noEmit = false; compilerOptions.noUnusedLocals = false; compilerOptions.preserveConstEnums = false; compilerOptions.declaration = false; + compilerOptions.noImplicitAny = false; compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; - delete compilerOptions.types; - delete tsConfig.extends; - tsConfig.exclude = []; options.compilerOptions = compilerOptions; let result = tss.shake(options); for (let fileName in result) { @@ -92,6 +91,8 @@ function extractEditor(options) { } delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); + const tsConfigBase = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.base.json')).toString()); + writeOutputFile('tsconfig.base.json', JSON.stringify(tsConfigBase, null, '\t')); [ 'vs/css.build.js', 'vs/css.d.ts', @@ -119,8 +120,7 @@ function createESMSourcesAndResources2(options) { return path.join(OUT_RESOURCES_FOLDER, dest); }; const allFiles = walkDirRecursive(SRC_FOLDER); - for (let i = 0; i < allFiles.length; i++) { - const file = allFiles[i]; + for (const file of allFiles) { if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { continue; } diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 6ec0aa1c958..79cdeeb455c 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -31,7 +31,7 @@ function writeFile(filePath: string, contents: Buffer | string): void { } export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void { - const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); + const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString()); let compilerOptions: { [key: string]: any }; if (tsConfig.extends) { compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); @@ -40,14 +40,13 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str } tsConfig.compilerOptions = compilerOptions; + compilerOptions.noEmit = false; compilerOptions.noUnusedLocals = false; compilerOptions.preserveConstEnums = false; compilerOptions.declaration = false; + compilerOptions.noImplicitAny = false; compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; - delete compilerOptions.types; - delete tsConfig.extends; - tsConfig.exclude = []; options.compilerOptions = compilerOptions; @@ -101,6 +100,8 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); + const tsConfigBase = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.base.json')).toString()); + writeOutputFile('tsconfig.base.json', JSON.stringify(tsConfigBase, null, '\t')); [ 'vs/css.build.js', @@ -139,8 +140,7 @@ export function createESMSourcesAndResources2(options: IOptions2): void { }; const allFiles = walkDirRecursive(SRC_FOLDER); - for (let i = 0; i < allFiles.length; i++) { - const file = allFiles[i]; + for (const file of allFiles) { if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) { continue; @@ -244,7 +244,6 @@ export function createESMSourcesAndResources2(options: IOptions2): void { 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; diff --git a/build/lib/stats.js b/build/lib/stats.js index c08cd57b3bc..99ad665f223 100644 --- a/build/lib/stats.js +++ b/build/lib/stats.js @@ -5,7 +5,8 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const es = require("event-stream"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const appInsights = require("applicationinsights"); class Entry { constructor(name, totalCount, totalSize) { @@ -24,13 +25,13 @@ class Entry { } else { if (this.totalCount === 1) { - return `Stats for '${util.colors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; + return `Stats for '${ansiColors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; } else { const count = this.totalCount < 100 - ? util.colors.green(this.totalCount.toString()) - : util.colors.red(this.totalCount.toString()); - return `Stats for '${util.colors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; + ? ansiColors.green(this.totalCount.toString()) + : ansiColors.red(this.totalCount.toString()); + return `Stats for '${ansiColors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; } } } @@ -57,13 +58,13 @@ function createStatsStream(group, log) { }, function () { if (log) { if (entry.totalCount === 1) { - util.log(`Stats for '${util.colors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); } else { const count = entry.totalCount < 100 - ? util.colors.green(entry.totalCount.toString()) - : util.colors.red(entry.totalCount.toString()); - util.log(`Stats for '${util.colors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); + ? ansiColors.green(entry.totalCount.toString()) + : ansiColors.red(entry.totalCount.toString()); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); } } this.emit('end'); diff --git a/build/lib/stats.ts b/build/lib/stats.ts index 7f5548dc1c4..a94b1c9ae1a 100644 --- a/build/lib/stats.ts +++ b/build/lib/stats.ts @@ -6,7 +6,8 @@ 'use strict'; import * as es from 'event-stream'; -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as File from 'vinyl'; import * as appInsights from 'applicationinsights'; @@ -22,14 +23,14 @@ class Entry { } } else { if (this.totalCount === 1) { - return `Stats for '${util.colors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; + return `Stats for '${ansiColors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; } else { const count = this.totalCount < 100 - ? util.colors.green(this.totalCount.toString()) - : util.colors.red(this.totalCount.toString()); + ? ansiColors.green(this.totalCount.toString()) + : ansiColors.red(this.totalCount.toString()); - return `Stats for '${util.colors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; + return `Stats for '${ansiColors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; } } } @@ -58,14 +59,14 @@ export function createStatsStream(group: string, log?: boolean): es.ThroughStrea }, function () { if (log) { if (entry.totalCount === 1) { - util.log(`Stats for '${util.colors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); } else { const count = entry.totalCount < 100 - ? util.colors.green(entry.totalCount.toString()) - : util.colors.red(entry.totalCount.toString()); + ? ansiColors.green(entry.totalCount.toString()) + : ansiColors.red(entry.totalCount.toString()); - util.log(`Stats for '${util.colors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); } } diff --git a/build/lib/task.js b/build/lib/task.js new file mode 100644 index 00000000000..f1e6e3f6245 --- /dev/null +++ b/build/lib/task.js @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); +function _isPromise(p) { + if (typeof p.then === 'function') { + return true; + } + return false; +} +function _renderTime(time) { + return `${Math.round(time)} ms`; +} +async function _execute(task) { + const name = task.taskName || task.displayName || ``; + if (!task._tasks) { + fancyLog('Starting', ansiColors.cyan(name), '...'); + } + const startTime = process.hrtime(); + await _doExecute(task); + const elapsedArr = process.hrtime(startTime); + const elapsedNanoseconds = (elapsedArr[0] * 1e9 + elapsedArr[1]); + if (!task._tasks) { + fancyLog(`Finished`, ansiColors.cyan(name), 'after', ansiColors.magenta(_renderTime(elapsedNanoseconds / 1e6))); + } +} +async function _doExecute(task) { + // Always invoke as if it were a callback task + return new Promise((resolve, reject) => { + if (task.length === 1) { + // this is a callback task + task((err) => { + if (err) { + return reject(err); + } + resolve(); + }); + return; + } + const taskResult = task(); + if (typeof taskResult === 'undefined') { + // this is a sync task + resolve(); + return; + } + if (_isPromise(taskResult)) { + // this is a promise returning task + taskResult.then(resolve, reject); + return; + } + // this is a stream returning task + taskResult.on('end', _ => resolve()); + taskResult.on('error', err => reject(err)); + }); +} +function series(...tasks) { + const result = async () => { + for (let i = 0; i < tasks.length; i++) { + await _execute(tasks[i]); + } + }; + result._tasks = tasks; + return result; +} +exports.series = series; +function parallel(...tasks) { + const result = async () => { + await Promise.all(tasks.map(t => _execute(t))); + }; + result._tasks = tasks; + return result; +} +exports.parallel = parallel; +function define(name, task) { + if (task._tasks) { + // This is a composite task + const lastTask = task._tasks[task._tasks.length - 1]; + if (lastTask._tasks || lastTask.taskName) { + // This is a composite task without a real task function + // => generate a fake task function + return define(name, series(task, () => Promise.resolve())); + } + lastTask.taskName = name; + task.displayName = name; + return task; + } + // This is a simple task + task.taskName = name; + task.displayName = name; + return task; +} +exports.define = define; diff --git a/build/lib/task.ts b/build/lib/task.ts new file mode 100644 index 00000000000..4e571ec9294 --- /dev/null +++ b/build/lib/task.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * 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 fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; + +export interface BaseTask { + displayName?: string; + taskName?: string; + _tasks?: Task[]; +} +export interface PromiseTask extends BaseTask { + (): Promise; +} +export interface StreamTask extends BaseTask { + (): NodeJS.ReadWriteStream; +} +export interface CallbackTask extends BaseTask { + (cb?: (err?: any) => void): void; +} + +export type Task = PromiseTask | StreamTask | CallbackTask; + +function _isPromise(p: Promise | NodeJS.ReadWriteStream): p is Promise { + if (typeof (p).then === 'function') { + return true; + } + return false; +} + +function _renderTime(time: number): string { + return `${Math.round(time)} ms`; +} + +async function _execute(task: Task): Promise { + const name = task.taskName || task.displayName || ``; + if (!task._tasks) { + fancyLog('Starting', ansiColors.cyan(name), '...'); + } + const startTime = process.hrtime(); + await _doExecute(task); + const elapsedArr = process.hrtime(startTime); + const elapsedNanoseconds = (elapsedArr[0] * 1e9 + elapsedArr[1]); + if (!task._tasks) { + fancyLog(`Finished`, ansiColors.cyan(name), 'after', ansiColors.magenta(_renderTime(elapsedNanoseconds / 1e6))); + } +} + +async function _doExecute(task: Task): Promise { + // Always invoke as if it were a callback task + return new Promise((resolve, reject) => { + if (task.length === 1) { + // this is a callback task + task((err) => { + if (err) { + return reject(err); + } + resolve(); + }); + return; + } + + const taskResult = task(); + + if (typeof taskResult === 'undefined') { + // this is a sync task + resolve(); + return; + } + + if (_isPromise(taskResult)) { + // this is a promise returning task + taskResult.then(resolve, reject); + return; + } + + // this is a stream returning task + taskResult.on('end', _ => resolve()); + taskResult.on('error', err => reject(err)); + }); +} + +export function series(...tasks: Task[]): PromiseTask { + const result = async () => { + for (let i = 0; i < tasks.length; i++) { + await _execute(tasks[i]); + } + }; + result._tasks = tasks; + return result; +} + +export function parallel(...tasks: Task[]): PromiseTask { + const result = async () => { + await Promise.all(tasks.map(t => _execute(t))); + }; + result._tasks = tasks; + return result; +} + +export function define(name: string, task: Task): Task { + if (task._tasks) { + // This is a composite task + const lastTask = task._tasks[task._tasks.length - 1]; + + if (lastTask._tasks || lastTask.taskName) { + // This is a composite task without a real task function + // => generate a fake task function + return define(name, series(task, () => Promise.resolve())); + } + + lastTask.taskName = name; + task.displayName = name; + return task; + } + + // This is a simple task + task.taskName = name; + task.displayName = name; + return task; +} diff --git a/build/lib/test/i18n.test.js b/build/lib/test/i18n.test.js index 8044a5eb711..298104865ab 100644 --- a/build/lib/test/i18n.test.js +++ b/build/lib/test/i18n.test.js @@ -27,13 +27,13 @@ suite('XLF Parser Tests', () => { }); test('JSON file source path to Transifex resource match', () => { const editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench'; - const platform = { name: 'vs/platform', project: editorProject }, editorContrib = { name: 'vs/editor/contrib', project: editorProject }, editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/parts/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/files', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject }; + const platform = { name: 'vs/platform', project: editorProject }, editorContrib = { name: 'vs/editor/contrib', project: editorProject }, editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/contrib/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/files', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject }; assert.deepEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); assert.deepEqual(i18n.getResource('vs/editor/contrib/clipboard/browser/clipboard'), editorContrib); assert.deepEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); assert.deepEqual(i18n.getResource('vs/base/common/errorMessage'), base); assert.deepEqual(i18n.getResource('vs/code/electron-main/window'), code); - assert.deepEqual(i18n.getResource('vs/workbench/parts/html/browser/webview'), workbenchParts); + assert.deepEqual(i18n.getResource('vs/workbench/contrib/html/browser/webview'), workbenchParts); assert.deepEqual(i18n.getResource('vs/workbench/services/files/node/fileService'), workbenchServices); assert.deepEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); }); diff --git a/build/lib/test/i18n.test.ts b/build/lib/test/i18n.test.ts index cfc1041974b..eebc7742457 100644 --- a/build/lib/test/i18n.test.ts +++ b/build/lib/test/i18n.test.ts @@ -38,7 +38,7 @@ suite('XLF Parser Tests', () => { editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, - workbenchParts = { name: 'vs/workbench/parts/html', project: workbenchProject }, + workbenchParts = { name: 'vs/workbench/contrib/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/files', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject}; @@ -47,7 +47,7 @@ suite('XLF Parser Tests', () => { assert.deepEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); assert.deepEqual(i18n.getResource('vs/base/common/errorMessage'), base); assert.deepEqual(i18n.getResource('vs/code/electron-main/window'), code); - assert.deepEqual(i18n.getResource('vs/workbench/parts/html/browser/webview'), workbenchParts); + assert.deepEqual(i18n.getResource('vs/workbench/contrib/html/browser/webview'), workbenchParts); assert.deepEqual(i18n.getResource('vs/workbench/services/files/node/fileService'), workbenchServices); assert.deepEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); }); diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index 884d28279d8..da51ac19104 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -15,8 +15,7 @@ var ShakeLevel; ShakeLevel[ShakeLevel["ClassMembers"] = 2] = "ClassMembers"; })(ShakeLevel = exports.ShakeLevel || (exports.ShakeLevel = {})); function printDiagnostics(diagnostics) { - for (let i = 0; i < diagnostics.length; i++) { - const diag = diagnostics[i]; + for (const diag of diagnostics) { let result = ''; if (diag.file) { result += `${diag.file.fileName}: `; @@ -97,6 +96,11 @@ function discoverAndReadFiles(options) { FILES[`${moduleId}.d.ts`] = dts_filecontents; continue; } + const js_filename = path.join(options.sourcesRoot, moduleId + '.js'); + if (fs.existsSync(js_filename)) { + // This is an import for a .js file, so ignore it... + continue; + } let ts_filename; if (options.redirects[moduleId]) { ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); @@ -475,8 +479,7 @@ function generateResult(languageService, shakeLevel) { } else { let survivingImports = []; - for (let i = 0; i < node.importClause.namedBindings.elements.length; i++) { - const importNode = node.importClause.namedBindings.elements[i]; + for (const importNode of node.importClause.namedBindings.elements) { if (getColor(importNode) === 2 /* Black */) { survivingImports.push(importNode.getFullText(sourceFile)); } diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index 9c142793ade..060808312e6 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -61,15 +61,14 @@ export interface ITreeShakingResult { } function printDiagnostics(diagnostics: ReadonlyArray): void { - for (let i = 0; i < diagnostics.length; i++) { - const diag = diagnostics[i]; + for (const diag of diagnostics) { let result = ''; if (diag.file) { result += `${diag.file.fileName}: `; } if (diag.file && diag.start) { let location = diag.file.getLineAndCharacterOfPosition(diag.start); - result += `- ${location.line + 1},${location.character} - ` + result += `- ${location.line + 1},${location.character} - `; } result += JSON.stringify(diag.messageText); console.log(result); @@ -160,6 +159,12 @@ function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap { continue; } + const js_filename = path.join(options.sourcesRoot, moduleId + '.js'); + if (fs.existsSync(js_filename)) { + // This is an import for a .js file, so ignore it... + continue; + } + let ts_filename: string; if (options.redirects[moduleId]) { ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); @@ -459,7 +464,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt } if (black_queue.length === 0) { - for (let i = 0; i < gray_queue.length; i++) { + for (let i = 0; i< gray_queue.length; i++) { const node = gray_queue[i]; const nodeParent = node.parent; if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { @@ -604,8 +609,7 @@ function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLe } } else { let survivingImports: string[] = []; - for (let i = 0; i < node.importClause.namedBindings.elements.length; i++) { - const importNode = node.importClause.namedBindings.elements[i]; + for (const importNode of node.importClause.namedBindings.elements) { if (getColor(importNode) === NodeColor.Black) { survivingImports.push(importNode.getFullText(sourceFile)); } diff --git a/build/lib/tslint/noNlsInStandaloneEditorRule.js b/build/lib/tslint/noNlsInStandaloneEditorRule.js new file mode 100644 index 00000000000..affd4cc8370 --- /dev/null +++ b/build/lib/tslint/noNlsInStandaloneEditorRule.js @@ -0,0 +1,54 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const ts = require("typescript"); +const Lint = require("tslint"); +const path_1 = require("path"); +class Rule extends Lint.Rules.AbstractRule { + apply(sourceFile) { + if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)editor.api/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)editor.main/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)editor.worker/.test(sourceFile.fileName)) { + return this.applyWithWalker(new NoNlsInStandaloneEditorRuleWalker(sourceFile, this.getOptions())); + } + return []; + } +} +exports.Rule = Rule; +class NoNlsInStandaloneEditorRuleWalker extends Lint.RuleWalker { + constructor(file, opts) { + super(file, opts); + } + visitImportEqualsDeclaration(node) { + if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { + this._validateImport(node.moduleReference.expression.getText(), node); + } + } + visitImportDeclaration(node) { + this._validateImport(node.moduleSpecifier.getText(), node); + } + visitCallExpression(node) { + super.visitCallExpression(node); + // import('foo') statements inside the code + if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { + const [path] = node.arguments; + this._validateImport(path.getText(), node); + } + } + _validateImport(path, node) { + // remove quotes + path = path.slice(1, -1); + // resolve relative paths + if (path[0] === '.') { + path = path_1.join(this.getSourceFile().fileName, path); + } + if (/vs(\/|\\)nls/.test(path)) { + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts`)); + } + } +} diff --git a/build/lib/tslint/noNlsInStandaloneEditorRule.ts b/build/lib/tslint/noNlsInStandaloneEditorRule.ts new file mode 100644 index 00000000000..ae23d74d784 --- /dev/null +++ b/build/lib/tslint/noNlsInStandaloneEditorRule.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import { join } from 'path'; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + if ( + /vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)editor.api/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)editor.main/.test(sourceFile.fileName) + || /vs(\/|\\)editor(\/|\\)editor.worker/.test(sourceFile.fileName) + ) { + return this.applyWithWalker(new NoNlsInStandaloneEditorRuleWalker(sourceFile, this.getOptions())); + } + + return []; + } +} + +class NoNlsInStandaloneEditorRuleWalker extends Lint.RuleWalker { + + constructor(file: ts.SourceFile, opts: Lint.IOptions) { + super(file, opts); + } + + protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { + if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { + this._validateImport(node.moduleReference.expression.getText(), node); + } + } + + protected visitImportDeclaration(node: ts.ImportDeclaration): void { + this._validateImport(node.moduleSpecifier.getText(), node); + } + + protected visitCallExpression(node: ts.CallExpression): void { + super.visitCallExpression(node); + + // import('foo') statements inside the code + if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { + const [path] = node.arguments; + this._validateImport(path.getText(), node); + } + } + + private _validateImport(path: string, node: ts.Node): void { + // remove quotes + path = path.slice(1, -1); + + // resolve relative paths + if (path[0] === '.') { + path = join(this.getSourceFile().fileName, path); + } + + if ( + /vs(\/|\\)nls/.test(path) + ) { + this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts`)); + } + } +} diff --git a/build/lib/tslint/noStandaloneEditorRule.js b/build/lib/tslint/noStandaloneEditorRule.js index 37bcf40e673..44b70a6a8d7 100644 --- a/build/lib/tslint/noStandaloneEditorRule.js +++ b/build/lib/tslint/noStandaloneEditorRule.js @@ -44,8 +44,8 @@ class NoStandaloneEditorRuleWalker extends Lint.RuleWalker { if (path[0] === '.') { path = path_1.join(this.getSourceFile().fileName, path); } - if (/vs(\/|\\)editor(\/|\\)standalone/.test(path) - || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone/.test(path) + if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path) + || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path) || /vs(\/|\\)editor(\/|\\)editor.api/.test(path) || /vs(\/|\\)editor(\/|\\)editor.main/.test(path) || /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)) { diff --git a/build/lib/tslint/noStandaloneEditorRule.ts b/build/lib/tslint/noStandaloneEditorRule.ts index b85e2da037a..713b94097d4 100644 --- a/build/lib/tslint/noStandaloneEditorRule.ts +++ b/build/lib/tslint/noStandaloneEditorRule.ts @@ -53,8 +53,8 @@ class NoStandaloneEditorRuleWalker extends Lint.RuleWalker { } if ( - /vs(\/|\\)editor(\/|\\)standalone/.test(path) - || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone/.test(path) + /vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path) + || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path) || /vs(\/|\\)editor(\/|\\)editor.api/.test(path) || /vs(\/|\\)editor(\/|\\)editor.main/.test(path) || /vs(\/|\\)editor(\/|\\)editor.worker/.test(path) diff --git a/build/lib/tslint/noUnexternalizedStringsRule.js b/build/lib/tslint/noUnexternalizedStringsRule.js index f79421effc1..18a90c0f9c1 100644 --- a/build/lib/tslint/noUnexternalizedStringsRule.js +++ b/build/lib/tslint/noUnexternalizedStringsRule.js @@ -109,8 +109,7 @@ class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); } else if (isObjectLiteral(keyArg)) { - for (let i = 0; i < keyArg.properties.length; i++) { - const property = keyArg.properties[i]; + for (const property of keyArg.properties) { if (isPropertyAssignment(property)) { const name = property.name.getText(); if (name === 'key') { diff --git a/build/lib/tslint/noUnexternalizedStringsRule.ts b/build/lib/tslint/noUnexternalizedStringsRule.ts index e8e8d001a00..e8c0e98ec15 100644 --- a/build/lib/tslint/noUnexternalizedStringsRule.ts +++ b/build/lib/tslint/noUnexternalizedStringsRule.ts @@ -148,8 +148,7 @@ class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { if (isStringLiteral(keyArg)) { this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); } else if (isObjectLiteral(keyArg)) { - for (let i = 0; i < keyArg.properties.length; i++) { - const property = keyArg.properties[i]; + for (const property of keyArg.properties) { if (isPropertyAssignment(property)) { const name = property.name.getText(); if (name === 'key') { diff --git a/build/lib/tslint/translationRemindRule.js b/build/lib/tslint/translationRemindRule.js index 45bbd4688f6..2234094421b 100644 --- a/build/lib/tslint/translationRemindRule.js +++ b/build/lib/tslint/translationRemindRule.js @@ -33,7 +33,7 @@ class TranslationRemindRuleWalker extends Lint.RuleWalker { visitImportLikeDeclaration(node) { const currentFile = node.getSourceFile().fileName; const matchService = currentFile.match(/vs\/workbench\/services\/\w+/); - const matchPart = currentFile.match(/vs\/workbench\/parts\/\w+/); + const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/); if (!matchService && !matchPart) { return; } diff --git a/build/lib/tslint/translationRemindRule.ts b/build/lib/tslint/translationRemindRule.ts index 559b782c6da..e2671599d3c 100644 --- a/build/lib/tslint/translationRemindRule.ts +++ b/build/lib/tslint/translationRemindRule.ts @@ -42,7 +42,7 @@ class TranslationRemindRuleWalker extends Lint.RuleWalker { private visitImportLikeDeclaration(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration) { const currentFile = node.getSourceFile().fileName; const matchService = currentFile.match(/vs\/workbench\/services\/\w+/); - const matchPart = currentFile.match(/vs\/workbench\/parts\/\w+/); + const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/); if (!matchService && !matchPart) { return; } diff --git a/build/lib/util.js b/build/lib/util.js index e6f041f3d60..6a210d3decc 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -8,12 +8,13 @@ const es = require("event-stream"); const debounce = require("debounce"); const _filter = require("gulp-filter"); const rename = require("gulp-rename"); -const _ = require("underscore"); const path = require("path"); const fs = require("fs"); const _rimraf = require("rimraf"); const git = require("./git"); const VinylFile = require("vinyl"); +const download_1 = require("../download/download"); +const REPO_ROOT = path.join(__dirname, '../../'); const NoCancellationToken = { isCancellationRequested: () => false }; function incremental(streamProvider, initial, supportsCancellation) { const input = es.through(); @@ -98,22 +99,18 @@ function skipDirectories() { }); } exports.skipDirectories = skipDirectories; -function cleanNodeModule(name, excludes, includes) { - const toGlob = (path) => '**/node_modules/' + name + (path ? '/' + path : ''); - const negate = (str) => '!' + str; - const allFilter = _filter(toGlob('**'), { restore: true }); - const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob))); +function cleanNodeModules(rulePath) { + const rules = fs.readFileSync(rulePath, 'utf8') + .split(/\r?\n/g) + .map(line => line.trim()) + .filter(line => line && !/^#/.test(line)); + const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); + const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); const input = es.through(); - const nodeModuleInput = input.pipe(allFilter); - let output = nodeModuleInput.pipe(_filter(globs)); - if (includes) { - const includeGlobs = includes.map(toGlob); - output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); - } - output = output.pipe(allFilter.restore); + const output = es.merge(input.pipe(_filter(['**', ...excludes])), input.pipe(_filter(includes))); return es.duplex(input, output); } -exports.cleanNodeModule = cleanNodeModule; +exports.cleanNodeModules = cleanNodeModules; function loadSourcemaps() { const input = es.through(); const output = input @@ -180,7 +177,8 @@ function rimraf(dir) { return cb(err); }); }; - return cb => retry(cb); + retry.taskName = `clean-${path.basename(dir)}`; + return retry; } exports.rimraf = rimraf; function getVersion(root) { @@ -220,3 +218,38 @@ function versionStringToNumber(versionStr) { return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); } exports.versionStringToNumber = versionStringToNumber; +function download(requestOptions) { + const result = es.through(); + const filename = path.join(REPO_ROOT, `.build/tmp-${Date.now()}-${path.posix.basename(requestOptions.path)}`); + const opts = { + requestOptions: requestOptions, + destinationPath: filename + }; + download_1.downloadInExternalProcess(opts).then(() => { + fs.stat(filename, (err, stat) => { + if (err) { + result.emit('error', err); + return; + } + fs.readFile(filename, (err, data) => { + if (err) { + result.emit('error', err); + return; + } + fs.unlink(filename, () => { + result.emit('data', new VinylFile({ + path: path.normalize(requestOptions.path), + stat: stat, + base: path.normalize(requestOptions.path), + contents: data + })); + result.emit('end'); + }); + }); + }); + }, (err) => { + result.emit('error', err); + }); + return result; +} +exports.download = download; diff --git a/build/lib/util.ts b/build/lib/util.ts index 4617cc83f2f..e87c0650ec3 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -17,6 +17,9 @@ import * as git from './git'; import * as VinylFile from 'vinyl'; import { ThroughStream } from 'through'; import * as sm from 'source-map'; +import { IDownloadOptions, downloadInExternalProcess, IDownloadRequestOptions } from '../download/download'; + +const REPO_ROOT = path.join(__dirname, '../../'); export interface ICancellationToken { isCancellationRequested(): boolean; @@ -129,23 +132,21 @@ export function skipDirectories(): NodeJS.ReadWriteStream { }); } -export function cleanNodeModule(name: string, excludes: string[], includes?: string[]): NodeJS.ReadWriteStream { - const toGlob = (path: string) => '**/node_modules/' + name + (path ? '/' + path : ''); - const negate = (str: string) => '!' + str; +export function cleanNodeModules(rulePath: string): NodeJS.ReadWriteStream { + const rules = fs.readFileSync(rulePath, 'utf8') + .split(/\r?\n/g) + .map(line => line.trim()) + .filter(line => line && !/^#/.test(line)); - const allFilter = _filter(toGlob('**'), { restore: true }); - const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob) as (x: string) => string)); + const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); + const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); const input = es.through(); - const nodeModuleInput = input.pipe(allFilter); - let output: NodeJS.ReadWriteStream = nodeModuleInput.pipe(_filter(globs)); + const output = es.merge( + input.pipe(_filter(['**', ...excludes])), + input.pipe(_filter(includes)) + ); - if (includes) { - const includeGlobs = includes.map(toGlob); - output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); - } - - output = output.pipe(allFilter.restore); return es.duplex(input, output); } @@ -233,8 +234,8 @@ export function rimraf(dir: string): (cb: any) => void { return cb(err); }); }; - - return cb => retry(cb); + retry.taskName = `clean-${path.basename(dir)}`; + return retry; } export function getVersion(root: string): string | undefined { @@ -280,3 +281,38 @@ export function versionStringToNumber(versionStr: string) { return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); } + +export function download(requestOptions: IDownloadRequestOptions): NodeJS.ReadWriteStream { + const result = es.through(); + const filename = path.join(REPO_ROOT, `.build/tmp-${Date.now()}-${path.posix.basename(requestOptions.path)}`); + const opts: IDownloadOptions = { + requestOptions: requestOptions, + destinationPath: filename + }; + downloadInExternalProcess(opts).then(() => { + fs.stat(filename, (err, stat) => { + if (err) { + result.emit('error', err); + return; + } + fs.readFile(filename, (err, data) => { + if (err) { + result.emit('error', err); + return; + } + fs.unlink(filename, () => { + result.emit('data', new VinylFile({ + path: path.normalize(requestOptions.path), + stat: stat, + base: path.normalize(requestOptions.path), + contents: data + })); + result.emit('end'); + }); + }); + }); + }, (err) => { + result.emit('error', err); + }); + return result; +} diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js index 94f73cdd4a0..0a4af2a798f 100644 --- a/build/lib/watch/index.js +++ b/build/lib/watch/index.js @@ -17,7 +17,7 @@ function handleDeletions() { }); } -let watch = void 0; +let watch = undefined; if (!watch) { watch = process.platform === 'win32' ? require('./watch-win32') : require('gulp-watch'); diff --git a/build/monaco/ThirdPartyNotices.txt b/build/monaco/ThirdPartyNotices.txt index 45eeffb9f32..1de70ddaab6 100644 --- a/build/monaco/ThirdPartyNotices.txt +++ b/build/monaco/ThirdPartyNotices.txt @@ -7,23 +7,54 @@ herein, whether by implication, estoppel or otherwise. - -%% winjs version 4.4.0 (https://github.com/winjs/winjs) +%% nodejs path library (https://github.com/nodejs/node/tree/43dd49c9782848c25e5b03448c8a0f923f13c158) ========================================= -WinJS +Copyright Joyent, Inc. and other Node contributors. -Copyright (c) Microsoft Corporation +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: -All rights reserved. +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. -MIT License +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. +========================================= +END OF nodejs path library NOTICES AND INFORMATION -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. +%% promise-polyfill version 8.1.0 (https://github.com/taylorhakes/promise-polyfill) +========================================= +Copyright (c) 2014 Taylor Hakes +Copyright (c) 2014 Forbes Lindesay -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. +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. ========================================= END OF winjs NOTICES AND INFORMATION diff --git a/build/monaco/api.js b/build/monaco/api.js index 716d1f56655..297510d14c1 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -7,14 +7,15 @@ Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const ts = require("typescript"); const path = require("path"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const dtsv = '2'; const tsfmt = require('../../tsfmt.json'); const SRC = path.join(__dirname, '../../src'); exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); function logErr(message, ...rest) { - util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); + fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest); } function isDeclaration(a) { return (a.kind === ts.SyntaxKind.InterfaceDeclaration @@ -456,11 +457,20 @@ class FSProvider { existsSync(filePath) { return fs.existsSync(filePath); } + statSync(filePath) { + return fs.statSync(filePath); + } readFileSync(_moduleId, filePath) { return fs.readFileSync(filePath); } } exports.FSProvider = FSProvider; +class CacheEntry { + constructor(sourceFile, mtime) { + this.sourceFile = sourceFile; + this.mtime = mtime; + } +} class DeclarationResolver { constructor(_fsProvider) { this._fsProvider = _fsProvider; @@ -470,31 +480,43 @@ class DeclarationResolver { this._sourceFileCache[moduleId] = null; } getDeclarationSourceFile(moduleId) { + if (this._sourceFileCache[moduleId]) { + // Since we cannot trust file watching to invalidate the cache, check also the mtime + const fileName = this._getFileName(moduleId); + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (this._sourceFileCache[moduleId].mtime !== mtime) { + this._sourceFileCache[moduleId] = null; + } + } if (!this._sourceFileCache[moduleId]) { this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); } - return this._sourceFileCache[moduleId]; + return this._sourceFileCache[moduleId] ? this._sourceFileCache[moduleId].sourceFile : null; + } + _getFileName(moduleId) { + if (/\.d\.ts$/.test(moduleId)) { + return path.join(SRC, moduleId); + } + return path.join(SRC, `${moduleId}.ts`); } _getDeclarationSourceFile(moduleId) { - if (/\.d\.ts$/.test(moduleId)) { - const fileName = path.join(SRC, moduleId); - if (!this._fsProvider.existsSync(fileName)) { - return null; - } - const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); - return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); - } - const fileName = path.join(SRC, `${moduleId}.ts`); + const fileName = this._getFileName(moduleId); if (!this._fsProvider.existsSync(fileName)) { return null; } + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (/\.d\.ts$/.test(moduleId)) { + // const mtime = this._fsProvider.statFileSync() + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return new CacheEntry(ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), mtime); + } const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); const fileMap = { 'file.ts': fileContents }; const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {})); const text = service.getEmitOutput('file.ts', true).outputFiles[0].text; - return ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5); + return new CacheEntry(ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), mtime); } } exports.DeclarationResolver = DeclarationResolver; diff --git a/build/monaco/api.ts b/build/monaco/api.ts index d12b77787a0..7672188160b 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -6,7 +6,8 @@ import * as fs from 'fs'; import * as ts from 'typescript'; import * as path from 'path'; -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; const dtsv = '2'; @@ -17,7 +18,7 @@ export const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); function logErr(message: any, ...rest: any[]): void { - util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); + fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest); } type SourceFileGetter = (moduleId: string) => ts.SourceFile | null; @@ -547,14 +548,24 @@ export class FSProvider { public existsSync(filePath: string): boolean { return fs.existsSync(filePath); } + public statSync(filePath: string): fs.Stats { + return fs.statSync(filePath); + } public readFileSync(_moduleId: string, filePath: string): Buffer { return fs.readFileSync(filePath); } } +class CacheEntry { + constructor( + public readonly sourceFile: ts.SourceFile, + public readonly mtime: number + ) {} +} + export class DeclarationResolver { - private _sourceFileCache: { [moduleId: string]: ts.SourceFile | null; }; + private _sourceFileCache: { [moduleId: string]: CacheEntry | null; }; constructor(private readonly _fsProvider: FSProvider) { this._sourceFileCache = Object.create(null); @@ -565,32 +576,51 @@ export class DeclarationResolver { } public getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + if (this._sourceFileCache[moduleId]) { + // Since we cannot trust file watching to invalidate the cache, check also the mtime + const fileName = this._getFileName(moduleId); + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (this._sourceFileCache[moduleId]!.mtime !== mtime) { + this._sourceFileCache[moduleId] = null; + } + } if (!this._sourceFileCache[moduleId]) { this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); } - return this._sourceFileCache[moduleId]; + return this._sourceFileCache[moduleId] ? this._sourceFileCache[moduleId]!.sourceFile : null; } - private _getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + private _getFileName(moduleId: string): string { if (/\.d\.ts$/.test(moduleId)) { - const fileName = path.join(SRC, moduleId); - if (!this._fsProvider.existsSync(fileName)) { - return null; - } - const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); - return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); + return path.join(SRC, moduleId); } - const fileName = path.join(SRC, `${moduleId}.ts`); + return path.join(SRC, `${moduleId}.ts`); + } + + private _getDeclarationSourceFile(moduleId: string): CacheEntry | null { + const fileName = this._getFileName(moduleId); if (!this._fsProvider.existsSync(fileName)) { return null; } + const mtime = this._fsProvider.statSync(fileName).mtime.getTime(); + if (/\.d\.ts$/.test(moduleId)) { + // const mtime = this._fsProvider.statFileSync() + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return new CacheEntry( + ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), + mtime + ); + } const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); const fileMap: IFileMap = { 'file.ts': fileContents }; const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {})); const text = service.getEmitOutput('file.ts', true).outputFiles[0].text; - return ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5); + return new CacheEntry( + ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), + mtime + ); } } diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 356ef3ff046..ed3a8f5d260 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -21,12 +21,11 @@ declare namespace monaco { export class Emitter { constructor(); readonly event: Event; - fire(event?: T): void; + fire(event: T): void; dispose(): void; } #include(vs/platform/markers/common/markers): MarkerTag, MarkerSeverity -#include(vs/base/common/winjs.base.d.ts): Promise #include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken #include(vs/base/common/uri): URI, UriComponents #include(vs/base/common/keyCodes): KeyCode @@ -86,4 +85,4 @@ declare namespace monaco.worker { } -//dtsv=2 \ No newline at end of file +//dtsv=2 diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index 663f7b7cc26..13290a7abb5 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -10,9 +10,10 @@ import { CountBadge } from './vs/base/browser/ui/countBadge/countBadge'; import { SimpleWorkerClient, create as create1 } from './vs/base/common/worker/simpleWorker'; import { create as create2 } from './vs/editor/common/services/editorSimpleWorker'; import { QuickOpenWidget } from './vs/base/parts/quickopen/browser/quickOpenWidget'; +import { WorkbenchAsyncDataTree } from './vs/platform/list/browser/listService'; import { SyncDescriptor0, SyncDescriptor1, SyncDescriptor2, SyncDescriptor3, SyncDescriptor4, SyncDescriptor5, SyncDescriptor6, SyncDescriptor7, SyncDescriptor8 } from './vs/platform/instantiation/common/descriptors'; -import { PolyfillPromise } from './vs/base/common/winjs.polyfill.promise'; import { DiffNavigator } from './vs/editor/browser/widget/diffNavigator'; +import { DocumentRangeFormattingEditProvider } from './vs/editor/common/modes'; import * as editorAPI from './vs/editor/editor.api'; (function () { @@ -23,6 +24,7 @@ import * as editorAPI from './vs/editor/editor.api'; a = (b).getWorkspace; // IWorkspaceFolderProvider a = (b).style; // IThemable a = (b).style; // IThemable + a = (>b).style; // IThemable a = (b).userHome; // IUserHomeProvider a = (b).previous; // IDiffNavigator a = (>b).type; @@ -31,14 +33,7 @@ import * as editorAPI from './vs/editor/editor.api'; a = (>b).getProxyObject; // IWorkerClient a = create1; a = create2; - - // promise polyfill - a = PolyfillPromise.all; - a = PolyfillPromise.race; - a = PolyfillPromise.resolve; - a = PolyfillPromise.reject; - a = (b).then; - a = (b).catch; + a = (b).extensionId; // injection madness a = (>b).ctor; @@ -73,7 +68,6 @@ import * as editorAPI from './vs/editor/editor.api'; a = editorAPI.SelectionDirection; a = editorAPI.MarkerSeverity; a = editorAPI.MarkerTag; - a = editorAPI.Promise; a = editorAPI.Uri; a = editorAPI.Token; a = editorAPI.editor; diff --git a/build/monaco/package.json b/build/monaco/package.json index efd919085b2..1962694ce6d 100644 --- a/build/monaco/package.json +++ b/build/monaco/package.json @@ -1,7 +1,7 @@ { "name": "monaco-editor-core", "private": true, - "version": "0.14.3", + "version": "0.16.0", "description": "A browser based code editor", "author": "Microsoft Corporation", "license": "MIT", diff --git a/build/monaco/version.txt b/build/monaco/version.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index ea2d2d9a2dc..02755e171ec 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -17,7 +17,17 @@ function yarnInstall(location, opts) { opts.cwd = location; opts.stdio = 'inherit'; - const result = cp.spawnSync(yarn, ['install'], opts); + const raw = process.env['npm_config_argv'] || '{}'; + const argv = JSON.parse(raw); + const original = argv.original || []; + const args = ['install']; + + if (original.indexOf('--ignore-optional') > -1) { + args.push('--ignore-optional'); + } + + console.log('Installing dependencies in \'%s\'.', location); + const result = cp.spawnSync(yarn, args, opts); if (result.error || result.status !== 0) { process.exit(1); diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index a2029c364fa..3b0d7dc538f 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -7,8 +7,8 @@ let err = false; const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]); -if (majorNodeVersion < 8 || majorNodeVersion >= 9) { - console.error('\033[1;31m*** Please use node >=8 and <9.\033[0;0m'); +if (majorNodeVersion < 8 || majorNodeVersion >= 11) { + console.error('\033[1;31m*** Please use node >=8 and <11.\033[0;0m'); err = true; } diff --git a/build/npm/update-grammar.js b/build/npm/update-grammar.js index c009e36285c..522b2941218 100644 --- a/build/npm/update-grammar.js +++ b/build/npm/update-grammar.js @@ -12,6 +12,8 @@ var cson = require('cson-parser'); var https = require('https'); var url = require('url'); +let commitDate = '0000-00-00'; + /** * @param {string} urlString */ @@ -120,8 +122,35 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'mas try { fs.writeFileSync(dest, JSON.stringify(result, null, '\t').replace(/\n/g, '\r\n')); + let cgmanifestRead = JSON.parse(fs.readFileSync('./cgmanifest.json').toString()); + let promises = new Array(); + const currentCommitDate = info.commitDate.substr(0, 10); + + // Add commit sha to cgmanifest. + if (currentCommitDate > commitDate) { + let packageJsonPath = 'https://raw.githubusercontent.com/' + repoId + `/${info.commitSha}/package.json`; + for (let i = 0; i < cgmanifestRead.registrations.length; i++) { + if (cgmanifestRead.registrations[i].component.git.repositoryUrl.substr(cgmanifestRead.registrations[i].component.git.repositoryUrl.length - repoId.length, repoId.length) === repoId) { + cgmanifestRead.registrations[i].component.git.commitHash = info.commitSha; + commitDate = currentCommitDate; + promises.push(download(packageJsonPath).then(function (packageJson) { + if (packageJson) { + try { + cgmanifestRead.registrations[i].version = JSON.parse(packageJson).version; + } catch (e) { + console.log('Cannot get version. File does not exist at ' + packageJsonPath); + } + } + })); + break; + } + } + } + Promise.all(promises).then(function (allResult) { + fs.writeFileSync('./cgmanifest.json', JSON.stringify(cgmanifestRead, null, '\t').replace(/\n/g, '\r\n')); + }); if (info) { - console.log('Updated ' + path.basename(dest) + ' to ' + repoId + '@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + console.log('Updated ' + path.basename(dest) + ' to ' + repoId + '@' + info.commitSha.substr(0, 7) + ' (' + currentCommitDate + ')'); } else { console.log('Updated ' + path.basename(dest)); } diff --git a/build/npm/update-localization-extension.js b/build/npm/update-localization-extension.js index eebf63ed330..b1656dc88f4 100644 --- a/build/npm/update-localization-extension.js +++ b/build/npm/update-localization-extension.js @@ -9,16 +9,31 @@ let i18n = require("../lib/i18n"); let fs = require("fs"); let path = require("path"); + +let gulp = require('gulp'); let vfs = require("vinyl-fs"); let rimraf = require('rimraf'); +let minimist = require('minimist'); -function update(idOrPath) { +function update(options) { + let idOrPath = options._; if (!idOrPath) { throw new Error('Argument must be the location of the localization extension.'); } + let transifex = options.transifex; + let location = options.location; + if (transifex === true && location !== undefined) { + throw new Error('Either --transifex or --location can be specified, but not both.'); + } + if (!transifex && !location) { + transifex = true; + } + if (location !== undefined && !fs.existsSync(location)) { + throw new Error(`${location} doesn't exist.`); + } let locExtFolder = idOrPath; if (/^\w{2}(-\w+)?$/.test(idOrPath)) { - locExtFolder = '../vscode-language-pack-' + idOrPath; + locExtFolder = path.join('..', 'vscode-loc', 'i18n', `vscode-language-pack-${idOrPath}`); } let locExtStat = fs.statSync(locExtFolder); if (!locExtStat || !locExtStat.isDirectory) { @@ -54,21 +69,64 @@ function update(idOrPath) { rimraf.sync(translationDataFolder); } - console.log('Downloading translations for \'' + languageId + '\' to \'' + translationDataFolder + '\'...'); - const translationPaths = []; - i18n.pullI18nPackFiles(server, userName, apiToken, { id: languageId }, translationPaths) - .pipe(vfs.dest(translationDataFolder)).on('end', function () { - localization.translations = []; - for (let tp of translationPaths) { - localization.translations.push({ id: tp.id, path: `./translations/${tp.resourceName}`}); - } - fs.writeFileSync(path.join(locExtFolder, 'package.json'), JSON.stringify(packageJSON, null, '\t')); - }); - + if (transifex) { + console.log(`Downloading translations for ${languageId} to '${translationDataFolder}' ...`); + let translationPaths = []; + i18n.pullI18nPackFiles(server, userName, apiToken, { id: languageId }, translationPaths) + .on('error', (error) => { + console.log(`Error occurred while importing translations:`); + translationPaths = undefined; + if (Array.isArray(error)) { + error.forEach(console.log); + } else if (error) { + console.log(error); + } else { + console.log('Unknown error'); + } + }) + .pipe(vfs.dest(translationDataFolder)) + .on('end', function () { + if (translationPaths !== undefined) { + localization.translations = []; + for (let tp of translationPaths) { + localization.translations.push({ id: tp.id, path: `./translations/${tp.resourceName}`}); + } + fs.writeFileSync(path.join(locExtFolder, 'package.json'), JSON.stringify(packageJSON, null, '\t')); + } + }); + } else { + console.log(`Importing translations for ${languageId} form '${location}' to '${translationDataFolder}' ...`); + let translationPaths = []; + gulp.src(path.join(location, languageId, '**', '*.xlf')) + .pipe(i18n.prepareI18nPackFiles(i18n.externalExtensionsWithTranslations, translationPaths, languageId === 'ps')) + .on('error', (error) => { + console.log(`Error occurred while importing translations:`); + translationPaths = undefined; + if (Array.isArray(error)) { + error.forEach(console.log); + } else if (error) { + console.log(error); + } else { + console.log('Unknown error'); + } + }) + .pipe(vfs.dest(translationDataFolder)) + .on('end', function () { + if (translationPaths !== undefined) { + localization.translations = []; + for (let tp of translationPaths) { + localization.translations.push({ id: tp.id, path: `./translations/${tp.resourceName}`}); + } + fs.writeFileSync(path.join(locExtFolder, 'package.json'), JSON.stringify(packageJSON, null, '\t')); + } + }); + } }); - - } if (path.basename(process.argv[1]) === 'update-localization-extension.js') { - update(process.argv[2]); + var options = minimist(process.argv.slice(2), { + boolean: 'transifex', + string: 'location' + }); + update(options); } diff --git a/build/package.json b/build/package.json index 7e096ee9bab..49495b1c1e5 100644 --- a/build/package.json +++ b/build/package.json @@ -2,9 +2,11 @@ "name": "code-oss-dev-build", "version": "1.0.0", "devDependencies": { + "@types/ansi-colors": "^3.2.0", "@types/azure": "0.9.19", "@types/debounce": "^1.0.0", "@types/documentdb": "1.10.2", + "@types/fancy-log": "^1.3.0", "@types/glob": "^7.1.1", "@types/gulp": "^4.0.5", "@types/gulp-concat": "^0.0.32", @@ -13,7 +15,6 @@ "@types/gulp-rename": "^0.0.33", "@types/gulp-sourcemaps": "^0.0.32", "@types/gulp-uglify": "^3.0.5", - "@types/gulp-util": "^3.0.34", "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", "@types/minimist": "^1.2.0", @@ -38,7 +39,7 @@ "minimist": "^1.2.0", "request": "^2.85.0", "tslint": "^5.9.1", - "typescript": "3.1.4", + "typescript": "3.4.5", "vsce": "1.48.0", "xml2js": "^0.4.17" }, @@ -48,4 +49,4 @@ "postinstall": "npm run compile", "npmCheckJs": "tsc --noEmit" } -} +} \ No newline at end of file diff --git a/build/tsconfig.json b/build/tsconfig.json index 46ad745bc23..df15ccdd1be 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -8,7 +8,7 @@ "resolveJsonModule": true, "experimentalDecorators": true, // enable JavaScript type checking for the language service - // use the tsconfig.build.json for compiling wich disable JavaScript + // use the tsconfig.build.json for compiling which disable JavaScript // type checking so that JavaScript file are not transpiled "allowJs": true, "checkJs": true, @@ -22,4 +22,4 @@ "exclude": [ "node_modules/**" ] -} \ No newline at end of file +} diff --git a/build/win32/code.iss b/build/win32/code.iss index 43572199eb5..ea31a50c9bf 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -1,7 +1,7 @@ #define LocalizedLanguageFile(Language = "") \ DirExists(RepoDir + "\licenses") && Language != "" \ - ? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.txt"') \ - : '; LicenseFile: "' + RepoDir + '\LICENSE.txt"' + ? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.rtf"') \ + : '; LicenseFile: "' + RepoDir + '\LICENSE.rtf"' [Setup] AppId={#AppId} diff --git a/build/yarn.lock b/build/yarn.lock index 45a09657223..3d6f612789b 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -10,6 +10,11 @@ normalize-path "^2.0.1" through2 "^2.0.3" +"@types/ansi-colors@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/ansi-colors/-/ansi-colors-3.2.0.tgz#3e4fe85d9131ce1c6994f3040bd0b25306c16a6e" + integrity sha512-0caWAhXht9N2lOdMzJLXybsSkYCx1QOdxx6pae48tswI9QV3DFX26AoOpy0JxwhCb+zISTqmd6H8t9Zby9BoZg== + "@types/azure@0.9.19": version "0.9.19" resolved "https://registry.yarnpkg.com/@types/azure/-/azure-0.9.19.tgz#1a6a9bd856b437ddecf3f9fc8407a683c869ba02" @@ -47,6 +52,11 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== +"@types/fancy-log@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.0.tgz#a61ab476e5e628cd07a846330df53b85e05c8ce0" + integrity sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw== + "@types/form-data@*": version "2.2.1" resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" @@ -117,16 +127,6 @@ "@types/node" "*" "@types/uglify-js" "^2" -"@types/gulp-util@^3.0.34": - version "3.0.34" - resolved "https://registry.yarnpkg.com/@types/gulp-util/-/gulp-util-3.0.34.tgz#d1291ebf706d93f46eb8df11344bbfd96247697e" - integrity sha512-E06WN1OfqL5UsMwJ1T7ClgnaXgaPipb7Ee8euMc3KRHLNqxdvWrDir9KA6uevgzBgT7XbjgmzZA2pkzDqBBX7A== - dependencies: - "@types/node" "*" - "@types/through2" "*" - "@types/vinyl" "*" - chalk "^2.2.0" - "@types/gulp@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/gulp/-/gulp-4.0.5.tgz#f5f498d5bf9538364792de22490a12c0e6bc5eb4" @@ -196,7 +196,7 @@ "@types/glob" "*" "@types/node" "*" -"@types/through2@*", "@types/through2@^2.0.34": +"@types/through2@^2.0.34": version "2.0.34" resolved "https://registry.yarnpkg.com/@types/through2/-/through2-2.0.34.tgz#9c2a259a238dace2a05a2f8e94b786961bc27ac4" integrity sha512-nhRG8+RuG/L+0fAZBQYaRflXKjTrHOKH8MFTChnf+dNVMxA3wHYYrfj0tztK0W51ABXjGfRCDc0vRkecCOrsow== @@ -512,7 +512,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.2.0, chalk@^2.3.0: +chalk@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== @@ -1894,10 +1894,10 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -typescript@3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.4.tgz#c74ef7b3c2da65beff548b903022cb8c3cd997ed" - integrity sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q== +typescript@3.4.5: + version "3.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" + integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" diff --git a/cglicenses.json b/cglicenses.json index 943a03cbf96..3d96813f3ef 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -620,25 +620,6 @@ "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." ] }, -{ - // Waiting for https://github.com/ralphtheninja/expand-template/issues/10 - "name": "expand-template", - "licenseDetail": [ - " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", - " Version 2, December 2004", - "", - " Copyright (C) 2004 Sam Hocevar ", - "", - " Everyone is permitted to copy and distribute verbatim or modified", - " copies of this license document, and changing it is allowed as long", - " as the name is changed.", - "", - " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", - " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", - "", - " 0. You just DO WHAT THE FUCK YOU WANT TO." - ] -}, { "name": "tunnel-agent", "licenseDetail": [ diff --git a/cgmanifest.json b/cgmanifest.json index 637c7d2f5ab..01cdd8df5f1 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33" + "commitHash": "164c37e3f235134c88e80fac2a182cfba3f07f00" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "61.0.3163.100" + "version": "66.0.3359.181" }, { "component": { @@ -48,12 +48,12 @@ "git": { "name": "libchromiumcontent", "repositoryUrl": "https://github.com/electron/libchromiumcontent", - "commitHash": "ccdb085454b0a387ee96e0f81a7ca9a8ce07a710" + "commitHash": "7ea271f92018b1eeb8e70ec6de8c29f9758a0c05" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "61.0.3163.100" + "version": "66.0.3359.181" }, { "component": { @@ -61,11 +61,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "8a44289089a08b7b19fa3c4651b5f1f5d1edd71b" + "commitHash": "5cbb905c1af7cea2d709932d59827d7c6d03ef4a" } }, "isOnlyProductionDependency": true, - "version": "8.9.3" + "version": "10.2.0" }, { "component": { @@ -73,12 +73,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "d281859cf59f12c7107a540a9f4cba0ecf5eff41" + "commitHash": "e84a6860e35e14b4031b88bb9b49841cdb89a305" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "2.0.12" + "version": "3.1.8" }, { "component": { @@ -105,6 +105,30 @@ "license": "MIT", "version": "0.14.0" }, + { + "component": { + "type": "git", + "git": { + "name": "vscode-octicons-font", + "repositoryUrl": "https://github.com/Microsoft/vscode-octicons-font", + "commitHash": "4f69de3a233ed501c2098e33047e116ac2fbbf42" + } + }, + "license": "MIT", + "version": "1.1.0" + }, + { + "component": { + "type": "git", + "git": { + "name": "octicons", + "repositoryUrl": "https://github.com/primer/octicons", + "commitHash": "d120bf97bc9a12fb415f69fedaf31fe58427ca56" + } + }, + "license": "MIT", + "version": "8.3.0" + }, { "component": { "type": "npm", @@ -493,6 +517,19 @@ " defined by the Mozilla Public License, v. 2.0." ], "license": "MPL" + }, + { + "component": { + "type": "git", + "git": { + "name": "ripgrep", + "repositoryUrl": "https://github.com/BurntSushi/ripgrep", + "commitHash": "8a7db1a918e969b85cd933d8ed9fa5285b281ba4" + } + }, + "isOnlyProductionDependency": true, + "license": "MIT", + "version": "0.10.0" } ], "version": 1 diff --git a/extensions/bat/cgmanifest.json b/extensions/bat/cgmanifest.json index 32d7db263cc..5bc3e285f0c 100644 --- a/extensions/bat/cgmanifest.json +++ b/extensions/bat/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "mmims/language-batchfile", "repositoryUrl": "https://github.com/mmims/language-batchfile", - "commitHash": "4b67596631b4ecd2c89c2ec1b2e08a6623438903" + "commitHash": "95ea8c699f7a8296b15767069868532d52631c46" } }, "license": "MIT", - "version": "0.0.0" + "version": "0.7.5" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/bat/syntaxes/batchfile.tmLanguage.json b/extensions/bat/syntaxes/batchfile.tmLanguage.json index 26ae88f43c5..9eff3c9de2b 100644 --- a/extensions/bat/syntaxes/batchfile.tmLanguage.json +++ b/extensions/bat/syntaxes/batchfile.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/mmims/language-batchfile/commit/4b67596631b4ecd2c89c2ec1b2e08a6623438903", + "version": "https://github.com/mmims/language-batchfile/commit/95ea8c699f7a8296b15767069868532d52631c46", "name": "Batch File", "scopeName": "source.batchfile", "patterns": [ @@ -46,7 +46,7 @@ "commands": { "patterns": [ { - "match": "(?<=^|[\\s@])(?i:adprep|append|arp|assoc|at|atmadm|attrib|auditpol|autochk|autoconv|autofmt|bcdboot|bcdedit|bdehdcfg|bitsadmin|bootcfg|brea|cacls|cd|certreq|certutil|change|chcp|chdir|chglogon|chgport|chgusr|chkdsk|chkntfs|choice|cipher|clip|cls|clscluadmin|cluster|cmd|cmdkey|cmstp|color|comp|compact|convert|copy|cprofile|cscript|csvde|date|dcdiag|dcgpofix|dcpromo|defra|del|dfscmd|dfsdiag|dfsrmig|diantz|dir|dirquota|diskcomp|diskcopy|diskpart|diskperf|diskraid|diskshadow|dispdiag|doin|dnscmd|doskey|driverquery|dsacls|dsadd|dsamain|dsdbutil|dsget|dsmgmt|dsmod|dsmove|dsquery|dsrm|edit|endlocal|eraseesentutl|eventcreate|eventquery|eventtriggers|evntcmd|expand|extract|fc|filescrn|find|findstr|finger|flattemp|fonde|forfiles|format|freedisk|fsutil|ftp|ftype|fveupdate|getmac|gettype|gpfixup|gpresult|gpupdate|graftabl|hashgen|hep|helpctr|hostname|icacls|iisreset|inuse|ipconfig|ipxroute|irftp|ismserv|jetpack|klist|ksetup|ktmutil|ktpass|label|ldifd|ldp|lodctr|logman|logoff|lpq|lpr|macfile|makecab|manage-bde|mapadmin|md|mkdir|mklink|mmc|mode|more|mount|mountvol|move|mqbup|mqsvc|mqtgsvc|msdt|msg|msiexec|msinfo32|mstsc|nbtstat|net computer|net group|net localgroup|net print|net session|net share|net start|net stop|net use|net user|net view|net|netcfg|netdiag|netdom|netsh|netstat|nfsadmin|nfsshare|nfsstat|nlb|nlbmgr|nltest|nslookup|ntackup|ntcmdprompt|ntdsutil|ntfrsutl|openfiles|pagefileconfig|path|pathping|pause|pbadmin|pentnt|perfmon|ping|pnpunatten|pnputil|popd|powercfg|powershell|powershell_ise|print|prncnfg|prndrvr|prnjobs|prnmngr|prnport|prnqctl|prompt|pubprn|pushd|pushprinterconnections|pwlauncher|qappsrv|qprocess|query|quser|qwinsta|rasdial|rcp|rd|rdpsign|regentc|recover|redircmp|redirusr|reg|regini|regsvr32|relog|ren|rename|rendom|repadmin|repair-bde|replace|reset session|rxec|risetup|rmdir|robocopy|route|rpcinfo|rpcping|rsh|runas|rundll32|rwinsta|sc|schtasks|scwcmd|secedit|serverceipoptin|servrmanagercmd|serverweroptin|setspn|setx|sfc|shadow|shift|showmount|shutdown|sort|start|storrept|subst|sxstrace|ysocmgr|systeminfo|takeown|tapicfg|taskkill|tasklist|tcmsetup|telnet|tftp|time|timeout|title|tlntadmn|tpmvscmgr|tpmvscmgr|tacerpt|tracert|tree|tscon|tsdiscon|tsecimp|tskill|tsprof|type|typeperf|tzutil|uddiconfig|umount|unlodctr|ver|verifier|verif|vol|vssadmin|w32tm|waitfor|wbadmin|wdsutil|wecutil|wevtutil|where|whoami|winnt|winnt32|winpop|winrm|winrs|winsat|wlbs|mic|wscript|xcopy)(?=$|\\s)", + "match": "(?<=^|[\\s@])(?i:adprep|append|arp|assoc|at|atmadm|attrib|auditpol|autochk|autoconv|autofmt|bcdboot|bcdedit|bdehdcfg|bitsadmin|bootcfg|brea|cacls|cd|certreq|certutil|change|chcp|chdir|chglogon|chgport|chgusr|chkdsk|chkntfs|choice|cipher|clip|cls|clscluadmin|cluster|cmd|cmdkey|cmstp|color|comp|compact|convert|copy|cprofile|cscript|csvde|date|dcdiag|dcgpofix|dcpromo|defra|del|dfscmd|dfsdiag|dfsrmig|diantz|dir|dirquota|diskcomp|diskcopy|diskpart|diskperf|diskraid|diskshadow|dispdiag|doin|dnscmd|doskey|driverquery|dsacls|dsadd|dsamain|dsdbutil|dsget|dsmgmt|dsmod|dsmove|dsquery|dsrm|edit|endlocal|eraseesentutl|eventcreate|eventquery|eventtriggers|evntcmd|expand|extract|fc|filescrn|find|findstr|finger|flattemp|fonde|forfiles|format|freedisk|fsutil|ftp|ftype|fveupdate|getmac|gettype|gpfixup|gpresult|gpupdate|graftabl|hashgen|hep|helpctr|hostname|icacls|iisreset|inuse|ipconfig|ipxroute|irftp|ismserv|jetpack|klist|ksetup|ktmutil|ktpass|label|ldifd|ldp|lodctr|logman|logoff|lpq|lpr|macfile|makecab|manage-bde|mapadmin|md|mkdir|mklink|mmc|mode|more|mount|mountvol|move|mqbup|mqsvc|mqtgsvc|msdt|msg|msiexec|msinfo32|mstsc|nbtstat|net computer|net group|net localgroup|net print|net session|net share|net start|net stop|net use|net user|net view|net|netcfg|netdiag|netdom|netsh|netstat|nfsadmin|nfsshare|nfsstat|nlb|nlbmgr|nltest|nslookup|ntackup|ntcmdprompt|ntdsutil|ntfrsutl|openfiles|pagefileconfig|path|pathping|pause|pbadmin|pentnt|perfmon|ping|pnpunatten|pnputil|popd|powercfg|powershell|powershell_ise|print|prncnfg|prndrvr|prnjobs|prnmngr|prnport|prnqctl|prompt|pubprn|pushd|pushprinterconnections|pwlauncher|qappsrv|qprocess|query|quser|qwinsta|rasdial|rcp|rd|rdpsign|regentc|recover|redircmp|redirusr|reg|regini|regsvr32|relog|ren|rename|rendom|repadmin|repair-bde|replace|reset session|rxec|risetup|rmdir|robocopy|route|rpcinfo|rpcping|rsh|runas|rundll32|rwinsta|scp|sc|schtasks|scwcmd|secedit|serverceipoptin|servrmanagercmd|serverweroptin|setspn|setx|sfc|shadow|shift|showmount|shutdown|sort|ssh|start|storrept|subst|sxstrace|ysocmgr|systeminfo|takeown|tapicfg|taskkill|tasklist|tcmsetup|telnet|tftp|time|timeout|title|tlntadmn|tpmvscmgr|tpmvscmgr|tacerpt|tracert|tree|tscon|tsdiscon|tsecimp|tskill|tsprof|type|typeperf|tzutil|uddiconfig|umount|unlodctr|ver|verifier|verif|vol|vssadmin|w32tm|waitfor|wbadmin|wdsutil|wecutil|wevtutil|where|whoami|winnt|winnt32|winpop|winrm|winrs|winsat|wlbs|mic|wscript|xcopy)(?=$|\\s)", "name": "keyword.command.batchfile" }, { diff --git a/extensions/clojure/cgmanifest.json b/extensions/clojure/cgmanifest.json index bb14f456a9a..3a72fefb369 100644 --- a/extensions/clojure/cgmanifest.json +++ b/extensions/clojure/cgmanifest.json @@ -6,13 +6,13 @@ "git": { "name": "atom/language-clojure", "repositoryUrl": "https://github.com/atom/language-clojure", - "commitHash": "ecc790326bc8e14220e4d2d72a392a30876c3219" + "commitHash": "de877502aa4a77ccdc2c7f0c9180436aea3effff" } }, "license": "MIT", - "version": "0.0.0", + "version": "0.22.7", "description": "The file syntaxes/clojure.tmLanguage.json was derived from the Atom package https://github.com/atom/language-clojure which was originally converted from the TextMate bundle https://github.com/mmcgrana/textmate-clojure." } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/clojure/syntaxes/clojure.tmLanguage.json b/extensions/clojure/syntaxes/clojure.tmLanguage.json index 3d059513af0..29c25edfa24 100644 --- a/extensions/clojure/syntaxes/clojure.tmLanguage.json +++ b/extensions/clojure/syntaxes/clojure.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-clojure/commit/ecc790326bc8e14220e4d2d72a392a30876c3219", + "version": "https://github.com/atom/language-clojure/commit/de877502aa4a77ccdc2c7f0c9180436aea3effff", "name": "Clojure", "scopeName": "source.clojure", "patterns": [ @@ -83,7 +83,7 @@ "name": "constant.numeric.ratio.clojure" }, { - "match": "(-?\\d+[rR][0-9a-zA-Z]+)", + "match": "(-?\\d+[rR]\\w+)", "name": "constant.numeric.arbitrary-radix.clojure" }, { @@ -116,17 +116,17 @@ ] }, "keyword": { - "match": "(?<=(\\s|\\(|\\[|\\{)):[a-zA-Z0-9\\#\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}|\\,))", + "match": "(?<=(\\s|\\(|\\[|\\{)):[\\w\\#\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}|\\,))", "name": "constant.keyword.clojure" }, "keyfn": { "patterns": [ { - "match": "(?<=(\\s|\\(|\\[|\\{))(if(-[-a-z\\?]*)?|when(-[-a-z]*)?|for(-[-a-z]*)?|cond|do|let(-[-a-z\\?]*)?|binding|loop|recur|fn|throw[a-z\\-]*|try|catch|finally|([a-z]*case))(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{))(if(-[-\\p{Ll}\\?]*)?|when(-[-\\p{Ll}]*)?|for(-[-\\p{Ll}]*)?|cond|do|let(-[-\\p{Ll}\\?]*)?|binding|loop|recur|fn|throw[\\p{Ll}\\-]*|try|catch|finally|([\\p{Ll}]*case))(?=(\\s|\\)|\\]|\\}))", "name": "storage.control.clojure" }, { - "match": "(?<=(\\s|\\(|\\[|\\{))(declare-?|(in-)?ns|import|use|require|load|compile|(def[a-z\\-]*))(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{))(declare-?|(in-)?ns|import|use|require|load|compile|(def[\\p{Ll}\\-]*))(?=(\\s|\\)|\\]|\\}))", "name": "keyword.control.clojure" } ] @@ -309,7 +309,7 @@ "include": "#dynamic-variables" }, { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", "name": "entity.global.clojure" }, { @@ -387,7 +387,7 @@ "namespace-symbol": { "patterns": [ { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)/", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)/", "captures": { "1": { "name": "meta.symbol.namespace.clojure" @@ -399,13 +399,13 @@ "symbol": { "patterns": [ { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", "name": "meta.symbol.clojure" } ] }, "var": { - "match": "(?<=(\\s|\\(|\\[|\\{)\\#)'[a-zA-Z0-9\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{)\\#)'[\\w\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}))", "name": "meta.var.clojure" }, "vector": { diff --git a/extensions/coffeescript/cgmanifest.json b/extensions/coffeescript/cgmanifest.json index b8f3fc4a771..3c792173e6e 100644 --- a/extensions/coffeescript/cgmanifest.json +++ b/extensions/coffeescript/cgmanifest.json @@ -6,13 +6,13 @@ "git": { "name": "atom/language-coffee-script", "repositoryUrl": "https://github.com/atom/language-coffee-script", - "commitHash": "a0da2a73ad817e2fc13c2ef8fcd2624017c39610" + "commitHash": "0f6db9143663e18b1ad00667820f46747dba495e" } }, "license": "MIT", "description": "The file syntaxes/coffeescript.tmLanguage.json was derived from the Atom package https://github.com/atom/language-coffee-script which was originally converted from the TextMate bundle https://github.com/jashkenas/coffee-script-tmbundle.", - "version": "0.0.0" + "version": "0.49.3" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/coffeescript/package.json b/extensions/coffeescript/package.json index 2758bcdc50b..8d9293a8852 100644 --- a/extensions/coffeescript/package.json +++ b/extensions/coffeescript/package.json @@ -11,7 +11,7 @@ "contributes": { "languages": [{ "id": "coffeescript", - "extensions": [ ".coffee", ".cson" ], + "extensions": [ ".coffee", ".cson", ".iced" ], "aliases": [ "CoffeeScript", "coffeescript", "coffee" ], "configuration": "./language-configuration.json" }], diff --git a/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json b/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json index 5fcff295c61..708856898bc 100644 --- a/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json +++ b/extensions/coffeescript/syntaxes/coffeescript.tmLanguage.json @@ -4,10 +4,13 @@ "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-coffee-script/commit/a0da2a73ad817e2fc13c2ef8fcd2624017c39610", + "version": "https://github.com/atom/language-coffee-script/commit/0f6db9143663e18b1ad00667820f46747dba495e", "name": "CoffeeScript", "scopeName": "source.coffee", "patterns": [ + { + "include": "#jsx" + }, { "match": "(new)\\s+(?:(?:(class)\\s+(\\w+(?:\\.\\w*)*)?)|(\\w+(?:\\.\\w*)*))", "name": "meta.class.instance.constructor.coffee", @@ -1213,6 +1216,101 @@ "include": "#embedded_comment" } ] + }, + "jsx": { + "patterns": [ + { + "include": "#jsx-tag" + }, + { + "include": "#jsx-end-tag" + } + ] + }, + "jsx-expression": { + "begin": "{", + "beginCaptures": { + "0": { + "name": "meta.brace.curly.coffee" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "meta.brace.curly.coffee" + } + }, + "patterns": [ + { + "include": "#double_quoted_string" + }, + { + "include": "$self" + } + ] + }, + "jsx-attribute": { + "patterns": [ + { + "captures": { + "1": { + "name": "entity.other.attribute-name.coffee" + }, + "2": { + "name": "keyword.operator.assignment.coffee" + } + }, + "match": "(?:^|\\s+)([-\\w.]+)\\s*(=)" + }, + { + "include": "#double_quoted_string" + }, + { + "include": "#single_quoted_string" + }, + { + "include": "#jsx-expression" + } + ] + }, + "jsx-tag": { + "patterns": [ + { + "begin": "(<)([-\\w\\.]+)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.coffee" + }, + "2": { + "name": "entity.name.tag.coffee" + } + }, + "end": "(/?>)", + "name": "meta.tag.coffee", + "patterns": [ + { + "include": "#jsx-attribute" + } + ] + } + ] + }, + "jsx-end-tag": { + "patterns": [ + { + "begin": "()", + "name": "meta.tag.coffee" + } + ] } } } \ No newline at end of file diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 46f4eba3a95..bb9690a07d4 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -57,6 +57,10 @@ "fileMatch": "%APP_SETTINGS_HOME%/settings.json", "url": "vscode://schemas/settings/user" }, + { + "fileMatch": "%MACHINE_SETTINGS_HOME%/settings.json", + "url": "vscode://schemas/settings/machine" + }, { "fileMatch": "%APP_WORKSPACES_HOME%/*/workspace.json", "url": "vscode://schemas/workspaceConfig" @@ -96,6 +100,6 @@ ] }, "devDependencies": { - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" } } diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts index b0a083e7e9e..913c5288809 100644 --- a/extensions/configuration-editing/src/extension.ts +++ b/extensions/configuration-editing/src/extension.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); -import * as vscode from 'vscode'; -import { getLocation, visit, parse, ParseErrorCode } from 'jsonc-parser'; +import { getLocation, parse, visit } from 'jsonc-parser'; import * as path from 'path'; +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; import { SettingsDocument } from './settingsDocumentHelper'; +const localize = nls.loadMessageBundle(); const fadedDecoration = vscode.window.createTextEditorDecorationType({ light: { @@ -22,9 +22,6 @@ const fadedDecoration = vscode.window.createTextEditorDecorationType({ let pendingLaunchJsonDecoration: NodeJS.Timer; export function activate(context: vscode.ExtensionContext): void { - //keybindings.json command-suggestions - context.subscriptions.push(registerKeybindingsCompletions()); - //settings.json suggestions context.subscriptions.push(registerSettingsCompletions()); @@ -48,67 +45,6 @@ export function activate(context: vscode.ExtensionContext): void { } }, null, context.subscriptions)); updateLaunchJsonDecorations(vscode.window.activeTextEditor); - - context.subscriptions.push(vscode.workspace.onWillSaveTextDocument(e => { - if (!e.document.fileName.endsWith('/settings.json')) { - return; - } - - autoFixSettingsJSON(e); - })); -} - -function autoFixSettingsJSON(willSaveEvent: vscode.TextDocumentWillSaveEvent): void { - const document = willSaveEvent.document; - const text = document.getText(); - const edit = new vscode.WorkspaceEdit(); - - let lastEndOfSomething = -1; - visit(text, { - onArrayEnd(offset: number, length: number): void { - lastEndOfSomething = offset + length; - }, - - onLiteralValue(_value: any, offset: number, length: number): void { - lastEndOfSomething = offset + length; - }, - - onObjectEnd(offset: number, length: number): void { - lastEndOfSomething = offset + length; - }, - - onError(error: ParseErrorCode, _offset: number, _length: number): void { - if (error === ParseErrorCode.CommaExpected && lastEndOfSomething > -1) { - const fixPosition = document.positionAt(lastEndOfSomething); - - // Don't insert a comma immediately before a : or ' :' - const colonRange = document.getWordRangeAtPosition(fixPosition, / *:/); - if (!colonRange) { - edit.insert(document.uri, fixPosition, ','); - } - } - } - }); - - willSaveEvent.waitUntil( - vscode.workspace.applyEdit(edit)); -} - -function registerKeybindingsCompletions(): vscode.Disposable { - const commands = vscode.commands.getCommands(true); - - return vscode.languages.registerCompletionItemProvider({ pattern: '**/keybindings.json' }, { - - provideCompletionItems(document, position, _token) { - const location = getLocation(document.getText(), document.offsetAt(position)); - if (location.path[1] === 'command') { - - const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); - return commands.then(ids => ids.map(id => newSimpleCompletionItem(JSON.stringify(id), range))); - } - return undefined; - } - }); } function registerSettingsCompletions(): vscode.Disposable { @@ -207,16 +143,6 @@ function provideInstalledExtensionProposals(extensionsContent: IExtensionsConten return undefined; } -function newSimpleCompletionItem(label: string, range: vscode.Range, description?: string, insertText?: string): vscode.CompletionItem { - const item = new vscode.CompletionItem(label); - item.kind = vscode.CompletionItemKind.Value; - item.detail = description; - item.insertText = insertText || label; - item.range = range; - - return item; -} - function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): void { if (!editor || path.basename(editor.document.fileName) !== 'launch.json') { return; diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index e6b137809ff..d4ac414897d 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -44,8 +44,11 @@ export class SettingsDocument { const completions: vscode.CompletionItem[] = []; completions.push(this.newSimpleCompletionItem('${activeEditorShort}', range, localize('activeEditorShort', "the file name (e.g. myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt)"))); - completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeEditorMedium}', range, localize('activeEditorMedium', "the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeEditorLong}', range, localize('activeEditorLong', "the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderShort}', range, localize('activeFolderShort', "the name of the folder the file is contained in (e.g. myFileFolder)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderMedium}', range, localize('activeFolderMedium', "the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)"))); + completions.push(this.newSimpleCompletionItem('${activeFolderLong}', range, localize('activeFolderLong', "the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)"))); completions.push(this.newSimpleCompletionItem('${rootName}', range, localize('rootName', "name of the workspace (e.g. myFolder or myWorkspace)"))); completions.push(this.newSimpleCompletionItem('${rootPath}', range, localize('rootPath', "file path of the workspace (e.g. /Users/Development/myWorkspace)"))); completions.push(this.newSimpleCompletionItem('${folderName}', range, localize('folderName', "name of the workspace folder the file is contained in (e.g. myFolder)"))); diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index 752f2a3a6f9..49c47166066 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== jsonc-parser@2.0.2: version "2.0.2" diff --git a/extensions/cpp/build/update-grammars.js b/extensions/cpp/build/update-grammars.js index f02f51ca2cc..406b326156c 100644 --- a/extensions/cpp/build/update-grammars.js +++ b/extensions/cpp/build/update-grammars.js @@ -6,8 +6,8 @@ var updateGrammar = require('../../../build/npm/update-grammar'); -updateGrammar.update('atom/language-c', 'grammars/c.cson', './syntaxes/c.tmLanguage.json'); -updateGrammar.update('atom/language-c', 'grammars/c%2B%2B.cson', './syntaxes/cpp.tmLanguage.json'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/c.tmLanguage.json', './syntaxes/c.tmLanguage.json'); +updateGrammar.update('jeff-hykin/cpp-textmate-grammar', '/syntaxes/cpp.tmLanguage.json', './syntaxes/cpp.tmLanguage.json'); // `source.c.platform` which is still included by other grammars updateGrammar.update('textmate/c.tmbundle', 'Syntaxes/Platform.tmLanguage', './syntaxes/platform.tmLanguage.json'); diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index 16d849387cd..a1b745d8a43 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -4,14 +4,14 @@ "component": { "type": "git", "git": { - "name": "atom/language-c", - "repositoryUrl": "https://github.com/atom/language-c", - "commitHash": "9c0c5f202741a5647025db8d5df5fefba47b036c" + "name": "jeff-hykin/cpp-textmate-grammar", + "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", + "commitHash": "dc404ccf4eb08a5f76434e759b519f59051a32e5" } }, "license": "MIT", - "version": "0.0.0", - "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from the Atom package https://atom.io/packages/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." + "version": "1.8.13", + "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { "component": { @@ -42,4 +42,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/cpp/syntaxes/c.tmLanguage.json b/extensions/cpp/syntaxes/c.tmLanguage.json index bcd5470a64c..ec2ee36d25d 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/atom/language-c/blob/master/grammars/c.cson", + "This file has been converted from https://github.com/jeff-hykin/cpp-textmate-grammar/blob/master//syntaxes/c.tmLanguage.json", "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-c/commit/9c0c5f202741a5647025db8d5df5fefba47b036c", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/48734a7a8b365c12aeb7551aed0090b49601ed70", "name": "C", "scopeName": "source.c", "patterns": [ @@ -21,12 +21,19 @@ "include": "#comments" }, { - "match": "\\b(break|case|continue|default|do|else|for|goto|if|_Pragma|return|switch|while)\\b", + "include": "#switch_statement" + }, + { + "match": "\\b(break|continue|do|else|for|goto|if|_Pragma|return|while)\\b", "name": "keyword.control.c" }, { "include": "#storage_types" }, + { + "match": "typedef", + "name": "keyword.other.typedef.c" + }, { "match": "\\b(const|extern|register|restrict|static|volatile|inline)\\b", "name": "storage.modifier.c" @@ -57,7 +64,7 @@ "include": "#strings" }, { - "begin": "(?x)\n^\\s* ((\\#)\\s*define) \\s+ # define\n((?[a-zA-Z_$][\\w$]*)) # macro name\n(?:\n (\\()\n (\n \\s* \\g \\s* # first argument\n ((,) \\s* \\g \\s*)* # additional arguments\n (?:\\.\\.\\.)? # varargs ellipsis?\n )\n (\\))\n)?", + "begin": "(?x)\n^\\s* ((\\#)\\s*define) \\s+\t# define\n((?[a-zA-Z_$][\\w$]*))\t # macro name\n(?:\n (\\()\n\t(\n\t \\s* \\g \\s*\t\t # first argument\n\t ((,) \\s* \\g \\s*)* # additional arguments\n\t (?:\\.\\.\\.)?\t\t\t# varargs ellipsis?\n\t)\n (\\))\n)?", "beginCaptures": { "1": { "name": "keyword.control.directive.define.c" @@ -90,8 +97,8 @@ ] }, { - "begin": "^\\s*((#)\\s*(error|warning))\\b", - "captures": { + "begin": "^\\s*((#)\\s*(error|warning))\\b\\s*", + "beginCaptures": { "1": { "name": "keyword.control.directive.diagnostic.$3.c" }, @@ -99,17 +106,61 @@ "name": "punctuation.definition.directive.c" } }, - "end": "(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", - "end": "(?<=\\))(?!\\w)", "name": "meta.function.c", + "begin": "(?\\]\\)]))\\s*([a-zA-Z_]\\w*)\\s*(?=(?:\\[\\]\\s*)?(?:,|\\)))", "captures": { + "1": { + "name": "variable.parameter.probably.c" + } + } + }, + "access-method": { + "name": "meta.function-call.member.c", + "begin": "([a-zA-Z_][a-zA-Z_0-9]*|(?<=[\\]\\)]))\\s*(?:(\\.)|(->))((?:(?:[a-zA-Z_][a-zA-Z_0-9]*)\\s*(?:(?:\\.)|(?:->)))*)\\s*([a-zA-Z_][a-zA-Z_0-9]*)(\\()", + "beginCaptures": { + "1": { + "name": "variable.object.c" + }, "2": { "name": "punctuation.separator.dot-access.c" }, @@ -311,10 +391,43 @@ "name": "punctuation.separator.pointer-access.c" }, "4": { - "name": "variable.other.member.c" + "patterns": [ + { + "match": "\\.", + "name": "punctuation.separator.dot-access.c" + }, + { + "match": "->", + "name": "punctuation.separator.pointer-access.c" + }, + { + "match": "[a-zA-Z_][a-zA-Z_0-9]*", + "name": "variable.object.c" + }, + { + "name": "everything.else.c", + "match": ".+" + } + ] + }, + "5": { + "name": "entity.name.function.member.c" + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.function.member.c" } }, - "match": "((\\.)|(->))\\s*(([a-zA-Z_][a-zA-Z_0-9]*)\\b(?!\\s*\\())?" + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.function.member.c" + } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] }, "block": { "patterns": [ @@ -352,25 +465,36 @@ "include": "#preprocessor-rule-conditional-block" }, { - "include": "#access" + "include": "#method_access" }, { - "include": "#libc" + "include": "#member_access" }, { "include": "#c_function_call" }, { - "captures": { + "name": "meta.initialization.c", + "begin": "(?x)\n(?:\n (?:\n\t(?=\\s)(?=+!]+ | \\(\\) | \\[\\]))\n)\n\\s*(\\() # opening bracket", + "beginCaptures": { "1": { "name": "variable.other.c" }, "2": { - "name": "punctuation.definition.parameters.c" + "name": "punctuation.section.parens.begin.bracket.round.initialization.c" } }, - "match": "(?x)\n(?:\n (?:\n (?=\\s)(?=+!]+ | \\(\\) | \\[\\]))\n)\n\\s*(\\() # opening bracket", - "name": "meta.initialization.c" + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.initialization.c" + } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] }, { "begin": "{", @@ -400,7 +524,7 @@ ] }, "c_function_call": { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", "end": "(?<=\\))(?!\\w)", "name": "meta.function-call.c", "patterns": [ @@ -446,13 +570,13 @@ } }, "match": "^// =(\\s*.*?)\\s*=\\s*$\\n?", - "name": "comment.line.banner.cpp" + "name": "comment.line.banner.c" }, { "begin": "(^[ \\t]+)?(?=//)", "beginCaptures": { "1": { - "name": "punctuation.whitespace.comment.leading.cpp" + "name": "punctuation.whitespace.comment.leading.c" } }, "end": "(?!\\G)", @@ -461,11 +585,11 @@ "begin": "//", "beginCaptures": { "0": { - "name": "punctuation.definition.comment.cpp" + "name": "punctuation.definition.comment.c" } }, "end": "(?=\\n)", - "name": "comment.line.double-slash.cpp", + "name": "comment.line.double-slash.c", "patterns": [ { "include": "#line_continuation_character" @@ -488,17 +612,6 @@ } ] }, - "libc": { - "captures": { - "1": { - "name": "punctuation.whitespace.support.function.leading.c" - }, - "2": { - "name": "support.function.C99.c" - } - }, - "match": "(?x) (\\s*) \\b\n(_Exit|(?:nearbyint|nextafter|nexttoward|netoward|nan)[fl]?|a(?:cos|sin)h?[fl]?|abort|abs|asctime|assert\n|atan(?:[h2]?[fl]?)?|atexit|ato[ifl]|atoll|bsearch|btowc|cabs[fl]?|cacos|cacos[fl]|cacosh[fl]?\n|calloc|carg[fl]?|casinh?[fl]?|catanh?[fl]?|cbrt[fl]?|ccosh?[fl]?|ceil[fl]?|cexp[fl]?|cimag[fl]?\n|clearerr|clock|clog[fl]?|conj[fl]?|copysign[fl]?|cosh?[fl]?|cpow[fl]?|cproj[fl]?|creal[fl]?\n|csinh?[fl]?|csqrt[fl]?|ctanh?[fl]?|ctime|difftime|div|erfc?[fl]?|exit|fabs[fl]?\n|exp(?:2[fl]?|[fl]|m1[fl]?)?|fclose|fdim[fl]?|fe[gs]et(?:env|exceptflag|round)|feclearexcept\n|feholdexcept|feof|feraiseexcept|ferror|fetestexcept|feupdateenv|fflush|fgetpos|fgetw?[sc]\n|floor[fl]?|fmax?[fl]?|fmin[fl]?|fmod[fl]?|fopen|fpclassify|fprintf|fputw?[sc]|fread|free|freopen\n|frexp[fl]?|fscanf|fseek|fsetpos|ftell|fwide|fwprintf|fwrite|fwscanf|genv|get[sc]|getchar|gmtime\n|gwc|gwchar|hypot[fl]?|ilogb[fl]?|imaxabs|imaxdiv|isalnum|isalpha|isblank|iscntrl|isdigit|isfinite\n|isgraph|isgreater|isgreaterequal|isinf|isless(?:equal|greater)?|isw?lower|isnan|isnormal|isw?print\n|isw?punct|isw?space|isunordered|isw?upper|iswalnum|iswalpha|iswblank|iswcntrl|iswctype|iswdigit|iswgraph\n|isw?xdigit|labs|ldexp[fl]?|ldiv|lgamma[fl]?|llabs|lldiv|llrint[fl]?|llround[fl]?|localeconv|localtime\n|log[2b]?[fl]?|log1[p0][fl]?|longjmp|lrint[fl]?|lround[fl]?|malloc|mbr?len|mbr?towc|mbsinit|mbsrtowcs\n|mbstowcs|memchr|memcmp|memcpy|memmove|memset|mktime|modf[fl]?|perror|pow[fl]?|printf|puts|putw?c(?:har)?\n|qsort|raise|rand|remainder[fl]?|realloc|remove|remquo[fl]?|rename|rewind|rint[fl]?|round[fl]?|scalbl?n[fl]?\n|scanf|setbuf|setjmp|setlocale|setvbuf|signal|signbit|sinh?[fl]?|snprintf|sprintf|sqrt[fl]?|srand|sscanf\n|strcat|strchr|strcmp|strcoll|strcpy|strcspn|strerror|strftime|strlen|strncat|strncmp|strncpy|strpbrk\n|strrchr|strspn|strstr|strto[kdf]|strtoimax|strtol[dl]?|strtoull?|strtoumax|strxfrm|swprintf|swscanf\n|system|tan|tan[fl]|tanh[fl]?|tgamma[fl]?|time|tmpfile|tmpnam|tolower|toupper|trunc[fl]?|ungetw?c|va_arg\n|va_copy|va_end|va_start|vfw?printf|vfw?scanf|vprintf|vscanf|vsnprintf|vsprintf|vsscanf|vswprintf|vswscanf\n|vwprintf|vwscanf|wcrtomb|wcscat|wcschr|wcscmp|wcscoll|wcscpy|wcscspn|wcsftime|wcslen|wcsncat|wcsncmp|wcsncpy\n|wcspbrk|wcsrchr|wcsrtombs|wcsspn|wcsstr|wcsto[dkf]|wcstoimax|wcstol[dl]?|wcstombs|wcstoull?|wcstoumax|wcsxfrm\n|wctom?b|wmem(?:set|chr|cpy|cmp|move)|wprintf|wscanf)\\b" - }, "line_continuation_character": { "patterns": [ { @@ -511,15 +624,8 @@ } ] }, - "numbers": { - "patterns": [ - { - "match": "\\b((0(x|X)[0-9a-fA-F]([0-9a-fA-F']*[0-9a-fA-F])?)|(0(b|B)[01]([01']*[01])?)|(([0-9]([0-9']*[0-9])?\\.?[0-9]*([0-9']*[0-9])?)|(\\.[0-9]([0-9']*[0-9])?))((e|E)(\\+|-)?[0-9]([0-9']*[0-9])?)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b", - "name": "constant.numeric.c" - } - ] - }, "parens": { + "name": "meta.parens.c", "begin": "\\(", "beginCaptures": { "0": { @@ -539,6 +645,7 @@ ] }, "parens-block": { + "name": "meta.parens.block.c", "begin": "\\(", "beginCaptures": { "0": { @@ -554,6 +661,10 @@ "patterns": [ { "include": "#block_innards" + }, + { + "match": "(?-mix:(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas|asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|_Imaginary|int|long|short|signed|struct|typedef|union|unsigned|void)\\s*\\()\n(?=\n (?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n |\n (?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", "end": "(?<=\\))(?!\\w)|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "entity.name.function.c" @@ -1846,7 +1961,8 @@ "include": "#vararg_ellipses" }, { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "name": "meta.function.definition.parameters.c", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "entity.name.function.c" @@ -1862,6 +1978,9 @@ } }, "patterns": [ + { + "include": "#probably_a_parameter" + }, { "include": "#function-innards" } @@ -1900,13 +2019,16 @@ "include": "#storage_types" }, { - "include": "#access" + "include": "#method_access" + }, + { + "include": "#member_access" }, { "include": "#operators" }, { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|enumerate|return|typeid|alignof|alignas|sizeof|[cr]?iterate|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++ # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "entity.name.function.c" @@ -1950,6 +2072,518 @@ "include": "#block_innards" } ] + }, + "default_statement": { + "name": "meta.conditional.case.c", + "begin": "((?\\[\\]=]))", + "patterns": [ + { + "name": "meta.head.switch.c", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.switch.c" + } + }, + "patterns": [ + { + "include": "#switch_conditional_parentheses" + }, + { + "include": "$base" + } + ] + }, + { + "name": "meta.body.switch.c", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.switch.c" + } + }, + "patterns": [ + { + "include": "#default_statement" + }, + { + "include": "#case_statement" + }, + { + "include": "$base" + } + ] + }, + { + "name": "meta.tail.switch.c", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "switch_conditional_parentheses": { + "name": "meta.conditional.switch.c", + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.parens.begin.bracket.round.conditional.switch.c" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.parens.end.bracket.round.conditional.switch.c" + } + }, + "patterns": [ + { + "include": "#conditional_context" + } + ] + }, + "static_assert": { + "begin": "(static_assert|_Static_assert)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.static_assert.c" + }, + "2": { + "name": "punctuation.section.arguments.begin.bracket.round.c" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.c" + } + }, + "patterns": [ + { + "name": "meta.static_assert.message.c", + "begin": "(,)\\s*(?=(?:L|u8|u|U\\s*\\\")?)", + "beginCaptures": { + "1": { + "name": "comma.c punctuation.separator.delimiter.c" + } + }, + "end": "(?=\\))", + "patterns": [ + { + "include": "#string_context" + }, + { + "include": "#string_context_c" + } + ] + }, + { + "include": "#function_call_context_c" + } + ] + }, + "conditional_context": { + "patterns": [ + { + "include": "$base" + } + ] + }, + "member_access": { + "match": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!(?:void|char|short|int|signed|unsigned|long|float|double|bool|_Bool|_Complex|_Imaginary|u_char|u_short|u_int|u_long|ushort|uint|u_quad_t|quad_t|qaddr_t|caddr_t|daddr_t|div_t|dev_t|fixpt_t|blkcnt_t|blksize_t|gid_t|in_addr_t|in_port_t|ino_t|key_t|mode_t|nlink_t|id_t|pid_t|off_t|segsz_t|swblk_t|uid_t|id_t|clock_t|size_t|ssize_t|time_t|useconds_t|suseconds_t|pthread_attr_t|pthread_cond_t|pthread_condattr_t|pthread_mutex_t|pthread_mutexattr_t|pthread_once_t|pthread_rwlock_t|pthread_rwlockattr_t|pthread_t|pthread_key_t|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t|memory_order|atomic_bool|atomic_char|atomic_schar|atomic_uchar|atomic_short|atomic_ushort|atomic_int|atomic_uint|atomic_long|atomic_ulong|atomic_llong|atomic_ullong|atomic_char16_t|atomic_char32_t|atomic_wchar_t|atomic_int_least8_t|atomic_uint_least8_t|atomic_int_least16_t|atomic_uint_least16_t|atomic_int_least32_t|atomic_uint_least32_t|atomic_int_least64_t|atomic_uint_least64_t|atomic_int_fast8_t|atomic_uint_fast8_t|atomic_int_fast16_t|atomic_uint_fast16_t|atomic_int_fast32_t|atomic_uint_fast32_t|atomic_int_fast64_t|atomic_uint_fast64_t|atomic_intptr_t|atomic_uintptr_t|atomic_size_t|atomic_ptrdiff_t|atomic_intmax_t|atomic_uintmax_t))[a-zA-Z_]\\w*\\b(?!\\())", + "captures": { + "1": { + "name": "variable.other.object.access.c" + }, + "2": { + "name": "punctuation.separator.dot-access.c" + }, + "3": { + "name": "punctuation.separator.pointer-access.c" + }, + "4": { + "patterns": [ + { + "include": "#member_access" + }, + { + "include": "#method_access" + }, + { + "match": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))", + "captures": { + "1": { + "name": "variable.other.object.access.c" + }, + "2": { + "name": "punctuation.separator.dot-access.c" + }, + "3": { + "name": "punctuation.separator.pointer-access.c" + } + } + } + ] + }, + "5": { + "name": "variable.other.member.c" + } + } + }, + "method_access": { + "contentName": "meta.function-call.member", + "begin": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*([a-zA-Z_]\\w*)(\\()", + "beginCaptures": { + "1": { + "name": "variable.other.object.access.c" + }, + "2": { + "name": "punctuation.separator.dot-access.c" + }, + "3": { + "name": "punctuation.separator.pointer-access.c" + }, + "4": { + "patterns": [ + { + "include": "#member_access" + }, + { + "include": "#method_access" + }, + { + "match": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))", + "captures": { + "1": { + "name": "variable.other.object.access.c" + }, + "2": { + "name": "punctuation.separator.dot-access.c" + }, + "3": { + "name": "punctuation.separator.pointer-access.c" + } + } + } + ] + }, + "5": { + "name": "entity.name.function.member.c" + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.function.member.c" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.function.member.c" + } + }, + "patterns": [ + { + "include": "#function-call-innards" + } + ] + }, + "numbers": { + "begin": "(?", - "name": "meta.angle-brackets.cpp", + "sizeof_operator": { + "contentName": "meta.arguments.operator.sizeof", + "begin": "((?\\[\\]=]))", + "patterns": [ + { + "name": "meta.head.switch.cpp", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { "1": { - "name": "entity.name.function.cpp" + "name": "punctuation.section.block.begin.bracket.curly.switch.cpp" + } + }, + "patterns": [ + { + "include": "#switch_conditional_parentheses" + }, + { + "include": "$base" + } + ] + }, + { + "name": "meta.body.switch.cpp", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.switch.cpp" + } + }, + "patterns": [ + { + "include": "#default_statement" + }, + { + "include": "#case_statement" + }, + { + "include": "$base" + } + ] + }, + { + "name": "meta.tail.switch.cpp", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "attributes": { + "name": "support.other.attribute.cpp", + "begin": "((?:\\[\\[|__attribute\\(\\(|__attribute__\\(\\(|__declspec\\())", + "beginCaptures": { + "1": { + "name": "punctuation.section.attribute.begin.cpp" + } + }, + "end": "((?:\\]\\]|\\)\\)|\\)))", + "endCaptures": { + "1": { + "name": "punctuation.section.attribute.end.cpp" + } + }, + "patterns": [ + { + "include": "#attributes" + }, + { + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#attributes" + }, + { + "include": "#string_context_c" + } + ] + }, + { + "match": "(using)\\s+((?:,\\w])*>\\s*", + "captures": { + "0": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + } + } + }, + "template_call_range": { + "name": "meta.template.call.cpp", + "begin": "(<)", + "beginCaptures": { + "1": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "template_isolated_definition": { + "match": "(?\\s*$)", + "captures": { + "1": { + "name": "storage.type.template.cpp" + }, + "2": { + "name": "punctuation.section.angle-brackets.start.template.definition.cpp" + }, + "3": { + "name": "meta.template.definition.cpp", + "patterns": [ + { + "include": "#template_definition_context" + } + ] + }, + "4": { + "name": "punctuation.section.angle-brackets.end.template.definition.cpp" + } + } + }, + "template_definition": { + "name": "meta.template.definition.cpp", + "begin": "(?)", + "endCaptures": { + "1": { + "name": "punctuation.section.angle-brackets.end.template.definition.cpp" + } + }, + "patterns": [ + { + "begin": "((?<=\\w)\\s*<)", + "beginCaptures": { + "1": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" } }, - "end": "\\)", + "end": "(>)", + "endCaptures": { + "1": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + { + "include": "#template_definition_context" + } + ] + }, + "template_argument_defaulted": { + "match": "(?<=<|,)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*([=])", + "captures": { + "1": { + "name": "storage.type.template.cpp" + }, + "2": { + "name": "entity.name.type.template.cpp" + }, + "3": { + "name": "keyword.operator.assignment.cpp" + } + } + }, + "template_definition_argument": { + "match": "(?:(?:\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*))\\s*(?:(,)|(?=>|$))", + "captures": { + "1": { + "name": "storage.type.template.argument.$1.cpp" + }, + "2": { + "name": "storage.type.template.argument.$2.cpp" + }, + "3": { + "name": "entity.name.type.template.cpp" + }, + "4": { + "name": "storage.type.template.cpp" + }, + "5": { + "name": "ellipses.cpp punctuation.vararg-ellipses.template.definition.cpp" + }, + "6": { + "name": "entity.name.type.template.cpp" + }, + "7": { + "name": "comma.cpp punctuation.separator.template.argument.cpp" + } + } + }, + "scope_resolution": { + "match": "((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "2": { + "name": "entity.name.type.namespace.scope-resolution.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp" + } + }, + "name": "meta.scope-resolution.cpp" + }, + "qualified_type": { + "match": "\\s*(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.])", + "captures": { + "0": { + "name": "entity.name.type.cpp meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + } + ] + }, + "1": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "2": { + "name": "meta.scope-resolution.cpp" + }, + "3": { + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "4": { + "name": "entity.name.type.namespace.scope-resolution.cpp" + }, + "5": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "6": { + "name": "punctuation.separator.namespace.access.cpp" + } + } + }, + "type_alias": { + "match": "(using)\\s*(?!namespace)(\\s*(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))\\s*(\\=)\\s*(typename)?\\s*((?:(?-mix:(?:(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))|(.+(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "2": { + "name": "entity.name.function.call.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "4": { + "name": "punctuation.section.arguments.begin.bracket.round.cpp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function_call_context_c" + } + ] + }, + "legacy_function_definition": { + "name": "meta.function.definition.parameters.cpp", + "begin": "(?!(?:(?:::|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\.|\\->|\\+\\+|\\-\\-|\\+|\\-|!|not|~|compl|\\*|&|sizeof|sizeof\\.\\.\\.|new|new\\[\\]|delete|delete\\[\\]|\\.\\*|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|not_eq|&|bitand|\\^|xor|\\||bitor|&&|and|\\|\\||or|\\?:|throw|=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|and_eq|\\^=|xor_eq|\\|=|or_eq|,|alignof|alignas|typeid|noexcept|static_cast|dynamic_cast|const_cast|reinterpret_cast)|(?:throw|while|for|do|if|else|goto|switch|try|catch|return|break|case|continue|default))\\s*\\()((?:(?:(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*|::))+|(?<=operator)(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|new|new\\[\\]|delete|delete\\[\\]|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.cpp" + }, + "2": { + "name": "punctuation.section.parameters.begin.bracket.round.cpp" + } + }, + "end": "(\\)|:)", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#probably_a_parameter" + }, + { + "include": "#function_context_c" + } + ] + }, + "operators": { + "patterns": [ + { + "include": "#sizeof_operator" + }, + { + "include": "#alignof_operator" + }, + { + "include": "#alignas_operator" + }, + { + "include": "#typeid_operator" + }, + { + "include": "#decltype_specifier" + }, + { + "match": "(?>=|\\|=", + "name": "keyword.operator.assignment.compound.bitwise.cpp" + }, + { + "match": "<<|>>", + "name": "keyword.operator.bitwise.shift.cpp" + }, + { + "match": "!=|<=|>=|==|<|>", + "name": "keyword.operator.comparison.cpp" + }, + { + "match": "&&|!|\\|\\|", + "name": "keyword.operator.logical.cpp" + }, + { + "match": "&|\\||\\^|~", + "name": "keyword.operator.cpp" + }, + { + "include": "#assignment_operator" + }, + { + "match": "%|\\*|/|-|\\+", + "name": "keyword.operator.cpp" + }, + { + "begin": "\\?", + "beginCaptures": { + "0": { + "name": "keyword.operator.ternary.cpp" + } + }, + "end": ":", + "applyEndPatternLast": true, "endCaptures": { "0": { - "name": "punctuation.definition.parameters.end.c" + "name": "keyword.operator.ternary.cpp" + } + }, + "patterns": [ + { + "include": "#method_access" + }, + { + "include": "#member_access" + }, + { + "include": "#function_call_c" + }, + { + "include": "$base" + } + ] + } + ] + }, + "function_pointer": { + "begin": "(\\s*(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))\\s*(((?:\\*\\s*)*)((?:\\&\\s*?){0,2})\\s*)(\\()(\\*)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)?\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.type.cpp meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "3": { + "name": "meta.scope-resolution.cpp" + }, + "4": { + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "5": { + "name": "entity.name.type.namespace.scope-resolution.cpp" + }, + "6": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "7": { + "name": "punctuation.separator.namespace.access.cpp" + }, + "9": { + "name": "storage.modifier.pointer.cpp" + }, + "10": { + "name": "storage.modifier.reference.cpp" + }, + "11": { + "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" + }, + "12": { + "name": "punctuation.definition.function.pointer.dereference.cpp" + }, + "13": { + "name": "variable.other.definition.pointer.function.cpp" + }, + "14": { + "name": "punctuation.definition.begin.bracket.square.cpp" + }, + "15": { + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "16": { + "name": "punctuation.definition.end.bracket.square.cpp" + }, + "17": { + "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" + }, + "18": { + "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" + } + }, + "end": "(\\))\\s*(?=[{=,);]|\\n)(?!\\()", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" + } + }, + "patterns": [ + { + "include": "#parameter_struct" + }, + { + "include": "#probably_a_parameter" + }, + { + "include": "#function_context_c" + } + ] + }, + "probably_a_parameter": { + "match": "(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?==))|((?<=\\w |\\*\\/|[&*>\\]\\)]|\\.\\.\\.)\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?=(?:\\[\\]\\s*)?(?:,|\\)))))", + "captures": { + "1": { + "name": "variable.parameter.defaulted.cpp" + }, + "2": { + "name": "variable.parameter.cpp" + } + } + }, + "operator_overload": { + "name": "meta.function.definition.parameters.operator-overload.cpp", + "begin": "(operator)((?:\\s*(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)|\\s+(?:(?:new|new\\[\\]|delete|delete\\[\\])|(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?::)*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:&)?)))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.operator.overload.cpp" + }, + "2": { + "name": "entity.name.operator.overloadee.cpp", + "patterns": [ + { + "include": "#scope_resolution" + } + ] + }, + "3": { + "name": "punctuation.section.parameters.begin.bracket.round.operator-overload.cpp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.operator-overload.cpp" + } + }, + "patterns": [ + { + "include": "#probably_a_parameter" + }, + { + "include": "#function_context_c" + } + ] + }, + "member_access": { + "match": "(?:((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!auto[^(?-mix:\\w)]|void[^(?-mix:\\w)]|char[^(?-mix:\\w)]|short[^(?-mix:\\w)]|int[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|long[^(?-mix:\\w)]|float[^(?-mix:\\w)]|double[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|uint_least64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\b(?!\\())", + "captures": { + "1": { + "name": "variable.language.this.cpp" + }, + "2": { + "name": "variable.other.object.access.cpp" + }, + "3": { + "name": "punctuation.separator.dot-access.cpp" + }, + "4": { + "name": "punctuation.separator.pointer-access.cpp" + }, + "5": { + "patterns": [ + { + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?-mix:(?:(?:(?\\*|->))))", + "name": "variable.other.object.property.cpp" + }, + { + "match": "(?:((?\\*|->)))", + "captures": { + "1": { + "name": "variable.language.this.cpp" + }, + "2": { + "name": "variable.other.object.access.cpp" + }, + "3": { + "name": "punctuation.separator.dot-access.cpp" + }, + "4": { + "name": "punctuation.separator.pointer-access.cpp" + } + } + }, + { + "include": "#member_access" + }, + { + "include": "#method_access" + } + ] + }, + "6": { + "name": "variable.other.property.cpp" + } + } + }, + "method_access": { + "contentName": "meta.function-call.member", + "begin": "(?:((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)(\\()", + "beginCaptures": { + "1": { + "name": "variable.language.this.cpp" + }, + "2": { + "name": "variable.other.object.access.cpp" + }, + "3": { + "name": "punctuation.separator.dot-access.cpp" + }, + "4": { + "name": "punctuation.separator.pointer-access.cpp" + }, + "5": { + "patterns": [ + { + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?-mix:(?:(?:(?\\*|->))))", + "name": "variable.other.object.property.cpp" + }, + { + "match": "(?:((?\\*|->)))", + "captures": { + "1": { + "name": "variable.language.this.cpp" + }, + "2": { + "name": "variable.other.object.access.cpp" + }, + "3": { + "name": "punctuation.separator.dot-access.cpp" + }, + "4": { + "name": "punctuation.separator.pointer-access.cpp" + } + } + }, + { + "include": "#member_access" + }, + { + "include": "#method_access" + } + ] + }, + "6": { + "name": "entity.name.function.member.cpp" + }, + "7": { + "name": "punctuation.section.arguments.begin.bracket.round.function.member.cpp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.function.member.cpp" + } + }, + "patterns": [ + { + "include": "#function_call_context_c" + } + ] + }, + "using_namespace": { + "name": "meta.using-namespace.cpp", + "begin": "(?:,\\w])*>\\s*)))?::)*\\s*))?((?:,\\w])*>\\s*)))?::)*\\s*)\\s*(?:(?:((?\\[\\]=]))", + "patterns": [ + { + "name": "meta.head.namespace.cpp", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.namespace.cpp" + } + } + }, + { + "name": "meta.body.namespace.cpp", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.namespace.cpp" } }, - "name": "meta.function.constructor.cpp", "patterns": [ { "include": "$base" } ] }, + { + "name": "meta.tail.namespace.cpp", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "macro_argument": { + "match": "##(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?!\\w)", + "name": "variable.other.macro.argument.cpp" + }, + "lambdas": { + "begin": "((?:(?<=[^\\s]|^)(?)(.+?(?=\\{|$))?", + "captures": { + "1": { + "name": "punctuation.definition.lambda.return-type.cpp" + }, + "2": { + "name": "storage.type.return-type.lambda.cpp" + } + } + }, + { + "name": "meta.function.definition.body.lambda.cpp", + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.lambda.cpp" + } + }, + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.lambda.cpp" + } + }, + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "pthread_types": { + "match": "(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:((?:,\\w])*>\\s*))?(::)))?\\s*((?\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.enum.cpp", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.enum.cpp" + } + }, + "patterns": [ + { + "include": "$base" + } + ] + }, + { + "name": "meta.body.enum.cpp", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.enum.cpp" + } + }, + "patterns": [ + { + "include": "#enumerator_list" + }, + { + "include": "#comments_context" + }, + { + "include": "#comma" + }, + { + "include": "#semicolon" + } + ] + }, + { + "name": "meta.tail.enum.cpp", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "inhertance_context": { + "patterns": [ + { + "match": ",", + "name": "comma.cpp punctuation.separator.delimiter.inhertance.cpp" + }, + { + "match": "(?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))", + "captures": { + "1": { + "name": "entity.name.type.inherited.cpp" + } + } + } + ] + }, + "class_block": { + "name": "meta.block.class.cpp", + "begin": "((((?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))+)*))?))", + "beginCaptures": { + "1": { + "name": "meta.head.class.cpp" + }, + "3": { + "name": "storage.type.$3.cpp" + }, + "4": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "5": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "6": { + "name": "entity.name.type.$3.cpp" + }, + "7": { + "name": "storage.type.modifier.final.cpp" + }, + "8": { + "name": "colon.cpp punctuation.separator.inhertance.cpp" + }, + "9": { + "patterns": [ + { + "include": "#inhertance_context" + } + ] + } + }, + "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.class.cpp", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.class.cpp" + } + }, + "patterns": [ + { + "include": "#preprocessor_context" + }, + { + "include": "#inhertance_context" + }, + { + "include": "#template_call_range" + }, + { + "include": "#comments_context" + } + ] + }, + { + "name": "meta.body.class.cpp", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.class.cpp" + } + }, + "patterns": [ + { + "include": "#function_pointer" + }, + { + "include": "#constructor_context" + }, + { + "include": "$base" + } + ] + }, + { + "name": "meta.tail.class.cpp", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "struct_block": { + "name": "meta.block.struct.cpp", + "begin": "((((?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))+)*))?))", + "beginCaptures": { + "1": { + "name": "meta.head.struct.cpp" + }, + "3": { + "name": "storage.type.$3.cpp" + }, + "4": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "5": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "6": { + "name": "entity.name.type.$3.cpp" + }, + "7": { + "name": "storage.type.modifier.final.cpp" + }, + "8": { + "name": "colon.cpp punctuation.separator.inhertance.cpp" + }, + "9": { + "patterns": [ + { + "include": "#inhertance_context" + } + ] + } + }, + "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.struct.cpp", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.struct.cpp" + } + }, + "patterns": [ + { + "include": "#preprocessor_context" + }, + { + "include": "#inhertance_context" + }, + { + "include": "#template_call_range" + }, + { + "include": "#comments_context" + } + ] + }, + { + "name": "meta.body.struct.cpp", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.struct.cpp" + } + }, + "patterns": [ + { + "include": "#function_pointer" + }, + { + "include": "#constructor_context" + }, + { + "include": "$base" + } + ] + }, + { + "name": "meta.tail.struct.cpp", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "union_block": { + "name": "meta.block.union.cpp", + "begin": "((((?:,\\w])*>\\s*)))?::)*\\s*)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(?:(?:,\\w])*>\\s*))?(?:::)))?\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?:(?-mix:(?:(?:,\\w])*>\\s*)))?(?![\\w<:.]))))+)*))?))", + "beginCaptures": { + "1": { + "name": "meta.head.union.cpp" + }, + "3": { + "name": "storage.type.$3.cpp" + }, + "4": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "5": { + "patterns": [ + { + "include": "#attributes" + } + ] + }, + "6": { + "name": "entity.name.type.$3.cpp" + }, + "7": { + "name": "storage.type.modifier.final.cpp" + }, + "8": { + "name": "colon.cpp punctuation.separator.inhertance.cpp" + }, + "9": { + "patterns": [ + { + "include": "#inhertance_context" + } + ] + } + }, + "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.union.cpp", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.union.cpp" + } + }, + "patterns": [ + { + "include": "#preprocessor_context" + }, + { + "include": "#inhertance_context" + }, + { + "include": "#template_call_range" + }, + { + "include": "#comments_context" + } + ] + }, + { + "name": "meta.body.union.cpp", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.union.cpp" + } + }, + "patterns": [ + { + "include": "#function_pointer" + }, + { + "include": "#constructor_context" + }, + { + "include": "$base" + } + ] + }, + { + "name": "meta.tail.union.cpp", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + } + ] + }, + "extern_block": { + "name": "meta.block.extern.cpp", + "begin": "((\\bextern)(?=\\s*\\\"))", + "beginCaptures": { + "1": { + "name": "meta.head.extern.cpp" + }, + "2": { + "name": "storage.type.extern.cpp" + } + }, + "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.extern.cpp", + "begin": "\\G ?", + "end": "((?:\\{|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.extern.cpp" + } + }, + "patterns": [ + { + "include": "$base" + } + ] + }, + { + "name": "meta.body.extern.cpp", + "begin": "(?<=\\{)", + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.extern.cpp" + } + }, + "patterns": [ + { + "include": "$base" + } + ] + }, + { + "name": "meta.tail.extern.cpp", + "begin": "(?<=})[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "$base" + } + ] + }, + { + "include": "$base" + } + ] + }, + "hacky_fix_for_stray_directive": { + "match": "(?(?-mix:[a-zA-Z_$][\\w$]*)))\t # macro name\n(?:\n (\\()\n\t(\n\t \\s* \\g \\s*\t\t # first argument\n\t ((,) \\s* \\g \\s*)* # additional arguments\n\t (?:\\.\\.\\.)?\t\t\t# varargs ellipsis?\n\t)\n (\\))\n)?", + "beginCaptures": { + "1": { + "name": "keyword.control.directive.define.cpp" + }, + "2": { + "name": "punctuation.definition.directive.cpp" + }, + "3": { + "name": "entity.name.function.preprocessor.cpp" + }, + "5": { + "name": "punctuation.definition.parameters.begin.cpp" + }, + "6": { + "name": "variable.parameter.preprocessor.cpp" + }, + "8": { + "name": "punctuation.separator.parameters.cpp" + }, + "9": { + "name": "punctuation.definition.parameters.end.cpp" + } + }, + "end": "(?=(?://|/\\*))|(?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.cpp" + } + }, + "name": "string.quoted.other.lt-gt.include.cpp" + } + ] + }, + "meta_preprocessor_line": { + "name": "meta.preprocessor.cpp", + "begin": "^\\s*((#)\\s*line)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.directive.line.cpp" + }, + "2": { + "name": "punctuation.definition.directive.cpp" + } + }, + "end": "(?=(?://|/\\*))|(?|\\[|\\]|=))", - "name": "meta.namespace-block.cpp", - "patterns": [ - { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.definition.scope.cpp" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.definition.scope.cpp" - } - }, - "patterns": [ - { - "include": "#special_block" - }, - { - "include": "#constructor" - }, - { - "include": "$base" - } - ] - }, - { - "include": "$base" - } - ] + "include": "#using_namespace" }, { - "begin": "\\b(class|struct)\\b\\s*([_A-Za-z][_A-Za-z0-9]*\\b)?+(\\s*:\\s*(public|protected|private)\\s*([_A-Za-z][_A-Za-z0-9]*\\b)((\\s*,\\s*(public|protected|private)\\s*[_A-Za-z][_A-Za-z0-9]*\\b)*))?", - "beginCaptures": { - "1": { - "name": "storage.type.cpp" - }, - "2": { - "name": "entity.name.type.cpp" - }, - "4": { - "name": "storage.type.modifier.cpp" - }, - "5": { - "name": "entity.name.type.inherited.cpp" - }, - "6": { - "patterns": [ - { - "match": "(public|protected|private)", - "name": "storage.type.modifier.cpp" - }, - { - "match": "[_A-Za-z][_A-Za-z0-9]*", - "name": "entity.name.type.inherited.cpp" - } - ] - } - }, - "end": "(?<=\\})|(?=(;|\\(|\\)|>|\\[|\\]|=))", - "name": "meta.class-struct-block.cpp", - "patterns": [ - { - "include": "#angle_brackets" - }, - { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.section.block.begin.bracket.curly.cpp" - } - }, - "end": "(\\})(\\s*\\n)?", - "endCaptures": { - "1": { - "name": "punctuation.section.block.end.bracket.curly.cpp" - }, - "2": { - "name": "invalid.illegal.you-forgot-semicolon.cpp" - } - }, - "patterns": [ - { - "include": "#special_block" - }, - { - "include": "#constructor" - }, - { - "include": "$base" - } - ] - }, - { - "include": "$base" - } - ] + "include": "#type_alias" }, { - "begin": "\\b(extern)(?=\\s*\")", - "beginCaptures": { - "1": { - "name": "storage.modifier.cpp" - } - }, - "end": "(?<=\\})|(?=\\w)|(?=\\s*#\\s*endif\\b)", - "name": "meta.extern-block.cpp", - "patterns": [ - { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.section.block.begin.bracket.curly.c" - } - }, - "end": "\\}|(?=\\s*#\\s*endif\\b)", - "endCaptures": { - "0": { - "name": "punctuation.section.block.end.bracket.curly.c" - } - }, - "patterns": [ - { - "include": "#special_block" - }, - { - "include": "$base" - } - ] - }, - { - "include": "$base" - } - ] + "include": "#namespace_block" + }, + { + "include": "#class_block" + }, + { + "include": "#struct_block" + }, + { + "include": "#union_block" + }, + { + "include": "#enum_block" + }, + { + "include": "#extern_block" } ] }, - "strings": { + "string_context": { "patterns": [ { "begin": "(u|u8|U|L)?\"", @@ -413,7 +2997,7 @@ "name": "constant.character.escape.cpp" }, { - "include": "source.c#string_placeholder" + "include": "#string_escapes_context_c" } ] }, @@ -442,6 +3026,1490 @@ "name": "string.quoted.double.raw.cpp" } ] + }, + "block": { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.cpp" + } + }, + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.cpp" + } + }, + "name": "meta.block.cpp", + "patterns": [ + { + "include": "#block_context" + } + ] + }, + "block_context": { + "patterns": [ + { + "include": "#preprocessor_rule_enabled_block" + }, + { + "include": "#preprocessor_rule_disabled_block" + }, + { + "include": "#preprocessor_rule_conditional_block" + }, + { + "include": "#method_access" + }, + { + "include": "#member_access" + }, + { + "include": "#function_call_c" + }, + { + "name": "meta.initialization.cpp", + "begin": "(?x)\n(?:\n (?:\n\t(?=\\s)(?=+!]+ | \\(\\) | \\[\\]))\n)\n\\s*(\\() # opening bracket", + "beginCaptures": { + "1": { + "name": "variable.other.cpp" + }, + "2": { + "name": "punctuation.section.parens.begin.bracket.round.initialization.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.initialization.cpp" + } + }, + "patterns": [ + { + "include": "#function_call_context_c" + } + ] + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.cpp" + } + }, + "end": "}|(?=\\s*#\\s*(?:elif|else|endif)\\b)", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.cpp" + } + }, + "patterns": [ + { + "include": "#block_context" + } + ] + }, + { + "include": "#parentheses_block" + }, + { + "include": "$base" + } + ] + }, + "function_call_c": { + "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas|constexpr|volatile|operator|(?:::)?new|(?:::)?delete)\\s*\\()\n(?=\n(?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*(?-mix:(?:(?-mix:(?:(?:,\\w])*>\\s*)))?)\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "end": "(?<=\\))(?!\\w)", + "name": "meta.function-call.cpp", + "patterns": [ + { + "include": "#function_call_context_c" + } + ] + }, + "comments_context": { + "patterns": [ + { + "captures": { + "1": { + "name": "meta.toc-list.banner.block.cpp" + } + }, + "match": "^/\\* =(\\s*.*?)\\s*= \\*/$\\n?", + "name": "comment.block.cpp" + }, + { + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.cpp" + } + }, + "end": "\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.cpp" + } + }, + "name": "comment.block.cpp" + }, + { + "captures": { + "1": { + "name": "meta.toc-list.banner.line.cpp" + } + }, + "match": "^// =(\\s*.*?)\\s*=\\s*$\\n?", + "name": "comment.line.banner.cpp" + }, + { + "begin": "(^[ \\t]+)?(?=//)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.cpp" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "//", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.cpp" + } + }, + "end": "(?=\\n)", + "name": "comment.line.double-slash.cpp", + "patterns": [ + { + "include": "#line_continuation_character" + } + ] + } + ] + } + ] + }, + "disabled": { + "begin": "^\\s*#\\s*if(n?def)?\\b.*$", + "end": "^\\s*#\\s*endif\\b", + "patterns": [ + { + "include": "#disabled" + }, + { + "include": "#pragma_mark" + } + ] + }, + "line_continuation_character": { + "match": "(\\\\)\\n", + "captures": { + "1": { + "name": "constant.character.escape.line-continuation.cpp" + } + } + }, + "parentheses": { + "name": "meta.parens.cpp", + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "$base" + } + ] + }, + "parentheses_block": { + "name": "meta.parens.block.cpp", + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#block_context" + }, + { + "match": "(?-mix:(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "end": "(?<=\\))(?!\\w)|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.cpp" + }, + "2": { + "name": "punctuation.section.arguments.begin.bracket.round.cpp" + } + }, + "end": "(\\))|(?:,\\w])*>\\s*)))?)) # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.operator.wordlike.cpp memory.cpp keyword.operator.new.cpp" + }, + "2": { + "patterns": [ + { + "include": "#template_call_innards" + } + ] + }, + "3": { + "name": "punctuation.section.arguments.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function_call_context_c" + } + ] + }, + { + "include": "#function_call" + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.parens.begin.bracket.round.cpp" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.parens.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function_call_context_c" + } + ] + }, + { + "include": "#block_context" + } + ] } } } \ No newline at end of file diff --git a/extensions/cpp/test/colorize-results/test-23630_cpp.json b/extensions/cpp/test/colorize-results/test-23630_cpp.json index f22786a105f..a58961ae945 100644 --- a/extensions/cpp/test/colorize-results/test-23630_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23630_cpp.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "ifndef", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -45,7 +45,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.c", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -56,7 +56,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -67,7 +67,7 @@ }, { "c": "define", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -78,7 +78,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.c", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -89,7 +89,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.macro.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -100,7 +100,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -111,7 +111,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", diff --git a/extensions/cpp/test/colorize-results/test-23850_cpp.json b/extensions/cpp/test/colorize-results/test-23850_cpp.json index bbb5237498f..924bbc78243 100644 --- a/extensions/cpp/test/colorize-results/test-23850_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23850_cpp.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "ifndef", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -45,7 +45,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -56,7 +56,7 @@ }, { "c": "define", - "t": "source.cpp meta.preprocessor.macro.c keyword.control.directive.define.c", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -67,7 +67,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.c", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -78,7 +78,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.macro.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -89,7 +89,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -100,7 +100,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", diff --git a/extensions/cpp/test/colorize-results/test_c.json b/extensions/cpp/test/colorize-results/test_c.json index 0725010d8c2..74be734c5ca 100644 --- a/extensions/cpp/test/colorize-results/test_c.json +++ b/extensions/cpp/test/colorize-results/test_c.json @@ -243,7 +243,7 @@ }, { "c": "int", - "t": "source.c storage.type.c", + "t": "source.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -265,7 +265,7 @@ }, { "c": "main", - "t": "source.c meta.function.c entity.name.function.c", + "t": "source.c meta.function.c meta.function.definition.parameters.c entity.name.function.c", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -276,7 +276,7 @@ }, { "c": "(", - "t": "source.c meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.c meta.function.c meta.function.definition.parameters.c punctuation.section.parameters.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -287,7 +287,7 @@ }, { "c": ")", - "t": "source.c meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.c meta.function.c meta.function.definition.parameters.c punctuation.section.parameters.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -320,7 +320,7 @@ }, { "c": "float", - "t": "source.c meta.block.c storage.type.c", + "t": "source.c meta.block.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -507,7 +507,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,18 +518,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -540,7 +540,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -551,7 +551,7 @@ }, { "c": "Enter coefficients a, b and c: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -562,7 +562,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -573,7 +573,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +595,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -606,18 +606,18 @@ }, { "c": "scanf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -628,7 +628,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -639,7 +639,7 @@ }, { "c": "%f%f%f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -650,7 +650,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -661,7 +661,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -672,7 +672,7 @@ }, { "c": "&", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -683,7 +683,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -694,7 +694,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -705,7 +705,7 @@ }, { "c": "&", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -716,7 +716,7 @@ }, { "c": "b", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -727,7 +727,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -738,7 +738,7 @@ }, { "c": "&", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -749,7 +749,7 @@ }, { "c": "c", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -760,7 +760,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +848,7 @@ }, { "c": "4", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -947,7 +947,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -958,7 +958,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -969,7 +969,7 @@ }, { "c": ">", - "t": "source.c meta.block.c keyword.operator.comparison.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.comparison.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -980,7 +980,7 @@ }, { "c": "0", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -991,7 +991,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1057,7 +1057,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1068,7 +1068,7 @@ }, { "c": "-", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1079,7 +1079,7 @@ }, { "c": "b", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1090,7 +1090,7 @@ }, { "c": "+", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1101,18 +1101,18 @@ }, { "c": "sqrt", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1123,7 +1123,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1133,8 +1133,19 @@ } }, { - "c": "))", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "c": ")", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1156,7 +1167,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1167,7 +1178,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1178,7 +1189,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1189,7 +1200,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1200,7 +1211,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1255,7 +1266,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1266,7 +1277,7 @@ }, { "c": "-", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1277,7 +1288,7 @@ }, { "c": "b", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,7 +1299,7 @@ }, { "c": "-", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1299,18 +1310,18 @@ }, { "c": "sqrt", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1321,7 +1332,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1331,8 +1342,19 @@ } }, { - "c": "))", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "c": ")", + "t": "source.c meta.block.c meta.parens.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1354,7 +1376,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1365,7 +1387,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1376,7 +1398,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1387,7 +1409,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1398,7 +1420,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1420,7 +1442,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1431,18 +1453,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1453,7 +1475,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1464,7 +1486,7 @@ }, { "c": "Roots are: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1475,7 +1497,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1486,7 +1508,7 @@ }, { "c": " and ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1497,7 +1519,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1508,7 +1530,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1519,7 +1541,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1530,7 +1552,7 @@ }, { "c": "r1 ", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1541,7 +1563,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1552,7 +1574,7 @@ }, { "c": " r2", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1563,7 +1585,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1662,7 +1684,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1673,7 +1695,7 @@ }, { "c": "determinant", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1684,7 +1706,7 @@ }, { "c": "==", - "t": "source.c meta.block.c keyword.operator.comparison.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.comparison.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1695,7 +1717,7 @@ }, { "c": "0", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1706,7 +1728,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1827,7 +1849,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1838,7 +1860,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1849,7 +1871,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1860,7 +1882,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1871,7 +1893,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1893,7 +1915,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1904,18 +1926,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1926,7 +1948,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1937,7 +1959,7 @@ }, { "c": "Roots are: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1948,7 +1970,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1959,7 +1981,7 @@ }, { "c": " and ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1970,7 +1992,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1981,7 +2003,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1992,7 +2014,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2003,7 +2025,7 @@ }, { "c": " r1", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2014,7 +2036,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2025,7 +2047,7 @@ }, { "c": " r2", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2036,7 +2058,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2190,7 +2212,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2201,7 +2223,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2212,7 +2234,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2223,7 +2245,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2234,7 +2256,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2278,50 +2300,6 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "sqrt", - "t": "source.c meta.block.c support.function.C99.c", - "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "-", - "t": "source.c meta.block.c keyword.operator.c", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "determinant", "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", @@ -2331,9 +2309,53 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "sqrt", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", + "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.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "-", + "t": "source.c meta.block.c meta.function-call.c keyword.operator.c", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "determinant", + "t": "source.c meta.block.c meta.function-call.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2355,7 +2377,7 @@ }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2366,7 +2388,7 @@ }, { "c": "2", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2377,7 +2399,7 @@ }, { "c": "*", - "t": "source.c meta.block.c keyword.operator.c", + "t": "source.c meta.block.c meta.parens.block.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2388,7 +2410,7 @@ }, { "c": "a", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2399,7 +2421,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2421,7 +2443,7 @@ }, { "c": " ", - "t": "source.c meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.c meta.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2432,18 +2454,18 @@ }, { "c": "printf", - "t": "source.c meta.block.c support.function.C99.c", + "t": "source.c meta.block.c meta.function-call.c entity.name.function.c", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.c meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2454,7 +2476,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.begin.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.begin.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2465,7 +2487,7 @@ }, { "c": "Roots are: ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2476,7 +2498,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2487,7 +2509,7 @@ }, { "c": "+", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2498,7 +2520,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2509,7 +2531,7 @@ }, { "c": "i and ", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2520,7 +2542,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2531,7 +2553,7 @@ }, { "c": "-", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2542,7 +2564,7 @@ }, { "c": "%.2f", - "t": "source.c meta.block.c string.quoted.double.c constant.other.placeholder.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c constant.other.placeholder.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2553,7 +2575,7 @@ }, { "c": "i", - "t": "source.c meta.block.c string.quoted.double.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2564,7 +2586,7 @@ }, { "c": "\"", - "t": "source.c meta.block.c string.quoted.double.c punctuation.definition.string.end.c", + "t": "source.c meta.block.c meta.function-call.c string.quoted.double.c punctuation.definition.string.end.c", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2575,7 +2597,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2586,7 +2608,7 @@ }, { "c": " real", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2597,7 +2619,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2608,7 +2630,7 @@ }, { "c": " imag", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2619,7 +2641,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2630,7 +2652,7 @@ }, { "c": " real", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2641,7 +2663,7 @@ }, { "c": ",", - "t": "source.c meta.block.c punctuation.separator.delimiter.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2652,7 +2674,7 @@ }, { "c": " imag", - "t": "source.c meta.block.c", + "t": "source.c meta.block.c meta.function-call.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2663,7 +2685,7 @@ }, { "c": ")", - "t": "source.c meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.c meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2740,7 +2762,7 @@ }, { "c": "0", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json index f3f72320fb5..e53e3ede6e4 100644 --- a/extensions/cpp/test/colorize-results/test_cc.json +++ b/extensions/cpp/test/colorize-results/test_cc.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "if", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "B4G_DEBUG_CHECK", - "t": "source.cpp meta.preprocessor.c entity.name.function.preprocessor.c", + "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -56,7 +56,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -67,7 +67,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -78,7 +78,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -89,7 +89,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -100,7 +100,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -111,7 +111,7 @@ }, { "c": "num_candidate_ret=", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -122,7 +122,7 @@ }, { "c": "%d", - "t": "source.cpp meta.function.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -133,7 +133,7 @@ }, { "c": ":", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -144,7 +144,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -155,7 +155,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -166,7 +166,7 @@ }, { "c": " num_candidate", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,7 +177,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -188,7 +188,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,7 +210,7 @@ }, { "c": "for", - "t": "source.cpp keyword.control.c", + "t": "source.cpp keyword.control.for.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -221,7 +221,7 @@ }, { "c": "(", - "t": "source.cpp punctuation.section.parens.begin.bracket.round.c", + "t": "source.cpp meta.parens.cpp punctuation.section.parens.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -232,7 +232,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.c", + "t": "source.cpp meta.parens.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -243,7 +243,7 @@ }, { "c": " i", - "t": "source.cpp", + "t": "source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -254,7 +254,7 @@ }, { "c": "=", - "t": "source.cpp keyword.operator.assignment.c", + "t": "source.cpp meta.parens.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -265,7 +265,7 @@ }, { "c": "0", - "t": "source.cpp constant.numeric.c", + "t": "source.cpp meta.parens.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -276,7 +276,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp meta.parens.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -287,7 +287,7 @@ }, { "c": "i", - "t": "source.cpp", + "t": "source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -298,7 +298,7 @@ }, { "c": "<", - "t": "source.cpp keyword.operator.comparison.c", + "t": "source.cpp meta.parens.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -309,7 +309,7 @@ }, { "c": "num_candidate", - "t": "source.cpp", + "t": "source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -320,7 +320,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp meta.parens.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -331,7 +331,7 @@ }, { "c": "++", - "t": "source.cpp keyword.operator.increment.c", + "t": "source.cpp meta.parens.cpp keyword.operator.increment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -342,7 +342,7 @@ }, { "c": "i", - "t": "source.cpp", + "t": "source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -353,7 +353,7 @@ }, { "c": ")", - "t": "source.cpp punctuation.section.parens.end.bracket.round.c", + "t": "source.cpp meta.parens.cpp punctuation.section.parens.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -375,7 +375,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -386,7 +386,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -397,7 +397,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -408,7 +408,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +419,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -430,7 +430,7 @@ }, { "c": "%d", - "t": "source.cpp meta.function.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -441,7 +441,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -452,7 +452,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -463,7 +463,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -474,18 +474,18 @@ }, { "c": "user_candidate", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp variable.other.object.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": "[", - "t": "source.cpp meta.function.c punctuation.definition.begin.bracket.square.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp punctuation.definition.begin.bracket.square.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +496,7 @@ }, { "c": "i", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "]", - "t": "source.cpp meta.function.c punctuation.definition.end.bracket.square.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp punctuation.definition.end.bracket.square.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,7 +518,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -529,7 +529,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -551,7 +551,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -562,7 +562,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -573,7 +573,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -584,7 +584,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +595,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -606,7 +606,7 @@ }, { "c": ";", - "t": "source.cpp meta.function.c string.quoted.double.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -617,7 +617,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -628,7 +628,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -639,7 +639,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -650,7 +650,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -661,7 +661,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.c keyword.control.directive.conditional.c", + "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -683,7 +683,7 @@ }, { "c": "void", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -705,7 +705,7 @@ }, { "c": "main", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -716,7 +716,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -726,8 +726,8 @@ } }, { - "c": "O obj", - "t": "source.cpp meta.function.c", + "c": "O ", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -736,9 +736,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "obj", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -760,7 +771,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -771,7 +782,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -782,7 +793,7 @@ }, { "c": "LOG_INFO", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -793,7 +804,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -804,7 +815,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -815,7 +826,7 @@ }, { "c": "not hilighted as string", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -826,7 +837,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -837,7 +848,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +859,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -859,7 +870,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -870,7 +881,7 @@ }, { "c": "LOG_INFO", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -881,7 +892,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -892,7 +903,7 @@ }, { "c": "obj ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -903,7 +914,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.c meta.function-call.c keyword.operator.bitwise.shift.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -914,7 +925,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +936,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -936,7 +947,7 @@ }, { "c": ", even worse; ", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -947,7 +958,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -958,7 +969,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -969,7 +980,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.c meta.function-call.c keyword.operator.bitwise.shift.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -979,8 +990,8 @@ } }, { - "c": " obj", - "t": "source.cpp meta.block.c meta.function-call.c", + "c": " ", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -989,9 +1000,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "obj", + "t": "source.cpp meta.block.cpp meta.function-call.cpp variable.other.object.access.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ".", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.separator.dot-access.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.separator.dot-access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1002,7 +1024,7 @@ }, { "c": "x", - "t": "source.cpp meta.block.c meta.function-call.c variable.other.member.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp variable.other.property.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1013,7 +1035,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1024,7 +1046,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.c meta.function-call.c keyword.operator.bitwise.shift.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1035,7 +1057,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1046,7 +1068,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1057,7 +1079,7 @@ }, { "c": " check this out.", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1068,7 +1090,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1079,7 +1101,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1090,7 +1112,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1101,7 +1123,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1112,7 +1134,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1123,7 +1145,7 @@ }, { "c": " everything from this point on is interpeted as a string literal...", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1134,7 +1156,7 @@ }, { "c": " O x", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1145,7 +1167,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1155,8 +1177,8 @@ } }, { - "c": " std", - "t": "source.cpp meta.block.c", + "c": " ", + "t": "source.cpp meta.block.cpp meta.scope-resolution.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1165,9 +1187,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "std", + "t": "source.cpp meta.block.cpp meta.scope-resolution.cpp entity.name.type.namespace.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, { "c": "::", - "t": "source.cpp meta.block.c punctuation.separator.namespace.access.cpp", + "t": "source.cpp meta.block.cpp meta.scope-resolution.cpp punctuation.separator.namespace.access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1178,7 +1211,7 @@ }, { "c": "unique_ptr", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1189,7 +1222,7 @@ }, { "c": "<", - "t": "source.cpp meta.block.c keyword.operator.comparison.c", + "t": "source.cpp meta.block.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1200,7 +1233,7 @@ }, { "c": "O", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1211,7 +1244,7 @@ }, { "c": ">", - "t": "source.cpp meta.block.c keyword.operator.comparison.c", + "t": "source.cpp meta.block.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1222,7 +1255,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1233,7 +1266,7 @@ }, { "c": "o", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1244,7 +1277,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1255,18 +1288,18 @@ }, { "c": "new", - "t": "source.cpp meta.block.c meta.function-call.c keyword.control.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.wordlike.cpp alias.cpp keyword.operator.new.cpp", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", - "dark_vs": "keyword.control: #569CD6", - "light_vs": "keyword.control: #0000FF", - "hc_black": "keyword.control: #C586C0" + "dark_plus": "keyword.operator.new.cpp: #C586C0", + "light_plus": "keyword.operator.new.cpp: #AF00DB", + "dark_vs": "keyword.operator.new: #569CD6", + "light_vs": "keyword.operator.new: #0000FF", + "hc_black": "keyword.operator.new.cpp: #C586C0" } }, { "c": " O", - "t": "source.cpp meta.block.c meta.function-call.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1277,7 +1310,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,7 +1321,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1299,7 +1332,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1310,7 +1343,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1321,7 +1354,7 @@ }, { "c": " sadness.", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1332,7 +1365,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1343,18 +1376,18 @@ }, { "c": "sprintf", - "t": "source.cpp meta.block.c support.function.C99.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": "(", - "t": "source.cpp meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1365,7 +1398,7 @@ }, { "c": "options", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1376,7 +1409,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1387,7 +1420,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1398,7 +1431,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1409,7 +1442,7 @@ }, { "c": "STYLE=Keramik;TITLE=", - "t": "source.cpp meta.block.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1420,7 +1453,7 @@ }, { "c": "%s", - "t": "source.cpp meta.block.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1431,7 +1464,7 @@ }, { "c": ";THEME=", - "t": "source.cpp meta.block.c string.quoted.double.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1442,7 +1475,7 @@ }, { "c": "%s", - "t": "source.cpp meta.block.c string.quoted.double.cpp constant.other.placeholder.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1453,7 +1486,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1464,7 +1497,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1475,7 +1508,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1486,7 +1519,7 @@ }, { "c": "...", - "t": "source.cpp meta.block.c punctuation.separator.dot-access.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.vararg-ellipses.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1497,7 +1530,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1508,7 +1541,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1519,7 +1552,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1530,7 +1563,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1552,7 +1585,7 @@ }, { "c": "main2", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1563,7 +1596,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1574,7 +1607,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1596,7 +1629,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1607,7 +1640,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.support.function.leading.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1618,260 +1651,7 @@ }, { "c": "printf", - "t": "source.cpp meta.block.c support.function.C99.c", - "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" - } - }, - { - "c": "(", - "t": "source.cpp meta.block.c punctuation.section.parens.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.end.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c punctuation.section.parens.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", - "r": { - "dark_plus": "comment: #6A9955", - "light_plus": "comment: #008000", - "dark_vs": "comment: #6A9955", - "light_vs": "comment: #008000", - "hc_black": "comment: #7CA668" - } - }, - { - "c": " the rest of", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", - "r": { - "dark_plus": "comment: #6A9955", - "light_plus": "comment: #008000", - "dark_vs": "comment: #6A9955", - "light_vs": "comment: #008000", - "hc_black": "comment: #7CA668" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "asm", - "t": "source.cpp meta.block.c meta.function-call.c storage.type.c", - "r": { - "dark_plus": "storage.type: #569CD6", - "light_plus": "storage.type: #0000FF", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type: #569CD6" - } - }, - { - "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.parens.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "movw $0x38, ", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "%a", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp constant.other.placeholder.c", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "x; ltr ", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "%a", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp constant.other.placeholder.c", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "x", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.parens.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "fn", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1882,7 +1662,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1893,7 +1673,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1903,8 +1683,8 @@ } }, { - "c": "{};", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp", + "c": ";", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1915,7 +1695,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.c meta.function-call.c string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1926,7 +1706,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1937,7 +1717,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1948,7 +1728,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1959,7 +1739,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1970,7 +1750,150 @@ }, { "c": " the rest of", - "t": "source.cpp meta.block.c comment.line.double-slash.cpp", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "asm(\"movw $0x38, %ax; ltr %ax\");", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fn", + "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "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.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "{};", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "//", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " the rest of", + "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1981,7 +1904,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index b3c9a841cc4..9702dc50b0b 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -23,7 +23,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.include.c keyword.control.directive.include.c punctuation.definition.directive.c", + "t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -34,7 +34,7 @@ }, { "c": "include", - "t": "source.cpp meta.preprocessor.include.c keyword.control.directive.include.c", + "t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -45,7 +45,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.include.c", + "t": "source.cpp meta.preprocessor.include.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -56,7 +56,7 @@ }, { "c": "<", - "t": "source.cpp meta.preprocessor.include.c string.quoted.other.lt-gt.include.c punctuation.definition.string.begin.c", + "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -67,7 +67,7 @@ }, { "c": "iostream", - "t": "source.cpp meta.preprocessor.include.c string.quoted.other.lt-gt.include.c", + "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -78,7 +78,7 @@ }, { "c": ">", - "t": "source.cpp meta.preprocessor.include.c string.quoted.other.lt-gt.include.c punctuation.definition.string.end.c", + "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -89,18 +89,18 @@ }, { "c": "using", - "t": "source.cpp meta.using-namespace-declaration.cpp keyword.control.cpp", + "t": "source.cpp meta.using-namespace.cpp keyword.other.using.directive.cpp", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", - "dark_vs": "keyword.control: #569CD6", - "light_vs": "keyword.control: #0000FF", - "hc_black": "keyword.control: #C586C0" + "dark_plus": "keyword.other.using: #C586C0", + "light_plus": "keyword.other.using: #AF00DB", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword.other.using: #C586C0" } }, { "c": " ", - "t": "source.cpp meta.using-namespace-declaration.cpp", + "t": "source.cpp meta.using-namespace.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -111,7 +111,7 @@ }, { "c": "namespace", - "t": "source.cpp meta.using-namespace-declaration.cpp storage.type.cpp", + "t": "source.cpp meta.using-namespace.cpp keyword.other.namespace.directive.cpp storage.type.namespace.directive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -122,7 +122,7 @@ }, { "c": " ", - "t": "source.cpp meta.using-namespace-declaration.cpp", + "t": "source.cpp meta.using-namespace.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -133,7 +133,7 @@ }, { "c": "std", - "t": "source.cpp meta.using-namespace-declaration.cpp entity.name.type.cpp", + "t": "source.cpp meta.using-namespace.cpp entity.name.type.namespace.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -144,7 +144,7 @@ }, { "c": ";", - "t": "source.cpp meta.using-namespace-declaration.cpp", + "t": "source.cpp meta.using-namespace.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "class", - "t": "source.cpp meta.class-struct-block.cpp storage.type.cpp", + "t": "source.cpp meta.block.class.cpp meta.head.class.cpp storage.type.class.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -166,7 +166,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.head.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,7 +177,7 @@ }, { "c": "Rectangle", - "t": "source.cpp meta.class-struct-block.cpp entity.name.type.cpp", + "t": "source.cpp meta.block.class.cpp meta.head.class.cpp entity.name.type.class.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -188,7 +188,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.head.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -199,7 +199,7 @@ }, { "c": "{", - "t": "source.cpp meta.class-struct-block.cpp punctuation.section.block.begin.bracket.curly.cpp", + "t": "source.cpp meta.block.class.cpp meta.head.class.cpp punctuation.section.block.begin.bracket.curly.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,7 +210,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -221,7 +221,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp storage.type.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -232,7 +232,7 @@ }, { "c": " width", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -243,7 +243,7 @@ }, { "c": ",", - "t": "source.cpp meta.class-struct-block.cpp punctuation.separator.delimiter.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -254,7 +254,7 @@ }, { "c": " height", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -265,7 +265,7 @@ }, { "c": ";", - "t": "source.cpp meta.class-struct-block.cpp punctuation.terminator.statement.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -276,7 +276,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -286,19 +286,30 @@ } }, { - "c": "public:", - "t": "source.cpp meta.class-struct-block.cpp storage.modifier.cpp", + "c": "public", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public.cpp", "r": { - "dark_plus": "storage.modifier: #569CD6", - "light_plus": "storage.modifier: #0000FF", - "dark_vs": "storage.modifier: #569CD6", - "light_vs": "storage.modifier: #0000FF", - "hc_black": "storage.modifier: #569CD6" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ":", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public.cpp colon.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" } }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +320,7 @@ }, { "c": "void", - "t": "source.cpp meta.class-struct-block.cpp storage.type.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -320,7 +331,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -331,7 +342,7 @@ }, { "c": "set_values", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -342,7 +353,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -353,7 +364,7 @@ }, { "c": "(", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -364,7 +375,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -375,7 +386,7 @@ }, { "c": ",", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -386,7 +397,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -397,7 +408,7 @@ }, { "c": ")", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -408,7 +419,7 @@ }, { "c": ";", - "t": "source.cpp meta.class-struct-block.cpp punctuation.terminator.statement.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +430,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -430,7 +441,7 @@ }, { "c": "int", - "t": "source.cpp meta.class-struct-block.cpp storage.type.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -441,7 +452,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -452,7 +463,7 @@ }, { "c": "area", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -463,7 +474,7 @@ }, { "c": "(", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -474,7 +485,7 @@ }, { "c": ")", - "t": "source.cpp meta.class-struct-block.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -485,7 +496,7 @@ }, { "c": " ", - "t": "source.cpp meta.class-struct-block.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +507,7 @@ }, { "c": "{", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +518,7 @@ }, { "c": "return", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c keyword.control.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp keyword.control.return.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -518,7 +529,7 @@ }, { "c": " width", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -529,7 +540,7 @@ }, { "c": "*", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c keyword.operator.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp keyword.operator.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -540,7 +551,7 @@ }, { "c": "height", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -551,7 +562,7 @@ }, { "c": ";", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -562,7 +573,7 @@ }, { "c": "}", - "t": "source.cpp meta.class-struct-block.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -573,7 +584,7 @@ }, { "c": "}", - "t": "source.cpp meta.class-struct-block.cpp punctuation.section.block.end.bracket.curly.cpp", + "t": "source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.section.block.end.bracket.curly.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -584,7 +595,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.c", + "t": "source.cpp meta.block.class.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +606,7 @@ }, { "c": "void", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -606,7 +617,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp meta.scope-resolution.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -616,8 +627,30 @@ } }, { - "c": "Rectangle::set_values", - "t": "source.cpp meta.function.c entity.name.function.c", + "c": "Rectangle", + "t": "source.cpp meta.scope-resolution.cpp entity.name.type.namespace.scope-resolution.cpp", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "::", + "t": "source.cpp meta.scope-resolution.cpp punctuation.separator.namespace.access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "set_values", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -628,7 +661,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -639,7 +672,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -650,7 +683,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -660,8 +693,8 @@ } }, { - "c": " x", - "t": "source.cpp meta.function.c", + "c": " ", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -670,9 +703,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "x", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ",", - "t": "source.cpp meta.function.c punctuation.separator.delimiter.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -683,7 +727,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -694,7 +738,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.c storage.type.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -704,8 +748,8 @@ } }, { - "c": " y", - "t": "source.cpp meta.function.c", + "c": " ", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -714,9 +758,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "y", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -738,7 +793,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +804,7 @@ }, { "c": " width ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -760,7 +815,7 @@ }, { "c": "=", - "t": "source.cpp meta.block.c keyword.operator.assignment.c", + "t": "source.cpp meta.block.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -771,7 +826,7 @@ }, { "c": " x", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -782,7 +837,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -793,7 +848,7 @@ }, { "c": " height ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -804,7 +859,7 @@ }, { "c": "=", - "t": "source.cpp meta.block.c keyword.operator.assignment.c", + "t": "source.cpp meta.block.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -815,7 +870,7 @@ }, { "c": " y", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -826,7 +881,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -837,7 +892,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +903,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.c", + "t": "source.cpp storage.type.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -870,7 +925,7 @@ }, { "c": "main", - "t": "source.cpp meta.function.c entity.name.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -881,7 +936,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -892,7 +947,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.c punctuation.section.parameters.begin.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -903,7 +958,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.c punctuation.section.parameters.end.bracket.round.c", + "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +980,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -936,7 +991,7 @@ }, { "c": " Rectangle rect", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,271 +1002,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " rect", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ".", - "t": "source.cpp meta.block.c punctuation.separator.dot-access.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "set_values", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", - "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.cpp meta.block.c meta.function-call.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "(", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "3", - "t": "source.cpp meta.block.c meta.function-call.c constant.numeric.c", - "r": { - "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", - "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", - "hc_black": "constant.numeric: #B5CEA8" - } - }, - { - "c": ",", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.separator.delimiter.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "4", - "t": "source.cpp meta.block.c meta.function-call.c constant.numeric.c", - "r": { - "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", - "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", - "hc_black": "constant.numeric: #B5CEA8" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " cout ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<<", - "t": "source.cpp meta.block.c keyword.operator.bitwise.shift.c", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.begin.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "area: ", - "t": "source.cpp meta.block.c string.quoted.double.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\"", - "t": "source.cpp meta.block.c string.quoted.double.cpp punctuation.definition.string.end.cpp", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": " ", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "<<", - "t": "source.cpp meta.block.c keyword.operator.bitwise.shift.c", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": " rect", - "t": "source.cpp meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ".", - "t": "source.cpp meta.block.c punctuation.separator.dot-access.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "area", - "t": "source.cpp meta.block.c meta.function-call.c entity.name.function.c", - "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.cpp meta.block.c meta.function-call.c punctuation.section.arguments.begin.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ")", - "t": "source.cpp meta.block.c meta.function-call.c punctuation.section.arguments.end.bracket.round.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1222,7 +1013,293 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "rect", + "t": "source.cpp meta.block.cpp variable.other.object.access.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.cpp meta.block.cpp punctuation.separator.dot-access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "set_values", + "t": "source.cpp meta.block.cpp variable.other.property.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.cpp meta.block.cpp meta.parens.block.cpp punctuation.section.parens.begin.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "3", + "t": "source.cpp meta.block.cpp meta.parens.block.cpp constant.numeric.decimal.cpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ",", + "t": "source.cpp meta.block.cpp meta.parens.block.cpp comma.cpp punctuation.separator.delimiter.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "4", + "t": "source.cpp meta.block.cpp meta.parens.block.cpp constant.numeric.decimal.cpp", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ")", + "t": "source.cpp meta.block.cpp meta.parens.block.cpp punctuation.section.parens.end.bracket.round.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " cout ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<<", + "t": "source.cpp meta.block.cpp keyword.operator.bitwise.shift.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "area: ", + "t": "source.cpp meta.block.cpp string.quoted.double.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "source.cpp meta.block.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "<<", + "t": "source.cpp meta.block.cpp keyword.operator.bitwise.shift.cpp", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "rect", + "t": "source.cpp meta.block.cpp variable.other.object.access.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.cpp meta.block.cpp punctuation.separator.dot-access.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "area", + "t": "source.cpp meta.block.cpp entity.name.function.member.cpp", + "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.cpp meta.block.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ")", + "t": "source.cpp meta.block.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1233,7 +1310,7 @@ }, { "c": "return", - "t": "source.cpp meta.block.c keyword.control.c", + "t": "source.cpp meta.block.cpp keyword.control.return.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1244,7 +1321,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.c", + "t": "source.cpp meta.block.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1255,7 +1332,7 @@ }, { "c": "0", - "t": "source.cpp meta.block.c constant.numeric.c", + "t": "source.cpp meta.block.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1266,7 +1343,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.c punctuation.terminator.statement.c", + "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1277,7 +1354,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index 58cb2a2cef0..beef7f27b8f 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "b95e4044ff1ac52e03f622de76f459dc5388954c" + "commitHash": "ad7514e8d78542a6ee37f6187091cd4102eb3797" } }, "license": "MIT", @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 31fb9c6b818..4d80bcc364e 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dotnet/csharp-tmLanguage/commit/b95e4044ff1ac52e03f622de76f459dc5388954c", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/ad7514e8d78542a6ee37f6187091cd4102eb3797", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -2150,7 +2150,7 @@ ] }, "local-variable-declaration": { - "begin": "(?x)\n(?:\n (?:(\\bref)\\s+(?:(\\breadonly)\\s+)?)?(\\bvar\\b)| # ref local\n (?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref local\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\n)\\s+\n(\\g)\\s*\n(?=,|;|=|\\))", + "begin": "(?x)\n(?:\n (?:(\\bref)\\s+(?:(\\breadonly)\\s+)?)?(\\bvar\\b)| # ref local\n (?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref local\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s*\\[(?:\\s*,\\s*)*\\]\\s*)* # array suffix?\n )\n )\n)\\s+\n(\\g)\\s*\n(?!=>)\n(?=,|;|=|\\))", "beginCaptures": { "1": { "name": "storage.modifier.cs" diff --git a/extensions/csharp/test/colorize-results/test_cs.json b/extensions/csharp/test/colorize-results/test_cs.json index 1fc73fb341c..dbbe61ef3c6 100644 --- a/extensions/csharp/test/colorize-results/test_cs.json +++ b/extensions/csharp/test/colorize-results/test_cs.json @@ -3,11 +3,11 @@ "c": "using", "t": "source.cs keyword.other.using.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", + "dark_plus": "keyword.other.using: #C586C0", + "light_plus": "keyword.other.using: #AF00DB", "dark_vs": "keyword: #569CD6", "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" + "hc_black": "keyword.other.using: #C586C0" } }, { diff --git a/extensions/css-language-features/.vscode/launch.json b/extensions/css-language-features/.vscode/launch.json index d6393141c5d..4f7166e6dce 100644 --- a/extensions/css-language-features/.vscode/launch.json +++ b/extensions/css-language-features/.vscode/launch.json @@ -23,8 +23,7 @@ "outFiles": [ "${workspaceFolder}/client/out/**/*.js" ], - "smartStep": true, - "preLaunchTask": "npm: compile" + "smartStep": true }, { "name": "Launch Tests", @@ -39,8 +38,7 @@ "sourceMaps": true, "outFiles": [ "${workspaceFolder}/client/out/test/**/*.js" - ], - "preLaunchTask": "npm: compile" + ] }, { "name": "Attach Language Server", diff --git a/extensions/css-language-features/.vscode/settings.json b/extensions/css-language-features/.vscode/settings.json new file mode 100644 index 00000000000..5b30e2983c2 --- /dev/null +++ b/extensions/css-language-features/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "prettier.semi": true, + "prettier.singleQuote": true, + "prettier.printWidth": 120, +} \ No newline at end of file diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 6e0f2eb77c7..8d8542f5a1e 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -9,8 +9,9 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString } from 'vscode'; +import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString, workspace, TextDocument, SelectionRange } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable } from 'vscode-languageclient'; +import { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; // this method is called when vs code is activated export function activate(context: ExtensionContext) { @@ -30,6 +31,11 @@ export function activate(context: ExtensionContext) { let documentSelector = ['css', 'scss', 'less']; + let dataPaths = [ + ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), + ...getCustomDataPathsFromAllExtensions() + ]; + // Options to control the language client let clientOptions: LanguageClientOptions = { documentSelector, @@ -37,6 +43,7 @@ export function activate(context: ExtensionContext) { configurationSection: ['css', 'scss', 'less'] }, initializationOptions: { + dataPaths } }; @@ -71,6 +78,26 @@ export function activate(context: ExtensionContext) { client.onReady().then(() => { context.subscriptions.push(initCompletionProvider()); + + documentSelector.forEach(selector => { + context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { + async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); + if (Array.isArray(rawResult)) { + return rawResult.map(rawSelectionRanges => { + return rawSelectionRanges.reduceRight((parent: SelectionRange | undefined, selectionRange: SelectionRange) => { + return { + range: client.protocol2CodeConverter.asRange(selectionRange.range), + parent + }; + }, undefined)!; + }); + } + return []; + } + })); + }); }); function initCompletionProvider(): Disposable { diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts new file mode 100644 index 00000000000..b812b133ddc --- /dev/null +++ b/extensions/css-language-features/client/src/customData.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { workspace, WorkspaceFolder, extensions } from 'vscode'; + +interface ExperimentalConfig { + experimental?: { + customData?: string[]; + }; +} + +export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined): string[] { + const dataPaths: string[] = []; + + if (!workspaceFolders) { + return dataPaths; + } + + workspaceFolders.forEach(wf => { + const allCssConfig = workspace.getConfiguration(undefined, wf.uri); + const wfCSSConfig = allCssConfig.inspect('css'); + if ( + wfCSSConfig && + wfCSSConfig.workspaceFolderValue && + wfCSSConfig.workspaceFolderValue.experimental && + wfCSSConfig.workspaceFolderValue.experimental.customData + ) { + const customData = wfCSSConfig.workspaceFolderValue.experimental.customData; + if (Array.isArray(customData)) { + customData.forEach(t => { + if (typeof t === 'string') { + dataPaths.push(path.resolve(wf.uri.fsPath, t)); + } + }); + } + } + }); + + return dataPaths; +} + +export function getCustomDataPathsFromAllExtensions(): string[] { + const dataPaths: string[] = []; + + for (const extension of extensions.all) { + const contributes = extension.packageJSON && extension.packageJSON.contributes; + + if (contributes && contributes.css && contributes.css.experimental.customData && Array.isArray(contributes.css.experimental.customData)) { + const relativePaths: string[] = contributes.css.experimental.customData; + relativePaths.forEach(rp => { + dataPaths.push(path.resolve(extension.extensionPath, rp)); + }); + } + } + + return dataPaths; +} diff --git a/extensions/css-language-features/client/src/typings/ref.d.ts b/extensions/css-language-features/client/src/typings/ref.d.ts index 9c1a5df18ed..de602d3f8d6 100644 --- a/extensions/css-language-features/client/src/typings/ref.d.ts +++ b/extensions/css-language-features/client/src/typings/ref.d.ts @@ -2,5 +2,5 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - /// +/// diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 3e5f4cf62e5..0b2e83c1d8d 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -5,7 +5,7 @@ "version": "1.0.0", "publisher": "vscode", "engines": { - "vscode": "0.10.x" + "vscode": "^1.29.0" }, "icon": "icons/css.png", "activationEvents": [ @@ -15,6 +15,7 @@ "onCommand:_css.applyCodeAction" ], "main": "./client/out/cssMain", + "enableProposedApi": true, "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", @@ -32,19 +33,27 @@ "id": "css", "title": "%css.title%", "properties": { + "css.experimental.customData": { + "type": "array", + "description": "A list of JSON file paths that define custom CSS data that loads custom properties, at directives, pseudo classes / elements.", + "default": [], + "items": { + "type": "string" + }, + "scope": "resource" + }, + "css.completion.triggerPropertyValueCompletion": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%css.completion.triggerPropertyValueCompletion.desc%" + }, "css.validate": { "type": "boolean", "scope": "resource", "default": true, "description": "%css.validate.desc%" }, - "css.colorDecorators.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%css.colorDecorators.enable.desc%", - "deprecationMessage": "%css.colorDecorators.enable.deprecationMessage%" - }, "css.lint.compatibleVendorPrefixes": { "type": "string", "scope": "resource", @@ -288,13 +297,6 @@ "default": true, "description": "%scss.validate.desc%" }, - "scss.colorDecorators.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%scss.colorDecorators.enable.desc%", - "deprecationMessage": "%scss.colorDecorators.enable.deprecationMessage%" - }, "scss.lint.compatibleVendorPrefixes": { "type": "string", "scope": "resource", @@ -517,13 +519,6 @@ "default": true, "description": "%less.validate.desc%" }, - "less.colorDecorators.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%less.colorDecorators.enable.desc%", - "deprecationMessage": "%less.colorDecorators.enable.deprecationMessage%" - }, "less.lint.compatibleVendorPrefixes": { "type": "string", "scope": "resource", @@ -737,11 +732,11 @@ ] }, "dependencies": { - "vscode-languageclient": "^5.1.0", + "vscode-languageclient": "^5.2.1", "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^8.10.25", + "@types/node": "^10.12.21", "mocha": "^5.2.0" } } diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index 56eeb384643..3e890bfd9bd 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -2,6 +2,7 @@ "displayName": "CSS Language Features", "description": "Provides rich language support for CSS, LESS and SCSS files.", "css.title": "CSS", + "css.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", "css.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.", @@ -69,10 +70,7 @@ "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.", "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`." -} \ No newline at end of file +} diff --git a/extensions/css-language-features/server/extension.webpack.config.js b/extensions/css-language-features/server/extension.webpack.config.js index 17dc2d39e34..68b850b3773 100644 --- a/extensions/css-language-features/server/extension.webpack.config.js +++ b/extensions/css-language-features/server/extension.webpack.config.js @@ -9,7 +9,6 @@ const withDefaults = require('../../shared.webpack.config'); const path = require('path'); -var webpack = require('webpack'); module.exports = withDefaults({ context: path.join(__dirname), @@ -19,12 +18,5 @@ module.exports = withDefaults({ output: { filename: 'cssServerMain.js', path: path.join(__dirname, 'dist') - }, - plugins: [ - new webpack.NormalModuleReplacementPlugin( - /[/\\]vscode-languageserver[/\\]lib[/\\]files\.js/, - require.resolve('./build/filesFillIn') - ), - new webpack.IgnorePlugin(/vertx/) - ], + } }); diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 28f60c966d7..e3fd51ae5ec 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,12 +9,12 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.13-next.3", - "vscode-languageserver": "^5.1.0" + "vscode-css-languageservice": "^4.0.2-next.3", + "vscode-languageserver": "^5.3.0-next.2" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "^8.10.25", + "@types/node": "^10.12.21", "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 bc656844208..e1c78b158c1 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -7,13 +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 { TextDocument, CompletionList, Position } from 'vscode-languageserver-types'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { getPathCompletionParticipant } from './pathCompletion'; import { formatError, runSafe } from './utils/runner'; import { getDocumentContext } from './utils/documentContext'; +import { getDataProviders } from './customData'; export interface Settings { css: LanguageSettings; @@ -50,6 +51,8 @@ let scopedSettingsSupport = false; let foldingRangeLimit = Number.MAX_VALUE; let workspaceFolders: WorkspaceFolder[]; +const languageServices: { [id: string]: LanguageService } = {}; + // 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. connection.onInitialize((params: InitializeParams): InitializeResult => { @@ -61,6 +64,9 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } } + const dataPaths: string[] = params.initializationOptions.dataPaths; + const customDataProviders = getDataProviders(dataPaths); + function getClientCapability(name: string, def: T) { const keys = name.split('.'); let c: any = params.capabilities; @@ -76,6 +82,10 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + languageServices.css = getCSSLanguageService({ customDataProviders }); + languageServices.scss = getSCSSLanguageService({ customDataProviders }); + languageServices.less = getLESSLanguageService({ customDataProviders }); + const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, @@ -96,12 +106,6 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { return { capabilities }; }); -const languageServices: { [id: string]: LanguageService } = { - css: getCSSLanguageService(), - scss: getSCSSLanguageService(), - less: getLESSLanguageService() -}; - function getLanguageService(document: TextDocument) { let service = languageServices[document.languageId]; if (!service) { @@ -126,7 +130,7 @@ function getDocumentSettings(textDocument: TextDocument): Thenable { }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); }); +connection.onRequest('$/textDocument/selectionRanges', async (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + const positions: Position[] = params.positions; + + if (document) { + const stylesheet = stylesheets.get(document); + return getLanguageService(document).getSelectionRanges(document, positions, stylesheet); + } + return Promise.resolve(null); + }, null, `Error while computing selection ranges for ${params.textDocument.uri}`, token); +}); + + // Listen on the connection connection.listen(); \ No newline at end of file diff --git a/extensions/css-language-features/server/src/customData.ts b/extensions/css-language-features/server/src/customData.ts new file mode 100644 index 00000000000..f173d884a2b --- /dev/null +++ b/extensions/css-language-features/server/src/customData.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CSSDataV1, ICSSDataProvider } from 'vscode-css-languageservice'; +import * as fs from 'fs'; + +export function getDataProviders(dataPaths: string[]): ICSSDataProvider[] { + const providers = dataPaths.map(p => { + if (fs.existsSync(p)) { + const data = parseCSSData(fs.readFileSync(p, 'utf-8')); + return { + provideProperties: () => data.properties || [], + provideAtDirectives: () => data.atDirectives || [], + providePseudoClasses: () => data.pseudoClasses || [], + providePseudoElements: () => data.pseudoElements || [] + }; + } else { + return { + provideProperties: () => [], + provideAtDirectives: () => [], + providePseudoClasses: () => [], + providePseudoElements: () => [] + }; + } + }); + + return providers; +} + +function parseCSSData(source: string): CSSDataV1 { + let rawData: any; + + try { + rawData = JSON.parse(source); + } catch (err) { + return { + version: 1 + }; + } + + return { + version: 1, + properties: rawData.properties || [], + atDirectives: rawData.atDirectives || [], + pseudoClasses: rawData.pseudoClasses || [], + pseudoElements: rawData.pseudoElements || [] + }; +} diff --git a/extensions/css-language-features/server/src/languageModelCache.ts b/extensions/css-language-features/server/src/languageModelCache.ts index 7ce4adca9d4..561de4a9a7a 100644 --- a/extensions/css-language-features/server/src/languageModelCache.ts +++ b/extensions/css-language-features/server/src/languageModelCache.ts @@ -15,7 +15,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime let languageModels: { [uri: string]: { version: number, languageId: string, cTime: number, languageModel: T } } = {}; let nModels = 0; - let cleanupInterval: NodeJS.Timer | undefined = void 0; + let cleanupInterval: NodeJS.Timer | undefined = undefined; if (cleanupIntervalTimeInSec > 0) { cleanupInterval = setInterval(() => { let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; @@ -73,7 +73,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime dispose() { if (typeof cleanupInterval !== 'undefined') { clearInterval(cleanupInterval); - cleanupInterval = void 0; + cleanupInterval = undefined; languageModels = {}; nModels = 0; } diff --git a/extensions/css-language-features/server/src/pathCompletion.ts b/extensions/css-language-features/server/src/pathCompletion.ts index 28fb9ab24bb..4d988a52da9 100644 --- a/extensions/css-language-features/server/src/pathCompletion.ts +++ b/extensions/css-language-features/server/src/pathCompletion.ts @@ -195,9 +195,9 @@ function escapePath(p: string) { } function resolveWorkspaceRoot(activeDoc: TextDocument, workspaceFolders: WorkspaceFolder[]): string | undefined { - for (let i = 0; i < workspaceFolders.length; i++) { - if (startsWith(activeDoc.uri, workspaceFolders[i].uri)) { - return path.resolve(URI.parse(workspaceFolders[i].uri).fsPath); + for (const folder of workspaceFolders) { + if (startsWith(activeDoc.uri, folder.uri)) { + return path.resolve(URI.parse(folder.uri).fsPath); } } return undefined; diff --git a/extensions/css-language-features/server/src/utils/documentContext.ts b/extensions/css-language-features/server/src/utils/documentContext.ts index 21030e801b4..9858ca84367 100644 --- a/extensions/css-language-features/server/src/utils/documentContext.ts +++ b/extensions/css-language-features/server/src/utils/documentContext.ts @@ -19,7 +19,7 @@ export function getDocumentContext(documentUri: string, workspaceFolders: Worksp return folderURI; } } - return void 0; + return undefined; } return { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 30c2fb6c62f..dbe0fa51fd9 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== ansi-regex@^3.0.0: version "3.0.0" @@ -229,12 +229,12 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.13-next.3: - version "3.0.13-next.3" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.3.tgz#b9e6f253cace52fbb749d2fe194ae4b196f3543e" - integrity sha512-7+7JddZRt8zFRLbqygxJw+GHtbQ5o2YvgEFvi4ixvbUAX6KlY3Yw6CgUUbg9dmBla7h+GbtoDjwiLFV6Oy+SBQ== +vscode-css-languageservice@^4.0.2-next.3: + version "4.0.2-next.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.2-next.3.tgz#f81925d6037f05724d1c1fe00e8fb7fcbece72ff" + integrity sha512-Th6ESBGTdNo4CbZEeKNVBKi4DwGjafS1+05kuoH3hO5mFCKr6ttdvu4GxMHca7nGN1efv5tiZ6slO8PCN1bLNA== dependencies: - vscode-languageserver-types "^3.13.0" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" vscode-jsonrpc@^4.0.0: @@ -242,25 +242,25 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.15.0-next.1: + version "3.15.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" + integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0, vscode-languageserver-types@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== -vscode-languageserver@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.1.0.tgz#012a28f154cc7a848c443d217894942e4c3eeb39" - integrity sha512-CIsrgx2Y5VHS317g/HwkSTWYBIQmy0DwEyZPmB2pEpVOhYFwVsYpbiJwHIIyLQsQtmRaO4eA2xM8KPjNSdXpBw== +vscode-languageserver@^5.3.0-next.2: + version "5.3.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" + integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== dependencies: - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.15.0-next.1" vscode-uri "^1.0.6" vscode-nls@^4.0.0: diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index d626f86f433..385d59738dd 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== balanced-match@^1.0.0: version "1.0.0" @@ -167,26 +167,26 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageclient@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.1.0.tgz#650ab0dc9fd0daaade058a8471aaff5bc3f9580e" - integrity sha512-Z95Kps8UqD4o17HE3uCkZuvenOsxHVH46dKmaGVpGixEFZigPaVuVxLM/JWeIY9aRenoC0ZD9CK1O7L4jpffKg== +vscode-languageclient@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" + integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.14.1" -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" + integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/css/cgmanifest.json b/extensions/css/cgmanifest.json index a4d5abc868c..7b7a8d5e8e8 100644 --- a/extensions/css/cgmanifest.json +++ b/extensions/css/cgmanifest.json @@ -4,15 +4,15 @@ "component": { "type": "git", "git": { - "name": "atom/language-css", - "repositoryUrl": "https://github.com/atom/language-css", - "commitHash": "7e06c88b89218fe6e8eba77fb674152f1cea0b10" + "name": "octref/language-css", + "repositoryUrl": "https://github.com/octref/language-css", + "commitHash": "6d3a2d01dd67ef062030f4520dd42a5424330a3b" } }, "license": "MIT", - "description": "The file syntaxes/css.tmLanguage.json was derived from the Atom package https://github.com/atom/language-css which was originally converted from the TextMate bundle https://github.com/textmate/css.tmbundle.", - "version": "0.0.0" + "description": "The file syntaxes/css.tmLanguage.json was derived from https://github.com/octref/language-css which was derived from the Atom package https://github.com/atom/language-css which was originally converted from the TextMate bundle https://github.com/textmate/css.tmbundle.", + "version": "0.42.11" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/css/syntaxes/css.tmLanguage.json b/extensions/css/syntaxes/css.tmLanguage.json index 488c8f12c30..a45463c8b9b 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/7e06c88b89218fe6e8eba77fb674152f1cea0b10", + "version": "https://github.com/octref/language-css/commit/6d3a2d01dd67ef062030f4520dd42a5424330a3b", "name": "CSS", "scopeName": "source.css", "patterns": [ @@ -1397,7 +1397,7 @@ "property-names": { "patterns": [ { - "match": "(?xi) (?(); + + private hasFired = false; + private shellPid?: number; + private regexp: RegExp; + private disposables: vscode.Disposable[] = []; + + static start(session: vscode.DebugSession): ServerReadyDetector | undefined { + if (session.configuration.serverReadyAction) { + let detector = ServerReadyDetector.detectors.get(session); + if (!detector) { + detector = new ServerReadyDetector(session); + ServerReadyDetector.detectors.set(session, detector); + } + return detector; + } + return undefined; + } + + static stop(session: vscode.DebugSession): void { + let detector = ServerReadyDetector.detectors.get(session); + if (detector) { + ServerReadyDetector.detectors.delete(session); + detector.dispose(); + } + } + + static rememberShellPid(session: vscode.DebugSession, pid: number) { + let detector = ServerReadyDetector.detectors.get(session); + if (detector) { + detector.shellPid = pid; + } + } + + private constructor(private session: vscode.DebugSession) { + super(() => this.internalDispose()); + + this.regexp = new RegExp(session.configuration.serverReadyAction.pattern || PATTERN, 'i'); + } + + private internalDispose() { + this.disposables.forEach(d => d.dispose()); + this.disposables = []; + } + + async trackTerminals() { + + let terminals: vscode.Terminal[] = []; + + // either find the terminal where the debug is started with "runInTerminal" or use all terminals + for (let terminal of vscode.window.terminals) { + if (!this.shellPid || await terminal.processId === this.shellPid) { + terminals.push(terminal); + } + } + this.shellPid = undefined; + + terminals.forEach(terminal => { + this.disposables.push(terminal.onDidWriteData(s => { + this.detectPattern(s); + })); + }); + } + + detectPattern(s: string): void { + + if (!this.hasFired) { + const matches = this.regexp.exec(s); + if (matches && matches.length >= 1) { + this.openExternalWithString(this.session, matches.length > 1 ? matches[1] : ''); + this.hasFired = true; + this.internalDispose(); + } + } + } + + private openExternalWithString(session: vscode.DebugSession, captureString: string) { + + const args: ServerReadyAction = session.configuration.serverReadyAction; + const format = args.uriFormat || URI_FORMAT; + + if (captureString === '') { + // nothing captured by reg exp -> use the uriFormat as the target url without substitution + // verify that format does not contain '%s' + if (format.indexOf('%s') >= 0) { + const errMsg = localize('server.ready.nocapture.error', "Format uri ('{0}') uses a substitution placeholder but pattern did not capture anything.", format); + vscode.window.showErrorMessage(errMsg, { modal: true }).then(_ => undefined); + return; + } + captureString = format; + } else if (/^[0-9]+$/.test(captureString)) { + // looks like a port number -> use the uriFormat and substitute a single "%s" with the port + // verify that format only contains a single '%s' + const s = format.split('%s'); + if (s.length !== 2) { + const errMsg = localize('server.ready.placeholder.error', "Format uri ('{0}') must contain exactly one substitution placeholder.", format); + vscode.window.showErrorMessage(errMsg, { modal: true }).then(_ => undefined); + return; + } + captureString = util.format(format, captureString); + } else { + // use the string as is + } + + this.openExternalWithUri(session, captureString); + } + + private openExternalWithUri(session: vscode.DebugSession, uri: string) { + + const args: ServerReadyAction = session.configuration.serverReadyAction; + switch (args.action || 'openExternally') { + case 'openExternally': + vscode.env.openExternal(vscode.Uri.parse(uri)); + break; + case 'debugWithChrome': + + const chrome = vscode.extensions.getExtension('msjsdiag.debugger-for-chrome'); + if (chrome) { + vscode.debug.startDebugging(session.workspaceFolder, { + type: 'chrome', + name: 'Chrome Debug', + request: 'launch', + url: uri, + webRoot: args.webRoot || WEB_ROOT + }, session); + } else { + const errMsg = localize('server.ready.chrome.not.installed', "The action 'debugWithChrome' requires the '{0}'", 'Debugger for Chrome'); + vscode.window.showErrorMessage(errMsg, { modal: true }).then(_ => undefined); + } + break; + default: + // not supported + break; + } + } +} + +export function activate(context: vscode.ExtensionContext) { + + context.subscriptions.push(vscode.debug.onDidChangeActiveDebugSession(session => { + if (session && session.configuration.serverReadyAction) { + const detector = ServerReadyDetector.start(session); + if (detector) { + detector.trackTerminals(); + } + } + })); + + context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(session => { + ServerReadyDetector.stop(session); + })); + + const trackers = new Set(); + + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('*', { + resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration) { + if (debugConfiguration.type && debugConfiguration.serverReadyAction) { + if (!trackers.has(debugConfiguration.type)) { + trackers.add(debugConfiguration.type); + startTrackerForType(context, debugConfiguration.type); + } + } + return debugConfiguration; + } + })); +} + +function startTrackerForType(context: vscode.ExtensionContext, type: string) { + + // scan debug console output for a PORT message + context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(type, { + createDebugAdapterTracker(session: vscode.DebugSession) { + const detector = ServerReadyDetector.start(session); + if (detector) { + let runInTerminalRequestSeq: number | undefined; + return { + onDidSendMessage: m => { + if (m.type === 'event' && m.event === 'output' && m.body) { + switch (m.body.category) { + case 'console': + case 'stderr': + case 'stdout': + if (m.body.output) { + detector.detectPattern(m.body.output); + } + break; + default: + break; + } + } + if (m.type === 'request' && m.command === 'runInTerminal' && m.arguments) { + if (m.arguments.kind === 'integrated') { + runInTerminalRequestSeq = m.seq; // remember this to find matching response + } + } + }, + onWillReceiveMessage: m => { + if (runInTerminalRequestSeq && m.type === 'response' && m.command === 'runInTerminal' && m.body && runInTerminalRequestSeq === m.request_seq) { + runInTerminalRequestSeq = undefined; + ServerReadyDetector.rememberShellPid(session, m.body.shellProcessId); + } + } + }; + } + return undefined; + } + })); +} diff --git a/src/vs/workbench/parts/terminal/browser/terminalFindWidget.css b/extensions/debug-server-ready/src/typings/ref.d.ts similarity index 69% rename from src/vs/workbench/parts/terminal/browser/terminalFindWidget.css rename to extensions/debug-server-ready/src/typings/ref.d.ts index 93422f893ea..954bab971e3 100644 --- a/src/vs/workbench/parts/terminal/browser/terminalFindWidget.css +++ b/extensions/debug-server-ready/src/typings/ref.d.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .simple-find-part .monaco-inputbox > .wrapper > .input { - width: 100% !important; - padding-right: 66px; -} \ No newline at end of file +/// +/// +/// diff --git a/extensions/debug-server-ready/tsconfig.json b/extensions/debug-server-ready/tsconfig.json new file mode 100644 index 00000000000..f9b780a0e1c --- /dev/null +++ b/extensions/debug-server-ready/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out", + "downlevelIteration": true + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/debug-server-ready/yarn.lock b/extensions/debug-server-ready/yarn.lock new file mode 100644 index 00000000000..6767cb8d8c2 --- /dev/null +++ b/extensions/debug-server-ready/yarn.lock @@ -0,0 +1,13 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@8.0.33": + version "8.0.33" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" + integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== + +vscode-nls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" + integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== diff --git a/extensions/docker/cgmanifest.json b/extensions/docker/cgmanifest.json index 844d3120d3c..dbbf43607ee 100644 --- a/extensions/docker/cgmanifest.json +++ b/extensions/docker/cgmanifest.json @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 8733fbdb5c9..9805a694c83 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -434,7 +434,9 @@ } }, "scripts": { - "compile": "gulp compile-extension:emmet" + "watch": "gulp watch-extension:emmet", + "compile": "gulp compile-extension:emmet", + "deps": "yarn add vscode-emmet-helper" }, "devDependencies": { "@types/node": "8.0.33", @@ -447,6 +449,6 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^0.1.1", "image-size": "^0.5.2", - "vscode-emmet-helper": "^1.2.12" + "vscode-emmet-helper": "^1.2.15" } } diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index f6634d35c9c..f332aff2e00 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -7,8 +7,8 @@ import * as vscode from 'vscode'; import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetNode'; import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny, allowedMimeTypesInScriptTag } from './util'; -const trimRegex = /[\u00a0]*[\d|#|\-|\*|\u2022]+\.?/; -const hexColorRegex = /^#[\d,a-f,A-F]{0,6}$/; +const trimRegex = /[\u00a0]*[\d#\-\*\u2022]+\.?/; +const hexColorRegex = /^#[\da-fA-F]{0,6}$/; const inlineElements = ['a', 'abbr', 'acronym', 'applet', 'b', 'basefont', 'bdo', 'big', 'br', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'object', 'q', @@ -54,7 +54,11 @@ function doWrapping(individualLines: boolean, args: any) { return; } } - const syntax = 'html'; + args = args || {}; + if (!args['language']) { + args['language'] = editor.document.languageId; + } + const syntax = getSyntaxFromArgs(args) || 'html'; const rootNode = parseDocument(editor.document, false); let inPreview = false; @@ -105,9 +109,9 @@ function doWrapping(individualLines: boolean, args: any) { function revertPreview(): Thenable { return editor.edit(builder => { - for (let i = 0; i < rangesToReplace.length; i++) { - builder.replace(rangesToReplace[i].previewRange, rangesToReplace[i].originalContent); - rangesToReplace[i].previewRange = rangesToReplace[i].originalRange; + for (const rangeToReplace of rangesToReplace) { + builder.replace(rangeToReplace.previewRange, rangeToReplace.originalContent); + rangeToReplace.previewRange = rangeToReplace.originalRange; } }, { undoStopBefore: false, undoStopAfter: false }); } @@ -295,8 +299,8 @@ export function expandEmmetAbbreviation(args: any): Thenable { - var posA = a.isReversed ? a.anchor : a.active; - var posB = b.isReversed ? b.anchor : b.active; + const posA = a.isReversed ? a.anchor : a.active; + const posB = b.isReversed ? b.anchor : b.active; return posA.compareTo(posB) * -1; }); @@ -535,9 +539,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen /** * Expands abbreviations as detailed in expandAbbrList in the editor - * @param editor - * @param expandAbbrList - * @param insertSameSnippet + * * @returns false if no snippet can be inserted. */ function expandAbbreviationInRange(editor: vscode.TextEditor, expandAbbrList: ExpandAbbreviationInput[], insertSameSnippet: boolean): Thenable { diff --git a/extensions/emmet/src/extension.ts b/extensions/emmet/src/extension.ts index fb7bcb22f0f..a5195af577c 100644 --- a/extensions/emmet/src/extension.ts +++ b/extensions/emmet/src/extension.ts @@ -98,7 +98,7 @@ export function activate(context: vscode.ExtensionContext) { })); context.subscriptions.push(vscode.commands.registerCommand('editor.emmet.action.incrementNumberByOneTenth', () => { - return incrementDecrement(.1); + return incrementDecrement(0.1); })); context.subscriptions.push(vscode.commands.registerCommand('editor.emmet.action.incrementNumberByOne', () => { diff --git a/extensions/emmet/src/reflectCssValue.ts b/extensions/emmet/src/reflectCssValue.ts index 56f9fbc9840..da20b38397b 100644 --- a/extensions/emmet/src/reflectCssValue.ts +++ b/extensions/emmet/src/reflectCssValue.ts @@ -29,9 +29,9 @@ function updateCSSNode(editor: TextEditor, property: Property): Thenable { new Selection(3, 17, 3, 17), // cursor inside the inner span element new Selection(4, 5, 4, 5), // cursor inside opening tag new Selection(5, 35, 5, 35), // cursor inside closing tag - new Selection(7, 3, 7, 3), // cursor inside open tag of
    one of of whose children is already commented + new Selection(7, 3, 7, 3), // cursor inside open tag of
      one of whose children is already commented new Selection(14, 8, 14, 8), // cursor inside the css property inside the style tag new Selection(18, 3, 18, 3) // cursor inside the css rule inside the style tag ]; @@ -114,7 +114,7 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => { editor.selections = [ new Selection(3, 7, 3, 25), // Hello< new Selection(4, 3, 4, 30), //
    • There
    • - new Selection(7, 2, 10, 7), // The
        one of of whose children is already commented + new Selection(7, 2, 10, 7), // The
          one of whose children is already commented new Selection(14, 4, 14, 17), // css property inside the style tag new Selection(17, 3, 20, 4) // the css rule inside the style tag ]; @@ -192,7 +192,7 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => { return withRandomFileEditor(contents, 'html', (editor, doc) => { editor.selections = [ new Selection(3, 24, 4, 20), - new Selection(7, 2, 9, 10) // The
            one of of whose children is already commented + new Selection(7, 2, 9, 10) // The
              one of whose children is already commented ]; return toggleComment().then(() => { @@ -232,7 +232,7 @@ suite('Tests for Toggle Comment action from Emmet (HTML)', () => { new Selection(3, 17, 3, 17), // cursor inside the inner span element new Selection(4, 5, 4, 5), // two cursors: one inside opening tag new Selection(4, 17, 4, 17), // and the second inside the inner span element - new Selection(7, 3, 7, 3), // two cursors: one inside open tag of
                one of of whose children is already commented + new Selection(7, 3, 7, 3), // two cursors: one inside open tag of
                  one of whose children is already commented new Selection(9, 10, 9, 10), // and the second inside inner li element, whose parent is selected new Selection(12, 3, 12, 3), // four nested cursors: one inside the style open tag new Selection(14, 8, 14, 8), // the second inside the css property inside the style tag diff --git a/extensions/emmet/src/test/wrapWithAbbreviation.test.ts b/extensions/emmet/src/test/wrapWithAbbreviation.test.ts index 9fc924508e2..d5a4a2bce3c 100644 --- a/extensions/emmet/src/test/wrapWithAbbreviation.test.ts +++ b/extensions/emmet/src/test/wrapWithAbbreviation.test.ts @@ -134,19 +134,7 @@ suite('Tests for Wrap with Abbreviations', () => {
                `; - - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(2, 0, 2, 0)]; - const promise = wrapWithAbbreviation({ abbreviation: 'li.hello|c' }); - if (!promise) { - assert.equal(1, 2, 'Wrap returned undefined instead of promise.'); - return Promise.resolve(); - } - return promise.then(() => { - assert.equal(editor.document.getText(), expectedContents); - return Promise.resolve(); - }); - }); + return testWrapWithAbbreviation([new Selection(2, 0, 2, 0)], 'li.hello|c', expectedContents, contents); }); test('Wrap with abbreviation entire node when cursor is on opening tag', () => { @@ -162,19 +150,7 @@ suite('Tests for Wrap with Abbreviations', () => { `; - - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(1, 1, 1, 1)]; - const promise = wrapWithAbbreviation({ abbreviation: 'div' }); - if (!promise) { - assert.equal(1, 2, 'Wrap returned undefined instead of promise.'); - return Promise.resolve(); - } - return promise.then(() => { - assert.equal(editor.document.getText(), expectedContents); - return Promise.resolve(); - }); - }); + return testWrapWithAbbreviation([new Selection(1, 1, 1, 1)], 'div', expectedContents, contents); }); test('Wrap with abbreviation entire node when cursor is on closing tag', () => { @@ -190,19 +166,7 @@ suite('Tests for Wrap with Abbreviations', () => { `; - - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(3, 1, 3, 1)]; - const promise = wrapWithAbbreviation({ abbreviation: 'div' }); - if (!promise) { - assert.equal(1, 2, 'Wrap returned undefined instead of promise.'); - return Promise.resolve(); - } - return promise.then(() => { - assert.equal(editor.document.getText(), expectedContents); - return Promise.resolve(); - }); - }); + return testWrapWithAbbreviation([new Selection(3, 1, 3, 1)], 'div', expectedContents, contents); }); test('Wrap with multiline abbreviation doesnt add extra spaces', () => { @@ -215,19 +179,7 @@ suite('Tests for Wrap with Abbreviations', () => {
              • hello
              `; - - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(1, 2, 1, 2)]; - const promise = wrapWithAbbreviation({ abbreviation: 'ul>li>a' }); - if (!promise) { - assert.equal(1, 2, 'Wrap returned undefined instead of promise.'); - return Promise.resolve(); - } - return promise.then(() => { - assert.equal(editor.document.getText(), expectedContents); - return Promise.resolve(); - }); - }); + return testWrapWithAbbreviation([new Selection(1, 2, 1, 2)], 'ul>li>a', expectedContents, contents); }); test('Wrap individual lines with abbreviation', () => { @@ -245,18 +197,7 @@ suite('Tests for Wrap with Abbreviations', () => {
          `; - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(2, 2, 3, 33)]; - const promise = wrapIndividualLinesWithAbbreviation({ abbreviation: 'ul>li.hello$*' }); - if (!promise) { - assert.equal(1, 2, 'Wrap Individual Lines with Abbreviation returned undefined.'); - return Promise.resolve(); - } - return promise.then(() => { - assert.equal(editor.document.getText(), wrapIndividualLinesExpected); - return Promise.resolve(); - }); - }); + return testWrapIndividualLinesWithAbbreviation([new Selection(2, 2, 3, 33)], 'ul>li.hello$*', wrapIndividualLinesExpected, contents); }); test('Wrap individual lines with abbreviation with extra space selected', () => { @@ -274,18 +215,7 @@ suite('Tests for Wrap with Abbreviations', () => {
      `; - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(2, 1, 4, 0)]; - const promise = wrapIndividualLinesWithAbbreviation({ abbreviation: 'ul>li.hello$*' }); - if (!promise) { - assert.equal(1, 2, 'Wrap Individual Lines with Abbreviation returned undefined.'); - return Promise.resolve(); - } - return promise.then(() => { - assert.equal(editor.document.getText(), wrapIndividualLinesExpected); - return Promise.resolve(); - }); - }); + return testWrapIndividualLinesWithAbbreviation([new Selection(2, 1, 4, 0)], 'ul>li.hello$*', wrapIndividualLinesExpected, contents); }); test('Wrap individual lines with abbreviation with comment filter', () => { @@ -305,18 +235,7 @@ suite('Tests for Wrap with Abbreviations', () => {
`; - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(2, 2, 3, 33)]; - const promise = wrapIndividualLinesWithAbbreviation({ abbreviation: 'ul>li.hello*|c' }); - if (!promise) { - assert.equal(1, 2, 'Wrap Individual Lines with Abbreviation returned undefined.'); - return Promise.resolve(); - } - return promise.then(() => { - assert.equal(editor.document.getText(), wrapIndividualLinesExpected); - return Promise.resolve(); - }); - }); + return testWrapIndividualLinesWithAbbreviation([new Selection(2, 2, 3, 33)], 'ul>li.hello*|c', wrapIndividualLinesExpected, contents); }); test('Wrap individual lines with abbreviation and trim', () => { @@ -334,19 +253,7 @@ suite('Tests for Wrap with Abbreviations', () => { `; - return withRandomFileEditor(contents, 'html', (editor, _) => { - editor.selections = [new Selection(2, 3, 3, 16)]; - const promise = wrapIndividualLinesWithAbbreviation({ abbreviation: 'ul>li.hello$*|t' }); - if (!promise) { - assert.equal(1, 2, 'Wrap Individual Lines with Abbreviation returned undefined.'); - return Promise.resolve(); - } - - return promise.then(() => { - assert.equal(editor.document.getText(), wrapIndividualLinesExpected); - return Promise.resolve(); - }); - }); + return testWrapIndividualLinesWithAbbreviation([new Selection(2, 3, 3, 16)], 'ul>li.hello$*|t', wrapIndividualLinesExpected, contents); }); test('Wrap with abbreviation and format set to false', () => { @@ -384,11 +291,35 @@ suite('Tests for Wrap with Abbreviations', () => { return testWrapWithAbbreviation([new Selection(2, 4, 3, 9), new Selection(5, 4, 6, 9)], 'div', wrapMultiLineExpected, htmlContentsForWrapMultiLineTests); }); + + test('Wrap multiline with abbreviation uses className for jsx files', () => { + const wrapMultiLineJsxExpected = ` + +`; + + return testWrapWithAbbreviation([new Selection(2,2,3,33)], '.hello', wrapMultiLineJsxExpected, htmlContentsForWrapTests, 'jsx'); + }); + + test('Wrap individual line with abbreviation uses className for jsx files', () => { + const wrapIndividualLinesJsxExpected = ` + +`; + + return testWrapIndividualLinesWithAbbreviation([new Selection(2,2,3,33)], '.hello$*', wrapIndividualLinesJsxExpected, htmlContentsForWrapTests, 'jsx'); + }); }); -function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests): Thenable { - return withRandomFileEditor(input, 'html', (editor, _) => { +function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests, fileExtension: string = 'html'): Thenable { + return withRandomFileEditor(input, fileExtension, (editor, _) => { editor.selections = selections; const promise = wrapWithAbbreviation({ abbreviation }); if (!promise) { @@ -402,3 +333,19 @@ function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, }); }); } + +function testWrapIndividualLinesWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests, fileExtension: string = 'html'): Thenable { + return withRandomFileEditor(input, fileExtension, (editor, _) => { + editor.selections = selections; + const promise = wrapIndividualLinesWithAbbreviation({ abbreviation }); + if (!promise) { + assert.equal(1, 2, 'Wrap individual lines with Abbreviation returned undefined.'); + return Promise.resolve(); + } + + return promise.then(() => { + assert.equal(editor.document.getText(), expectedContents); + return Promise.resolve(); + }); + }); +} diff --git a/extensions/emmet/src/toggleComment.ts b/extensions/emmet/src/toggleComment.ts index fa8cdc5e68c..952126fca5e 100644 --- a/extensions/emmet/src/toggleComment.ts +++ b/extensions/emmet/src/toggleComment.ts @@ -39,8 +39,7 @@ export function toggleComment(): Thenable | undefined { return result === 0 ? arr1[0].range.start.character - arr2[0].range.start.character : result; }); let lastEditPosition = new vscode.Position(0, 0); - for (let i = 0; i < allEdits.length; i++) { - const edits = allEdits[i]; + for (const edits of allEdits) { if (edits[0].range.end.isAfterOrEqual(lastEditPosition)) { edits.forEach(x => { editBuilder.replace(x.range, x.newText); @@ -151,8 +150,8 @@ function toggleCommentStylesheet(selection: vscode.Selection, rootNode: Styleshe } function adjustStartNodeCss(node: Node | null, pos: vscode.Position, rootNode: Stylesheet): vscode.Position { - for (let i = 0; i < rootNode.comments.length; i++) { - let commentRange = new vscode.Range(rootNode.comments[i].start, rootNode.comments[i].end); + for (const comment of rootNode.comments) { + let commentRange = new vscode.Range(comment.start, comment.end); if (commentRange.contains(pos)) { return pos; } @@ -184,8 +183,8 @@ function adjustStartNodeCss(node: Node | null, pos: vscode.Position, rootNode: S } function adjustEndNodeCss(node: Node | null, pos: vscode.Position, rootNode: Stylesheet): vscode.Position { - for (let i = 0; i < rootNode.comments.length; i++) { - let commentRange = new vscode.Range(rootNode.comments[i].start, rootNode.comments[i].end); + for (const comment of rootNode.comments) { + let commentRange = new vscode.Range(comment.start, comment.end); if (commentRange.contains(pos)) { return pos; } diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 6e2531c001a..c90ff7f5c25 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -96,11 +96,10 @@ export function getMappingForIncludedLanguages(): any { /** * Get the corresponding emmet mode for given vscode language mode * Eg: jsx for typescriptreact/javascriptreact or pug for jade -* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting, +* If the language is not supported by emmet or has been excluded via `excludeLanguages` setting, * then nothing is returned * -* @param language -* @param exlcudedLanguages Array of language ids that user has chosen to exlcude for emmet +* @param excludedLanguages Array of language ids that user has chosen to exclude for emmet */ export function getEmmetMode(language: string, excludedLanguages: string[]): string | undefined { if (!language || excludedLanguages.indexOf(language) > -1) { @@ -353,7 +352,6 @@ export function getHtmlNode(document: vscode.TextDocument, root: Node | undefine /** * Returns inner range of an html node. - * @param currentNode */ export function getInnerRange(currentNode: HtmlNode): vscode.Range | undefined { if (!currentNode.close) { @@ -364,7 +362,6 @@ export function getInnerRange(currentNode: HtmlNode): vscode.Range | undefined { /** * Returns the deepest non comment node under given node - * @param node */ export function getDeepestNode(node: Node | undefined): Node | undefined { if (!node || !node.children || node.children.length === 0 || !node.children.find(x => x.type !== 'comment')) { diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index 22f16e39770..1fb4a9aff41 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2469,10 +2469,10 @@ vinyl@~2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-emmet-helper@^1.2.12: - version "1.2.12" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.12.tgz#6a75d5a74af735db179c708a9a91feb191f3edb5" - integrity sha512-pCo+BNgP3lGuzTIGP1IMOGILpNGVopiAu02FrDC4ctEQQ5vFIaMtI4HxDGAw2Zu7pqGEHn6Z1KFmOb2Hd/xkHA== +vscode-emmet-helper@^1.2.15: + version "1.2.15" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.15.tgz#62dbfbf49bb9ebe329cb7bffdda5aaac725eea7a" + integrity sha512-JplvmMMWSvm/6/dZezix2ADPM49u6YahPYjs/QToohUpomW/2Eb27ecCrkCyOGBPfKLKGiOPHCssss8TSDA9ag== dependencies: "@emmetio/extract-abbreviation" "0.1.6" jsonc-parser "^1.0.0" diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 0caec7607a6..52159b965bb 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -41,10 +41,18 @@ "fileMatch": "*color-theme.json", "url": "vscode://schemas/color-theme" } + ], + "languages": [ + { + "id": "ignore", + "filenames": [ + ".vscodeignore" + ] + } ] }, "devDependencies": { "@types/markdown-it": "0.0.2", - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" } } diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock index d82f7c212a0..720c84f0656 100644 --- a/extensions/extension-editing/yarn.lock +++ b/extensions/extension-editing/yarn.lock @@ -7,16 +7,16 @@ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660" integrity sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA= +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== + "@types/node@^6.0.46": version "6.0.78" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.78.tgz#5d4a3f579c1524e01ee21bf474e6fba09198f470" integrity sha512-+vD6E8ixntRzzZukoF3uP1iV+ZjVN3koTcaeK+BEoc/kSfGbLDIGC7RmCaUgVpUfN6cWvfczFRERCyKM9mkvXg== -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== - 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/cgmanifest.json b/extensions/fsharp/cgmanifest.json index df594e070bb..d24f6a3d010 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "24c1588529af144d205f66fbcec6889500f9aaa9" + "commitHash": "b2100c95d7857c5421d111a860fcdd20954a0263" } }, "license": "MIT", @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 067389b1650..82e6d33548f 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/ionide/ionide-fsgrammar/commit/24c1588529af144d205f66fbcec6889500f9aaa9", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/b2100c95d7857c5421d111a860fcdd20954a0263", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -117,6 +117,9 @@ } } }, + { + "include": "#compiler_directives" + }, { "include": "#constants" }, @@ -132,6 +135,21 @@ { "include": "#keywords" }, + { + "include": "#text" + }, + { + "include": "#definition" + }, + { + "include": "#attributes" + }, + { + "include": "#keywords" + }, + { + "include": "#cexprs" + }, { "include": "#text" } @@ -267,6 +285,33 @@ } ] }, + { + "begin": "(\\()", + "end": "(\\))", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "(([?[:alpha:]0-9'`^._ ]+))+", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#tuple_signature" + } + ] + }, { "match": "(?!when|and|or\\b)\\b([\\w0-9'`^._]+)", "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", @@ -553,7 +598,7 @@ "include": "#common_declaration" }, { - "match": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([[:alpha:]0-9'`^._ ]+)){0,1}", + "match": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)((?!with\\b)\\b([\\w0-9'`^._ ]+)){0,1}", "captures": { "1": { "name": "keyword.symbol.fsharp" @@ -754,9 +799,9 @@ } }, { - "begin": "(<(?![[:space:]]*\\)))", + "begin": "(<+(?![[:space:]]*\\)))", "beginComment": "The group (?![[:space:]]*\\) is for protection against overload operator. static member (<)", - "end": "((?)", + "end": "((?|\\))", "endComment": "The group (? when using SRTP synthax", "beginCaptures": { "1": { @@ -796,6 +841,14 @@ { "include": "#definition" }, + { + "match": "(?<=>)\\s*(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, { "include": "#variables" }, @@ -808,7 +861,7 @@ "patterns": [ { "name": "binding.fsharp", - "begin": "\\b(let mutable|let inline|let|member val|static member inline|static member|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", + "begin": "\\b(let mutable|static let mutable|let inline|let|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", "end": "\\s*(with\\b|=|\\n+=|(?<=\\=))", "beginCaptures": { "1": { @@ -838,6 +891,26 @@ } ] }, + { + "name": "binding.fsharp", + "begin": "\\b((get|set)\\s*(?=\\())(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", + "end": "\\s*(=|\\n+=|(?<=\\=))", + "beginCaptures": { + "3": { + "name": "variable.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "patterns": [ + { + "include": "#common_binding_definition" + } + ] + }, { "name": "binding.fsharp", "begin": "\\b(static val mutable|val mutable|val)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", @@ -902,7 +975,7 @@ } }, { - "match": "([[:alpha:]0-9'`^._]+)|``([[:alpha:]0-9'^._ ]+)``", + "match": "(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -1124,7 +1197,7 @@ "match": "\\(\\)" }, { - "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|[[:alpha:]0-9'`<>^._ ]\\w*)", + "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|(?!private\\b)\\b[\\w[:alpha:]0-9'`<>^._ ]+)", "captures": { "1": { "name": "keyword.symbol.fsharp" diff --git a/extensions/fsharp/test/colorize-results/test_fs.json b/extensions/fsharp/test/colorize-results/test_fs.json index 37c0b61c14a..e5736fc10aa 100644 --- a/extensions/fsharp/test/colorize-results/test_fs.json +++ b/extensions/fsharp/test/colorize-results/test_fs.json @@ -604,28 +604,6 @@ "hc_black": "keyword: #569CD6" } }, - { - "c": " get", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "()", - "t": "source.fsharp constant.language.unit.fsharp", - "r": { - "dark_plus": "constant.language: #569CD6", - "light_plus": "constant.language: #0000FF", - "dark_vs": "constant.language: #569CD6", - "light_vs": "constant.language: #0000FF", - "hc_black": "constant.language: #569CD6" - } - }, { "c": " ", "t": "source.fsharp", @@ -637,9 +615,42 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "get", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "()", + "t": "source.fsharp binding.fsharp constant.language.unit.fsharp", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": " ", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "=", - "t": "source.fsharp keyword.symbol.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -681,50 +692,6 @@ "hc_black": "keyword: #569CD6" } }, - { - "c": " set", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "(", - "t": "source.fsharp keyword.symbol.fsharp", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, - { - "c": "value", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ")", - "t": "source.fsharp keyword.symbol.fsharp", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, { "c": " ", "t": "source.fsharp", @@ -736,9 +703,64 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "set", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.fsharp binding.fsharp keyword.symbol.fsharp", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "value", + "t": "source.fsharp binding.fsharp variable.parameter.fsharp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.fsharp binding.fsharp keyword.symbol.fsharp", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "=", - "t": "source.fsharp keyword.symbol.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -979,7 +1001,7 @@ } }, { - "c": " targetAge", + "c": " targetAge ", "t": "source.fsharp binding.fsharp variable.parameter.fsharp", "r": { "dark_plus": "variable: #9CDCFE", @@ -989,17 +1011,6 @@ "hc_black": "variable: #9CDCFE" } }, - { - "c": " ", - "t": "source.fsharp binding.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, { "c": "=", "t": "source.fsharp binding.fsharp keyword.fsharp", diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json index a7e0b63aac3..d0bdb9ac443 100644 --- a/extensions/git/cgmanifest.json +++ b/extensions/git/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "textmate/git.tmbundle", "repositoryUrl": "https://github.com/textmate/git.tmbundle", - "commitHash": "93897a78c6e52bef13dadc0d4091d203c5facb40" + "commitHash": "3f6ad2138200db14b57a090ecb2d2e733275ca3e" } }, "licenseDetail": [ @@ -63,4 +63,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/git/package.json b/extensions/git/package.json index ef4385ef3db..4395d306bb5 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -240,6 +240,11 @@ "title": "%command.branch%", "category": "Git" }, + { + "command": "git.branchFrom", + "title": "%command.branchFrom%", + "category": "Git" + }, { "command": "git.deleteBranch", "title": "%command.deleteBranch%", @@ -320,6 +325,16 @@ "title": "%command.pushWithTagsForce%", "category": "Git" }, + { + "command": "git.addRemote", + "title": "%command.addRemote%", + "category": "Git" + }, + { + "command": "git.removeRemote", + "title": "%command.removeRemote%", + "category": "Git" + }, { "command": "git.sync", "title": "%command.sync%", @@ -502,6 +517,10 @@ "command": "git.branch", "when": "config.git.enabled && gitOpenRepositoryCount != 0" }, + { + "command": "git.branchFrom", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, { "command": "git.deleteBranch", "when": "config.git.enabled && gitOpenRepositoryCount != 0" @@ -570,6 +589,14 @@ "command": "git.pushWithTagsForce", "when": "config.git.enabled && config.git.allowForcePush && gitOpenRepositoryCount != 0" }, + { + "command": "git.addRemote", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, + { + "command": "git.removeRemote", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, { "command": "git.sync", "when": "config.git.enabled && gitOpenRepositoryCount != 0" @@ -661,94 +688,109 @@ "group": "1_sync", "when": "scmProvider == git" }, + { + "command": "git.pushForce", + "group": "1_sync", + "when": "scmProvider == git && config.git.allowForcePush" + }, { "command": "git.pushTo", "group": "1_sync", "when": "scmProvider == git" }, + { + "command": "git.pushToForce", + "group": "1_sync", + "when": "scmProvider == git && config.git.allowForcePush" + }, + { + "command": "git.checkout", + "group": "2_branch", + "when": "scmProvider == git" + }, { "command": "git.publish", - "group": "2_publish", + "group": "2_branch", "when": "scmProvider == git" }, { "command": "git.commitStaged", - "group": "3_commit", + "group": "4_commit", "when": "scmProvider == git" }, { "command": "git.commitStagedSigned", - "group": "3_commit", + "group": "4_commit", "when": "scmProvider == git" }, { "command": "git.commitStagedAmend", - "group": "3_commit", + "group": "4_commit", "when": "scmProvider == git" }, { "command": "git.commitAll", - "group": "3_commit", + "group": "4_commit", "when": "scmProvider == git" }, { "command": "git.commitAllSigned", - "group": "3_commit", + "group": "4_commit", "when": "scmProvider == git" }, { "command": "git.commitAllAmend", - "group": "3_commit", + "group": "4_commit", "when": "scmProvider == git" }, { "command": "git.undoCommit", - "group": "3_commit", + "group": "4_commit", "when": "scmProvider == git" }, { "command": "git.stageAll", - "group": "4_stage", + "group": "5_stage", "when": "scmProvider == git" }, { "command": "git.unstageAll", - "group": "4_stage", + "group": "5_stage", "when": "scmProvider == git" }, { "command": "git.cleanAll", - "group": "4_stage", + "group": "5_stage", "when": "scmProvider == git && !gitFreshRepository" }, { "command": "git.stashIncludeUntracked", - "group": "5_stash", + "group": "6_stash", "when": "scmProvider == git" }, { "command": "git.stash", - "group": "5_stash", + "group": "6_stash", "when": "scmProvider == git" }, { "command": "git.stashPop", - "group": "5_stash", + "group": "6_stash", "when": "scmProvider == git" }, { "command": "git.stashPopLatest", - "group": "5_stash", + "group": "6_stash", "when": "scmProvider == git" }, { "command": "git.stashApply", - "group": "5_stash", + "group": "6_stash", "when": "scmProvider == git" }, { "command": "git.stashApplyLatest", - "group": "5_stash", + "group": "6_stash", "when": "scmProvider == git" }, { @@ -989,7 +1031,7 @@ ], "markdownDescription": "%config.path%", "default": null, - "scope": "application" + "scope": "machine" }, "git.autoRepositoryDetection": { "type": [ @@ -1018,12 +1060,19 @@ }, "git.autofetch": { "type": "boolean", + "scope": "resource", "description": "%config.autofetch%", "default": false, "tags": [ "usesOnlineServices" ] }, + "git.autofetchPeriod": { + "type": "number", + "scope": "resource", + "description": "%config.autofetchPeriod%", + "default": 180 + }, "git.branchValidationRegex": { "type": "string", "description": "%config.branchValidationRegex%", @@ -1133,6 +1182,7 @@ "%config.postCommitCommand.sync%" ], "markdownDescription": "%config.postCommitCommand%", + "scope": "resource", "default": "none" }, "git.showInlineOpenFileAction": { @@ -1160,6 +1210,14 @@ "default": 72, "description": "%config.inputValidationLength%" }, + "git.inputValidationSubjectLength": { + "type": [ + "number", + "null" + ], + "default": 50, + "description": "%config.inputValidationSubjectLength%" + }, "git.detectSubmodules": { "type": "boolean", "scope": "resource", @@ -1214,6 +1272,12 @@ "default": false, "description": "%config.fetchOnPull%" }, + "git.autoStash": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%config.autoStash%" + }, "git.allowForcePush": { "type": "boolean", "default": false, @@ -1231,6 +1295,7 @@ }, "git.openDiffOnClick": { "type": "boolean", + "scope": "resource", "default": true, "description": "%config.openDiffOnClick%" } @@ -1385,7 +1450,7 @@ "file-type": "^7.2.0", "iconv-lite": "^0.4.24", "jschardet": "^1.6.0", - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", "which": "^1.3.0" }, @@ -1393,7 +1458,7 @@ "@types/byline": "4.2.31", "@types/file-type": "^5.2.1", "@types/mocha": "2.2.43", - "@types/node": "^8.10.25", + "@types/node": "^10.12.21", "@types/which": "^1.0.28", "mocha": "^3.2.0" } diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 356d1473bbc..892e3519cd1 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -32,6 +32,7 @@ "command.undoCommit": "Undo Last Commit", "command.checkout": "Checkout to...", "command.branch": "Create Branch...", + "command.branchFrom": "Create Branch From...", "command.deleteBranch": "Delete Branch...", "command.renameBranch": "Rename Branch...", "command.merge": "Merge Branch...", @@ -48,6 +49,8 @@ "command.pushToForce": "Push to... (Force)", "command.pushWithTags": "Push With Tags", "command.pushWithTagsForce": "Push With Tags (Force)", + "command.addRemote": "Add Remote", + "command.removeRemote": "Remove Remote", "command.sync": "Sync", "command.syncRebase": "Sync (Rebase)", "command.publish": "Publish Branch", @@ -68,6 +71,7 @@ "config.autoRepositoryDetection.openEditors": "Scan for parent folders of open files.", "config.autorefresh": "Whether auto refreshing is enabled.", "config.autofetch": "When enabled, commits will automatically be fetched from the default remote of the current Git repository.", + "config.autofetchPeriod": "Duration in seconds between each automatic git fetch, when `git.autofetch` is enabled.", "config.confirmSync": "Confirm before synchronizing git repositories.", "config.countBadge": "Controls the git badge counter.", "config.countBadge.all": "Count all changes.", @@ -97,6 +101,7 @@ "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", "config.inputValidation": "Controls when to show commit message input validation.", "config.inputValidationLength": "Controls the commit message length threshold for showing a warning.", + "config.inputValidationSubjectLength": "Controls the commit message subject length threshold for showing a warning. Unset it to inherit the value of `config.inputValidationLength`.", "config.detectSubmodules": "Controls whether to automatically detect git submodules.", "config.detectSubmodulesLimit": "Controls the limit of git submodules detected.", "config.alwaysShowStagedChangesResourceGroup": "Always show the Staged Changes resource group.", @@ -107,6 +112,7 @@ "config.rebaseWhenSync": "Force git to use rebase when running the sync command.", "config.confirmEmptyCommits": "Always confirm the creation of empty commits.", "config.fetchOnPull": "Fetch all branches when pulling or just the current one.", + "config.autoStash": "Stash any changes before pulling and restore them after successful pull.", "config.allowForcePush": "Controls whether force push (with or without lease) is enabled.", "config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.", "config.confirmForcePush": "Controls whether to ask for confirmation before force-pushing.", diff --git a/extensions/git/resources/icons/dark/open-change.svg b/extensions/git/resources/icons/dark/open-change.svg index e43ba7616c5..6f785c26a5e 100644 --- a/extensions/git/resources/icons/dark/open-change.svg +++ b/extensions/git/resources/icons/dark/open-change.svg @@ -1 +1 @@ -Compare_16x \ No newline at end of file + \ No newline at end of file diff --git a/extensions/git/resources/icons/light/open-change.svg b/extensions/git/resources/icons/light/open-change.svg index e53964f3b81..873b93d8106 100644 --- a/extensions/git/resources/icons/light/open-change.svg +++ b/extensions/git/resources/icons/light/open-change.svg @@ -1 +1 @@ -Compare_16x \ No newline at end of file + \ No newline at end of file diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 9469580d97b..25742babc15 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -5,7 +5,7 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl } from 'vscode'; import { mapEvent } from '../util'; @@ -76,6 +76,10 @@ export class ApiRepository implements Repository { return this._repository.setConfig(key, value); } + getGlobalConfig(key: string): Promise { + return this._repository.getGlobalConfig(key); + } + getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> { return this._repository.getObjectDetails(treeish, path); } @@ -104,19 +108,27 @@ export class ApiRepository implements Repository { return this._repository.diff(cached); } - diffWithHEAD(path: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string): Promise { return this._repository.diffWithHEAD(path); } - diffWith(ref: string, path: string): Promise { + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string): Promise { return this._repository.diffWith(ref, path); } - diffIndexWithHEAD(path: string): Promise { + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string): Promise { return this._repository.diffIndexWithHEAD(path); } - diffIndexWith(ref: string, path: string): Promise { + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string): Promise { return this._repository.diffIndexWith(ref, path); } @@ -124,7 +136,9 @@ export class ApiRepository implements Repository { return this._repository.diffBlobs(object1, object2); } - diffBetween(ref1: string, ref2: string, path: string): Promise { + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string): Promise { return this._repository.diffBetween(ref1, ref2, path); } @@ -168,17 +182,25 @@ export class ApiRepository implements Repository { return this._repository.removeRemote(name); } - fetch(remote?: string | undefined, ref?: string | undefined): Promise { - return this._repository.fetch(remote, ref); + fetch(remote?: string | undefined, ref?: string | undefined, depth?: number | undefined): Promise { + return this._repository.fetch(remote, ref, depth); } - pull(): Promise { - return this._repository.pull(); + pull(unshallow?: boolean): Promise { + return this._repository.pull(undefined, unshallow); } push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise { return this._repository.pushTo(remoteName, branchName, setUpstream); } + + blame(path: string): Promise { + return this._repository.blame(path); + } + + log(options?: LogOptions): Promise { + return this._repository.log(options); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index ea4d1f97d6c..ae8eb5315bc 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -41,6 +41,7 @@ export interface Commit { readonly hash: string; readonly message: string; readonly parents: string[]; + readonly authorEmail?: string | undefined; } export interface Submodule { @@ -67,6 +68,7 @@ export const enum Status { DELETED, UNTRACKED, IGNORED, + INTENT_TO_ADD, ADDED_BY_US, ADDED_BY_THEM, @@ -109,6 +111,14 @@ export interface RepositoryUIState { readonly onDidChange: Event; } +/** + * Log options. + */ +export interface LogOptions { + /** Max number of log entries to retrieve. If not specified, the default is 32. */ + readonly maxEntries?: number; +} + export interface Repository { readonly rootUri: Uri; @@ -119,6 +129,7 @@ export interface Repository { getConfigs(): Promise<{ key: string; value: string; }[]>; getConfig(key: string): Promise; setConfig(key: string, value: string): Promise; + getGlobalConfig(key: string): Promise; getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }>; @@ -130,11 +141,16 @@ export interface Repository { apply(patch: string, reverse?: boolean): Promise; diff(cached?: boolean): Promise; + diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; + diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; + diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; + diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffBlobs(object1: string, object2: string): Promise; + diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; hashObject(data: string): Promise; @@ -152,9 +168,12 @@ export interface Repository { addRemote(name: string, url: string): Promise; removeRemote(name: string): Promise; - fetch(remote?: string, ref?: string): Promise; - pull(): Promise; + fetch(remote?: string, ref?: string, depth?: number): Promise; + pull(unshallow?: boolean): Promise; push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise; + + blame(path: string): Promise; + log(options?: LogOptions): Promise; } export interface API { @@ -214,4 +233,6 @@ export const enum GitErrorCodes { WrongCase = 'WrongCase', CantLockRef = 'CantLockRef', CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', + PatchDoesNotApply = 'PatchDoesNotApply', + NoPathFound = 'NoPathFound' } \ No newline at end of file diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index b0dc28f8dd6..03000c7290d 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -65,7 +65,7 @@ export class Askpass implements Disposable { return ipcHandlePath; } - private onRequest(req: http.ServerRequest, res: http.ServerResponse): void { + private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void { const chunks: string[] = []; req.setEncoding('utf8'); req.on('data', (d: string) => chunks.push(d)); diff --git a/extensions/git/src/autofetch.ts b/extensions/git/src/autofetch.ts index bd95f7dc03f..9fd81bfabfe 100644 --- a/extensions/git/src/autofetch.ts +++ b/extensions/git/src/autofetch.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget } from 'vscode'; +import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget, Uri } from 'vscode'; import { Repository, Operation } from './repository'; import { eventToPromise, filterEvent, onceEvent } from './util'; import * as nls from 'vscode-nls'; @@ -17,7 +17,6 @@ function isRemoteOperation(operation: Operation): boolean { export class AutoFetcher { - private static readonly Period = 3 * 60 * 1000 /* three minutes */; private static DidInformUser = 'autofetch.didInformUser'; private _onDidChange = new EventEmitter(); @@ -61,7 +60,7 @@ export class AutoFetcher { } if (result === yes) { - const gitConfig = workspace.getConfiguration('git'); + const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); gitConfig.update('autofetch', true, ConfigurationTarget.Global); } @@ -69,7 +68,7 @@ export class AutoFetcher { } private onConfiguration(): void { - const gitConfig = workspace.getConfiguration('git'); + const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); if (gitConfig.get('autofetch') === false) { this.disable(); @@ -111,8 +110,10 @@ export class AutoFetcher { return; } - const timeout = new Promise(c => setTimeout(c, AutoFetcher.Period)); + const period = workspace.getConfiguration('git', Uri.file(this.repository.root)).get('autofetchPeriod', 180) * 1000; + const timeout = new Promise(c => setTimeout(c, period)); const whenDisabled = eventToPromise(filterEvent(this.onDidChange, enabled => !enabled)); + await Promise.race([timeout, whenDisabled]); } } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e41a8161c56..f6e00e9abdd 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -93,7 +93,7 @@ class CreateBranchItem implements QuickPickItem { constructor(private cc: CommandCenter) { } - get label(): string { return localize('create branch', '$(plus) Create new branch'); } + get label(): string { return localize('create branch', '$(plus) Create new branch...'); } get description(): string { return ''; } get alwaysShow(): boolean { return true; } @@ -103,6 +103,29 @@ class CreateBranchItem implements QuickPickItem { } } +class CreateBranchFromItem implements QuickPickItem { + + constructor(private cc: CommandCenter) { } + + get label(): string { return localize('create branch from', '$(plus) Create new branch from...'); } + get description(): string { return ''; } + + get alwaysShow(): boolean { return true; } + + async run(repository: Repository): Promise { + await this.cc.branch(repository); + } +} + +class HEADItem implements QuickPickItem { + + constructor(private repository: Repository) { } + + get label(): string { return 'HEAD'; } + get description(): string { return (this.repository.HEAD && this.repository.HEAD.commit || '').substr(0, 8); } + get alwaysShow(): boolean { return true; } +} + interface CommandOptions { repository?: boolean; diff?: boolean; @@ -154,6 +177,22 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ return { merge, resolved, unresolved, deletionConflicts }; } +function createCheckoutItems(repository: Repository): CheckoutItem[] { + const config = workspace.getConfiguration('git'); + const checkoutType = config.get('checkoutType') || 'all'; + const includeTags = checkoutType === 'all' || checkoutType === 'tags'; + const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; + + const heads = repository.refs.filter(ref => ref.type === RefType.Head) + .map(ref => new CheckoutItem(ref)); + const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) + .map(ref => new CheckoutTagItem(ref)); + const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) + .map(ref => new CheckoutRemoteHeadItem(ref)); + + return [...heads, ...tags, ...remoteHeads]; +} + enum PushType { Push, PushTo, @@ -340,6 +379,7 @@ export class CommandCenter { case Status.MODIFIED: case Status.UNTRACKED: case Status.IGNORED: + case Status.INTENT_TO_ADD: const repository = this.model.getRepository(resource.resourceUri); if (!repository) { @@ -496,6 +536,7 @@ export class CommandCenter { @command('git.init') async init(): Promise { let repositoryPath: string | undefined = undefined; + let askToOpen = true; if (workspace.workspaceFolders) { const placeHolder = localize('init', "Pick workspace folder to initialize git repo in"); @@ -510,6 +551,7 @@ export class CommandCenter { return; } else if (item.folder) { repositoryPath = item.folder.uri.fsPath; + askToOpen = false; } } @@ -543,6 +585,10 @@ export class CommandCenter { } repositoryPath = uri.fsPath; + + if (workspace.workspaceFolders && workspace.workspaceFolders.some(w => w.uri.toString() === uri.toString())) { + askToOpen = false; + } } await this.git.init(repositoryPath); @@ -552,6 +598,10 @@ export class CommandCenter { const open = localize('openrepo', "Open Repository"); choices.push(open); + if (!askToOpen) { + return; + } + const addToWorkspace = localize('add', "Add to Workspace"); if (workspace.workspaceFolders) { message = localize('proposeopen2 init', "Would you like to open the initialized repository, or add it to the current workspace?"); @@ -613,14 +663,16 @@ export class CommandCenter { if (!(resource instanceof Resource)) { // can happen when called from a keybinding + console.log('WHAT'); resource = this.getSCMResource(); } if (resource) { - const resources = ([resource, ...resourceStates] as Resource[]) - .filter(r => r.type !== Status.DELETED && r.type !== Status.INDEX_DELETED); - - uris = resources.map(r => r.resourceUri); + uris = ([resource, ...resourceStates] as Resource[]) + .filter(r => r.type !== Status.DELETED && r.type !== Status.INDEX_DELETED) + .map(r => r.resourceUri); + } else if (window.activeTextEditor) { + uris = [window.activeTextEditor.document.uri]; } } @@ -629,6 +681,7 @@ export class CommandCenter { } const activeTextEditor = window.activeTextEditor; + for (const uri of uris) { const opts: TextDocumentShowOptions = { preserveFocus, @@ -1384,55 +1437,59 @@ export class CommandCenter { return true; } - const config = workspace.getConfiguration('git'); - const checkoutType = config.get('checkoutType') || 'all'; - const includeTags = checkoutType === 'all' || checkoutType === 'tags'; - const includeRemotes = checkoutType === 'all' || checkoutType === 'remote'; - const createBranch = new CreateBranchItem(this); - - const heads = repository.refs.filter(ref => ref.type === RefType.Head) - .map(ref => new CheckoutItem(ref)); - - const tags = (includeTags ? repository.refs.filter(ref => ref.type === RefType.Tag) : []) - .map(ref => new CheckoutTagItem(ref)); - - const remoteHeads = (includeRemotes ? repository.refs.filter(ref => ref.type === RefType.RemoteHead) : []) - .map(ref => new CheckoutRemoteHeadItem(ref)); - - const picks = [createBranch, ...heads, ...tags, ...remoteHeads]; + const createBranchFrom = new CreateBranchFromItem(this); + const picks = [createBranch, createBranchFrom, ...createCheckoutItems(repository)]; const placeHolder = localize('select a ref to checkout', 'Select a ref to checkout'); - const choice = await window.showQuickPick(picks, { placeHolder }); + + const quickpick = window.createQuickPick(); + quickpick.items = picks; + quickpick.placeholder = placeHolder; + quickpick.ignoreFocusOut = true; + quickpick.show(); + + const choice = await new Promise(c => quickpick.onDidAccept(() => c(quickpick.activeItems[0]))); + quickpick.hide(); if (!choice) { return false; } - await choice.run(repository); + if (choice === createBranch) { + await this._branch(repository, quickpick.value); + } else if (choice === createBranchFrom) { + await this._branch(repository, quickpick.value, true); + } else { + await (choice as CheckoutItem).run(repository); + } + return true; } @command('git.branch', { repository: true }) async branch(repository: Repository): Promise { + await this._branch(repository); + } + + @command('git.branchFrom', { repository: true }) + async branchFrom(repository: Repository): Promise { + await this._branch(repository, undefined, true); + } + + private async _branch(repository: Repository, defaultName?: string, from = false): Promise { const config = workspace.getConfiguration('git'); - const branchValidationRegex = config.get('branchValidationRegex')!; const branchWhitespaceChar = config.get('branchWhitespaceChar')!; - const validateName = new RegExp(branchValidationRegex); - const sanitize = (name: string) => { - name = name.trim(); + const branchValidationRegex = config.get('branchValidationRegex')!; + const sanitize = (name: string) => name ? + name.trim().replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar) + : name; - if (!name) { - return name; - } - - return name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, branchWhitespaceChar); - }; - - const result = await window.showInputBox({ + const rawBranchName = defaultName || await window.showInputBox({ placeHolder: localize('branch name', "Branch name"), prompt: localize('provide branch name', "Please provide a branch name"), ignoreFocusOut: true, validateInput: (name: string) => { + const validateName = new RegExp(branchValidationRegex); if (validateName.test(sanitize(name))) { return null; } @@ -1441,13 +1498,27 @@ export class CommandCenter { } }); - const name = sanitize(result || ''); + const branchName = sanitize(rawBranchName || ''); - if (!name) { + if (!branchName) { return; } - await repository.branch(name, true); + let target = 'HEAD'; + + if (from) { + const picks = [new HEADItem(repository), ...createCheckoutItems(repository)]; + const placeHolder = localize('select a ref to create a new branch from', 'Select a ref to create the \'{0}\' branch from', branchName); + const choice = await window.showQuickPick(picks, { placeHolder }); + + if (!choice) { + return; + } + + target = choice.label; + } + + await repository.branch(branchName, true, target); } @command('git.deleteBranch', { repository: true }) @@ -1761,6 +1832,72 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushTo, forcePush: true }); } + @command('git.addRemote', { repository: true }) + async addRemote(repository: Repository): Promise { + const remotes = repository.remotes; + + const sanitize = (name: string) => { + name = name.trim(); + return name && name.replace(/^\.|\/\.|\.\.|~|\^|:|\/$|\.lock$|\.lock\/|\\|\*|\s|^\s*$|\.$|\[|\]$/g, '-'); + }; + + const resultName = await window.showInputBox({ + placeHolder: localize('remote name', "Remote name"), + prompt: localize('provide remote name', "Please provide a remote name"), + ignoreFocusOut: true, + validateInput: (name: string) => { + if (sanitize(name)) { + return null; + } + return localize('remote name format invalid', "Remote name format invalid"); + } + }); + + const name = sanitize(resultName || ''); + + if (!name) { + return; + } + + if (remotes.find(r => r.name === name)) { + window.showErrorMessage(localize('remote already exists', "Remote '{0}' already exists.", name)); + return; + } + + const url = await window.showInputBox({ + placeHolder: localize('remote url', "Remote URL"), + prompt: localize('provide remote URL', "Enter URL for remote \"{0}\"", name), + ignoreFocusOut: true + }); + + if (!url) { + return; + } + + await repository.addRemote(name, url); + } + + @command('git.removeRemote', { repository: true }) + async removeRemote(repository: Repository): Promise { + const remotes = repository.remotes; + + if (remotes.length === 0) { + window.showErrorMessage(localize('no remotes added', "Your repository has no remotes.")); + return; + } + + const picks = remotes.map(r => r.name); + const placeHolder = localize('remove remote', "Pick a remote to remove"); + + const remoteName = await window.showQuickPick(picks, { placeHolder }); + + if (!remoteName) { + return; + } + + await repository.removeRemote(remoteName); + } + private async _sync(repository: Repository, rebase: boolean): Promise { const HEAD = repository.HEAD; @@ -2078,6 +2215,7 @@ export class CommandCenter { uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri); this.outputChannel.appendLine(`git.getSCMResource.uri ${uri && uri.toString()}`); + for (const r of this.model.repositories.map(r => r.root)) { this.outputChannel.appendLine(`repo root ${r}`); } diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 0c27365bc51..9f17b20ee8e 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -20,7 +20,6 @@ class GitIgnoreDecorationProvider implements DecorationProvider { private disposables: Disposable[] = []; constructor(private model: Model) { - //todo@joh -> events when the ignore status actually changes, not only when the file changes this.onDidChangeDecorations = fireEvent(anyEvent( filterEvent(workspace.onDidSaveTextDocument, e => e.fileName.endsWith('.gitignore')), model.onDidOpenRepository, @@ -119,7 +118,7 @@ class GitDecorationProvider implements DecorationProvider { const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()])); this.decorations = newDecorations; - this._onDidChangeDecorations.fire([...uris.values()].map(Uri.parse)); + this._onDidChangeDecorations.fire([...uris.values()].map(value => Uri.parse(value, true))); } private collectDecorationData(group: GitResourceGroup, bucket: Map): void { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index ff7e7bdd607..42952e0ca05 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,9 +12,9 @@ import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; -import { CancellationToken } from 'vscode'; +import { CancellationToken, Uri } from 'vscode'; import { detectEncoding } from './encoding'; -import { Ref, RefType, Branch, Remote, GitErrorCodes } from './api/git'; +import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git'; const readfile = denodeify(fs.readFile); @@ -114,17 +114,17 @@ function findGitWin32InPath(onLookup: (path: string) => void): Promise { function findGitWin32(onLookup: (path: string) => void): Promise { return findSystemGitWin32(process.env['ProgramW6432'] as string, onLookup) - .then(void 0, () => findSystemGitWin32(process.env['ProgramFiles(x86)'] as string, onLookup)) - .then(void 0, () => findSystemGitWin32(process.env['ProgramFiles'] as string, onLookup)) - .then(void 0, () => findSystemGitWin32(path.join(process.env['LocalAppData'] as string, 'Programs'), onLookup)) - .then(void 0, () => findGitWin32InPath(onLookup)); + .then(undefined, () => findSystemGitWin32(process.env['ProgramFiles(x86)'] as string, onLookup)) + .then(undefined, () => findSystemGitWin32(process.env['ProgramFiles'] as string, onLookup)) + .then(undefined, () => findSystemGitWin32(path.join(process.env['LocalAppData'] as string, 'Programs'), onLookup)) + .then(undefined, () => findGitWin32InPath(onLookup)); } export function findGit(hint: string | undefined, onLookup: (path: string) => void): Promise { const first = hint ? findSpecificGit(hint, onLookup) : Promise.reject(null); return first - .then(void 0, () => { + .then(undefined, () => { switch (process.platform) { case 'darwin': return findGitDarwin(onLookup); case 'win32': return findGitWin32(onLookup); @@ -248,7 +248,7 @@ export class GitError { this.error = data.error; this.message = data.error.message; } else { - this.error = void 0; + this.error = undefined; this.message = ''; } @@ -306,11 +306,15 @@ function getGitErrorCode(stderr: string): string | undefined { return GitErrorCodes.BranchAlreadyExists; } else if (/'.+' is not a valid branch name/.test(stderr)) { return GitErrorCodes.InvalidBranchName; + } else if (/Please,? commit your changes or stash them/.test(stderr)) { + return GitErrorCodes.DirtyWorkTree; } - return void 0; + return undefined; } +const COMMIT_FORMAT = '%H\n%ae\n%P\n%B'; + export class Git { readonly path: string; @@ -347,7 +351,7 @@ export class Git { await mkdirp(parentPath); try { - await this.exec(parentPath, ['clone', url, folderPath], { cancellationToken }); + await this.exec(parentPath, ['clone', url.includes(' ') ? encodeURI(url) : url, folderPath], { cancellationToken }); } catch (err) { if (err.stderr) { err.stderr = err.stderr.replace(/^Cloning.+$/m, '').trim(); @@ -450,6 +454,7 @@ export interface Commit { hash: string; message: string; parents: string[]; + authorEmail?: string | undefined; } export class GitStatusParser { @@ -581,13 +586,13 @@ export function parseGitmodules(raw: string): Submodule[] { } export function parseGitCommit(raw: string): Commit | null { - const match = /^([0-9a-f]{40})\n(.*)\n([^]*)$/m.exec(raw.trim()); + const match = /^([0-9a-f]{40})\n(.*)\n(.*)\n([^]*)$/m.exec(raw.trim()); if (!match) { return null; } - const parents = match[2] ? match[2].split(' ') : []; - return { hash: match[1], message: match[3], parents }; + const parents = match[3] ? match[3].split(' ') : []; + return { hash: match[1], message: match[4], parents, authorEmail: match[2] }; } interface LsTreeElement { @@ -629,6 +634,10 @@ export interface CommitOptions { empty?: boolean; } +export interface PullOptions { + unshallow?: boolean; +} + export enum ForcePushMode { Force, ForceWithLease @@ -697,6 +706,41 @@ export class Repository { }); } + async log(options?: LogOptions): Promise { + const maxEntries = options && typeof options.maxEntries === 'number' && options.maxEntries > 0 ? options.maxEntries : 32; + const args = ['log', '-' + maxEntries, `--pretty=format:${COMMIT_FORMAT}%x00%x00`]; + const gitResult = await this.run(args); + if (gitResult.exitCode) { + // An empty repo. + return []; + } + + const s = gitResult.stdout; + const result: Commit[] = []; + let index = 0; + while (index < s.length) { + let nextIndex = s.indexOf('\x00\x00', index); + if (nextIndex === -1) { + nextIndex = s.length; + } + + let entry = s.substr(index, nextIndex - index); + if (entry.startsWith('\n')) { + entry = entry.substring(1); + } + + const commit = parseGitCommit(entry); + if (!commit) { + break; + } + + result.push(commit); + index = nextIndex + 2; + } + + return result; + } + async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false): Promise { const stdout = await this.buffer(object); @@ -829,7 +873,15 @@ export class Repository { args.push('-R'); } - await this.run(args); + try { + await this.run(args); + } catch (err) { + if (/patch does not apply/.test(err.stderr)) { + err.gitErrorCode = GitErrorCodes.PatchDoesNotApply; + } + + throw err; + } } async diff(cached = false): Promise { @@ -843,25 +895,53 @@ export class Repository { return result.stdout; } - async diffWithHEAD(path: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string | undefined): Promise; + async diffWithHEAD(path?: string | undefined): Promise { + if (!path) { + return await this.diffFiles(false); + } + const args = ['diff', '--', path]; const result = await this.run(args); return result.stdout; } - async diffWith(ref: string, path: string): Promise { + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string | undefined): Promise; + async diffWith(ref: string, path?: string): Promise { + if (!path) { + return await this.diffFiles(false, ref); + } + const args = ['diff', ref, '--', path]; const result = await this.run(args); return result.stdout; } - async diffIndexWithHEAD(path: string): Promise { + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string | undefined): Promise; + async diffIndexWithHEAD(path?: string): Promise { + if (!path) { + return await this.diffFiles(true); + } + const args = ['diff', '--cached', '--', path]; const result = await this.run(args); return result.stdout; } - async diffIndexWith(ref: string, path: string): Promise { + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string | undefined): Promise; + async diffIndexWith(ref: string, path?: string): Promise { + if (!path) { + return await this.diffFiles(true, ref); + } + const args = ['diff', '--cached', ref, '--', path]; const result = await this.run(args); return result.stdout; @@ -873,13 +953,102 @@ export class Repository { return result.stdout; } - async diffBetween(ref1: string, ref2: string, path: string): Promise { - const args = ['diff', `${ref1}...${ref2}`, '--', path]; + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string | undefined): Promise; + async diffBetween(ref1: string, ref2: string, path?: string): Promise { + const range = `${ref1}...${ref2}`; + if (!path) { + return await this.diffFiles(false, range); + } + + const args = ['diff', range, '--', path]; const result = await this.run(args); return result.stdout.trim(); } + private async diffFiles(cached: boolean, ref?: string): Promise { + const args = ['diff', '--name-status', '-z', '--diff-filter=ADMR']; + if (cached) { + args.push('--cached'); + } + + if (ref) { + args.push(ref); + } + + const gitResult = await this.run(args); + if (gitResult.exitCode) { + return []; + } + + const entries = gitResult.stdout.split('\x00'); + let index = 0; + const result: Change[] = []; + + entriesLoop: + while (index < entries.length - 1) { + const change = entries[index++]; + const resourcePath = entries[index++]; + if (!change || !resourcePath) { + break; + } + + const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath)); + let status: Status = Status.UNTRACKED; + + // Copy or Rename status comes with a number, e.g. 'R100'. We don't need the number, so we use only first character of the status. + switch (change[0]) { + case 'M': + status = Status.MODIFIED; + break; + + case 'A': + status = Status.INDEX_ADDED; + break; + + case 'D': + status = Status.DELETED; + break; + + // Rename contains two paths, the second one is what the file is renamed/copied to. + case 'R': + if (index >= entries.length) { + break; + } + + const newPath = entries[index++]; + if (!newPath) { + break; + } + + const uri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath)); + result.push({ + uri, + renameUri: uri, + originalUri, + status: Status.INDEX_RENAMED + }); + + continue; + + default: + // Unknown status + break entriesLoop; + } + + result.push({ + status, + originalUri, + uri: originalUri, + renameUri: originalUri, + }); + } + + return result; + } + async getMergeBase(ref1: string, ref2: string): Promise { const args = ['merge-base', ref1, ref2]; const result = await this.run(args); @@ -1034,7 +1203,7 @@ export class Repository { } async branch(name: string, checkout: boolean, ref?: string): Promise { - const args = checkout ? ['checkout', '-q', '-b', name] : ['branch', '-q', name]; + const args = checkout ? ['checkout', '-q', '-b', name, '--no-track'] : ['branch', '-q', name]; if (ref) { args.push(ref); @@ -1158,7 +1327,7 @@ export class Repository { await this.run(args); } - async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean } = {}): Promise { + async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number } = {}): Promise { const args = ['fetch']; if (options.remote) { @@ -1175,6 +1344,9 @@ export class Repository { args.push('--prune'); } + if (typeof options.depth === 'number') { + args.push(`--depth=${options.depth}`); + } try { await this.run(args); @@ -1189,9 +1361,13 @@ export class Repository { } } - async pull(rebase?: boolean, remote?: string, branch?: string): Promise { + async pull(rebase?: boolean, remote?: string, branch?: string, options: PullOptions = {}): Promise { const args = ['pull', '--tags']; + if (options.unshallow) { + args.push('--unshallow'); + } + if (rebase) { args.push('-r'); } @@ -1263,16 +1439,33 @@ export class Repository { } } + async blame(path: string): Promise { + try { + const args = ['blame']; + args.push(path); + + let result = await this.run(args); + + return result.stdout.trim(); + } catch (err) { + if (/^fatal: no such path/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.NoPathFound; + } + + throw err; + } + } + async createStash(message?: string, includeUntracked?: boolean): Promise { try { - const args = ['stash', 'save']; + const args = ['stash', 'push']; if (includeUntracked) { args.push('-u'); } if (message) { - args.push('--', message); + args.push('-m', message); } await this.run(args); @@ -1368,7 +1561,7 @@ export class Repository { throw new Error('Not in a branch'); } - return { name: result.stdout.trim(), commit: void 0, type: RefType.Head }; + return { name: result.stdout.trim(), commit: undefined, type: RefType.Head }; } catch (err) { const result = await this.run(['rev-parse', 'HEAD']); @@ -1376,7 +1569,7 @@ export class Repository { throw new Error('Error parsing HEAD'); } - return { name: void 0, commit: result.stdout.trim(), type: RefType.Head }; + return { name: undefined, commit: result.stdout.trim(), type: RefType.Head }; } } @@ -1521,7 +1714,7 @@ export class Repository { } async getCommit(ref: string): Promise { - const result = await this.run(['show', '-s', '--format=%H\n%P\n%B', ref]); + const result = await this.run(['show', '-s', `--format=${COMMIT_FORMAT}`, ref]); return parseGitCommit(result.stdout) || Promise.reject('bad commit format'); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index d188eea026e..2fac9e2a39f 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -13,7 +13,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, Status } from './api/git'; +import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, LogOptions, Change } from './api/git'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -94,6 +94,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_COPIED: return Resource.Icons[theme].Copied; case Status.UNTRACKED: return Resource.Icons[theme].Untracked; case Status.IGNORED: return Resource.Icons[theme].Ignored; + case Status.INTENT_TO_ADD: return Resource.Icons[theme].Added; case Status.BOTH_DELETED: return Resource.Icons[theme].Conflict; case Status.ADDED_BY_US: return Resource.Icons[theme].Conflict; case Status.DELETED_BY_THEM: return Resource.Icons[theme].Conflict; @@ -116,6 +117,7 @@ export class Resource implements SourceControlResourceState { case Status.INDEX_COPIED: return localize('index copied', "Index Copied"); case Status.UNTRACKED: return localize('untracked', "Untracked"); case Status.IGNORED: return localize('ignored', "Ignored"); + case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add"); case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted"); case Status.ADDED_BY_US: return localize('added by us', "Added By Us"); case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them"); @@ -166,6 +168,7 @@ export class Resource implements SourceControlResourceState { case Status.MODIFIED: return 'M'; case Status.INDEX_ADDED: + case Status.INTENT_TO_ADD: return 'A'; case Status.INDEX_DELETED: case Status.DELETED: @@ -201,8 +204,9 @@ export class Resource implements SourceControlResourceState { case Status.DELETED: return new ThemeColor('gitDecoration.deletedResourceForeground'); case Status.INDEX_ADDED: + case Status.INTENT_TO_ADD: return new ThemeColor('gitDecoration.addedResourceForeground'); - case Status.INDEX_RENAMED: // todo@joh - special color? + case Status.INDEX_RENAMED: case Status.UNTRACKED: return new ThemeColor('gitDecoration.untrackedResourceForeground'); case Status.IGNORED: @@ -295,7 +299,9 @@ export const enum Operation { GetObjectDetails = 'GetObjectDetails', SubmoduleUpdate = 'SubmoduleUpdate', RebaseContinue = 'RebaseContinue', - Apply = 'Apply' + Apply = 'Apply', + Blame = 'Blame', + Log = 'Log', } function isReadOnly(operation: Operation): boolean { @@ -643,18 +649,29 @@ export class Repository implements Disposable { }; } + let lineNumber = 0; let start = 0, end; let match: RegExpExecArray | null; const regex = /\r?\n/g; while ((match = regex.exec(text)) && position > match.index) { start = match.index + match[0].length; + lineNumber++; } end = match ? match.index : text.length; const line = text.substring(start, end); - const threshold = Math.max(config.get('inputValidationLength') || 72, 0) || 72; + + let threshold = config.get('inputValidationLength', 50); + + if (lineNumber === 0) { + const inputValidationSubjectLength = config.get('inputValidationSubjectLength', null); + + if (inputValidationSubjectLength !== null) { + threshold = inputValidationSubjectLength; + } + } if (line.length <= threshold) { if (setting !== 'always') { @@ -697,10 +714,18 @@ export class Repository implements Disposable { return this.run(Operation.Config, () => this.repository.config('local', key)); } + getGlobalConfig(key: string): Promise { + return this.run(Operation.Config, () => this.repository.config('global', key)); + } + setConfig(key: string, value: string): Promise { return this.run(Operation.Config, () => this.repository.config('local', key, value)); } + log(options?: LogOptions): Promise { + return this.run(Operation.Log, () => this.repository.log(options)); + } + @throttle async status(): Promise { await this.run(Operation.Status); @@ -710,19 +735,31 @@ export class Repository implements Disposable { return this.run(Operation.Diff, () => this.repository.diff(cached)); } - diffWithHEAD(path: string): Promise { + diffWithHEAD(): Promise; + diffWithHEAD(path: string): Promise; + diffWithHEAD(path?: string | undefined): Promise; + diffWithHEAD(path?: string | undefined): Promise { return this.run(Operation.Diff, () => this.repository.diffWithHEAD(path)); } - diffWith(ref: string, path: string): Promise { + diffWith(ref: string): Promise; + diffWith(ref: string, path: string): Promise; + diffWith(ref: string, path?: string | undefined): Promise; + diffWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffWith(ref, path)); } - diffIndexWithHEAD(path: string): Promise { + diffIndexWithHEAD(): Promise; + diffIndexWithHEAD(path: string): Promise; + diffIndexWithHEAD(path?: string | undefined): Promise; + diffIndexWithHEAD(path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWithHEAD(path)); } - diffIndexWith(ref: string, path: string): Promise { + diffIndexWith(ref: string): Promise; + diffIndexWith(ref: string, path: string): Promise; + diffIndexWith(ref: string, path?: string | undefined): Promise; + diffIndexWith(ref: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffIndexWith(ref, path)); } @@ -730,7 +767,10 @@ export class Repository implements Disposable { return this.run(Operation.Diff, () => this.repository.diffBlobs(object1, object2)); } - diffBetween(ref1: string, ref2: string, path: string): Promise { + diffBetween(ref1: string, ref2: string): Promise; + diffBetween(ref1: string, ref2: string, path: string): Promise; + diffBetween(ref1: string, ref2: string, path?: string | undefined): Promise; + diffBetween(ref1: string, ref2: string, path?: string): Promise { return this.run(Operation.Diff, () => this.repository.diffBetween(ref1, ref2, path)); } @@ -904,8 +944,8 @@ export class Repository implements Disposable { await this.run(Operation.Fetch, () => this.repository.fetch({ all: true })); } - async fetch(remote?: string, ref?: string): Promise { - await this.run(Operation.Fetch, () => this.repository.fetch({ remote, ref })); + async fetch(remote?: string, ref?: string, depth?: number): Promise { + await this.run(Operation.Fetch, () => this.repository.fetch({ remote, ref, depth })); } @throttle @@ -918,18 +958,11 @@ export class Repository implements Disposable { branch = `${head.upstream.name}`; } - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); - - if (fetchOnPull) { - await this.run(Operation.Pull, () => this.repository.pull(true)); - } else { - await this.run(Operation.Pull, () => this.repository.pull(true, remote, branch)); - } + return this.pullFrom(true, remote, branch); } @throttle - async pull(head?: Branch): Promise { + async pull(head?: Branch, unshallow?: boolean): Promise { let remote: string | undefined; let branch: string | undefined; @@ -938,25 +971,22 @@ export class Repository implements Disposable { branch = `${head.upstream.name}`; } - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); - - if (fetchOnPull) { - await this.run(Operation.Pull, () => this.repository.pull(false)); - } else { - await this.run(Operation.Pull, () => this.repository.pull(false, remote, branch)); - } + return this.pullFrom(false, remote, branch, unshallow); } - async pullFrom(rebase?: boolean, remote?: string, branch?: string): Promise { - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); + async pullFrom(rebase?: boolean, remote?: string, branch?: string, unshallow?: boolean): Promise { + await this.run(Operation.Pull, async () => { + await this.maybeAutoStash(async () => { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const fetchOnPull = config.get('fetchOnPull'); - if (fetchOnPull) { - await this.run(Operation.Pull, () => this.repository.pull(rebase)); - } else { - await this.run(Operation.Pull, () => this.repository.pull(rebase, remote, branch)); - } + if (fetchOnPull) { + await this.repository.pull(rebase, undefined, undefined, { unshallow }); + } else { + await this.repository.pull(rebase, remote, branch, { unshallow }); + } + }); + }); } @throttle @@ -980,6 +1010,10 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode)); } + async blame(path: string): Promise { + return await this.run(Operation.Blame, () => this.repository.blame(path)); + } + @throttle sync(head: Branch): Promise { return this._sync(head, false); @@ -1002,26 +1036,28 @@ export class Repository implements Disposable { } await this.run(Operation.Sync, async () => { - const config = workspace.getConfiguration('git', Uri.file(this.root)); - const fetchOnPull = config.get('fetchOnPull'); + await this.maybeAutoStash(async () => { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const fetchOnPull = config.get('fetchOnPull'); - if (fetchOnPull) { - await this.repository.pull(rebase); - } else { - await this.repository.pull(rebase, remoteName, pullBranch); - } + if (fetchOnPull) { + await this.repository.pull(rebase); + } else { + await this.repository.pull(rebase, remoteName, pullBranch); + } - const remote = this.remotes.find(r => r.name === remoteName); + const remote = this.remotes.find(r => r.name === remoteName); - if (remote && remote.isReadOnly) { - return; - } + if (remote && remote.isReadOnly) { + return; + } - const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); + const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true); - if (shouldPush) { - await this.repository.push(remoteName, pushBranch); - } + if (shouldPush) { + await this.repository.push(remoteName, pushBranch); + } + }); }); } @@ -1102,7 +1138,8 @@ export class Repository implements Disposable { const text = lastLine.isEmptyOrWhitespace ? `${textToAppend}\n` : `\n${textToAppend}\n`; edit.insert(document.uri, lastLine.range.end, text); - workspace.applyEdit(edit); + await workspace.applyEdit(edit); + await document.save(); }); } @@ -1211,6 +1248,24 @@ export class Repository implements Disposable { } } + private static KnownHugeFolderNames = ['node_modules']; + + private async findKnownHugeFolderPathsToIgnore(): Promise { + const folderPaths: string[] = []; + + for (const folderName of Repository.KnownHugeFolderNames) { + const folderPath = path.join(this.repository.root, folderName); + + if (await new Promise(c => fs.exists(folderPath, c))) { + folderPaths.push(folderPath); + } + } + + const ignored = await this.checkIgnore(folderPaths); + + return folderPaths.filter(p => !ignored.has(p)); + } + @throttle private async updateModelState(): Promise { const { status, didHitLimit } = await this.repository.getStatus(); @@ -1221,15 +1276,34 @@ export class Repository implements Disposable { this.isRepositoryHuge = didHitLimit; if (didHitLimit && !shouldIgnore && !this.didWarnAboutLimit) { + const knownHugeFolderPaths = await this.findKnownHugeFolderPathsToIgnore(); + const gitWarn = localize('huge', "The git repository at '{0}' has too many active changes, only a subset of Git features will be enabled.", this.repository.root); const neverAgain = { title: localize('neveragain', "Don't Show Again") }; - window.showWarningMessage(localize('huge', "The git repository at '{0}' has too many active changes, only a subset of Git features will be enabled.", this.repository.root), neverAgain).then(result => { + if (knownHugeFolderPaths.length > 0) { + const folderPath = knownHugeFolderPaths[0]; + const folderName = path.basename(folderPath); + + const addKnown = localize('add known', "Would you like to add '{0}' to .gitignore?", folderName); + const yes = { title: localize('yes', "Yes") }; + + const result = await window.showWarningMessage(`${gitWarn} ${addKnown}`, yes, neverAgain); + + if (result === neverAgain) { + config.update('ignoreLimitWarning', true, false); + this.didWarnAboutLimit = true; + } else if (result === yes) { + this.ignore([Uri.file(folderPath)]); + } + } else { + const result = await window.showWarningMessage(gitWarn, neverAgain); + if (result === neverAgain) { config.update('ignoreLimitWarning', true, false); } - }); - this.didWarnAboutLimit = true; + this.didWarnAboutLimit = true; + } } let HEAD: Branch | undefined; @@ -1287,6 +1361,7 @@ export class Repository implements Disposable { switch (raw.y) { case 'M': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; + case 'A': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; } return undefined; }); @@ -1339,6 +1414,22 @@ export class Repository implements Disposable { } } + private async maybeAutoStash(runOperation: () => Promise): Promise { + const config = workspace.getConfiguration('git', Uri.file(this.root)); + const shouldAutoStash = config.get('autoStash') + && this.workingTreeGroup.resourceStates.some(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED); + + if (!shouldAutoStash) { + return await runOperation(); + } + + await this.repository.createStash(undefined, true); + const result = await runOperation(); + await this.repository.popStash(); + + return result; + } + private onFSChange(_uri: Uri): void { const config = workspace.getConfiguration('git'); const autorefresh = config.get('autorefresh'); diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 1b9d03e96f0..f1e26c9ae86 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -13,7 +13,18 @@ export function applyLineChanges(original: TextDocument, modified: TextDocument, const isInsertion = diff.originalEndLineNumber === 0; const isDeletion = diff.modifiedEndLineNumber === 0; - result.push(original.getText(new Range(currentLine, 0, isInsertion ? diff.originalStartLineNumber : diff.originalStartLineNumber - 1, 0))); + let endLine = isInsertion ? diff.originalStartLineNumber : diff.originalStartLineNumber - 1; + let endCharacter = 0; + + // if this is a deletion at the very end of the document,then we need to account + // for a newline at the end of the last line which may have been deleted + // https://github.com/Microsoft/vscode/issues/59670 + if (isDeletion && diff.originalStartLineNumber === original.lineCount) { + endLine -= 1; + endCharacter = original.lineAt(endLine).range.end.character; + } + + result.push(original.getText(new Range(currentLine, 0, endLine, endCharacter))); if (!isDeletion) { let fromLine = diff.modifiedStartLineNumber - 1; @@ -114,4 +125,4 @@ export function invertLineChange(diff: LineChange): LineChange { originalStartLineNumber: diff.modifiedStartLineNumber, originalEndLineNumber: diff.modifiedEndLineNumber }; -} \ No newline at end of file +} diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index 6b7af8c8684..e28cf10d192 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -177,37 +177,43 @@ suite('git', () => { suite('parseGitCommit', () => { test('single parent commit', function () { const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com 8e5a374372b8393906c7e380dbb09349c5385554 This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_SINGLE_PARENT), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: ['8e5a374372b8393906c7e380dbb09349c5385554'] + parents: ['8e5a374372b8393906c7e380dbb09349c5385554'], + authorEmail: 'john.doe@mail.com', }); }); test('multiple parent commits', function () { const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_MULTIPLE_PARENTS), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'] + parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'], + authorEmail: 'john.doe@mail.com', }); }); test('no parent commits', function () { const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 +john.doe@mail.com This is a commit message.`; assert.deepEqual(parseGitCommit(GIT_OUTPUT_NO_PARENTS), { hash: '52c293a05038d865604c2284aa8698bd087915a1', message: 'This is a commit message.', - parents: [] + parents: [], + authorEmail: 'john.doe@mail.com', }); }); }); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 14cbb0a65d3..7bf81adccd9 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -69,7 +69,7 @@ export function anyEvent(...events: Event[]): Event { } export function done(promise: Promise): Promise { - return promise.then(() => void 0); + return promise.then(() => undefined); } export function onceEvent(event: Event): Event { diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 616d42caddf..f13ef93c34f 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -26,20 +26,20 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== "@types/which@^1.0.28": version "1.0.28" resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY= -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -313,12 +313,12 @@ supports-color@3.1.2: dependencies: has-flag "^1.0.0" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/go/cgmanifest.json b/extensions/go/cgmanifest.json index 46b1c1c4319..a26813ed47e 100644 --- a/extensions/go/cgmanifest.json +++ b/extensions/go/cgmanifest.json @@ -11,8 +11,8 @@ }, "license": "MIT", "description": "The file syntaxes/go.json was derived from the Atom package https://atom.io/packages/language-go.", - "version": "0.39.0" + "version": "0.44.3" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/groovy/cgmanifest.json b/extensions/groovy/cgmanifest.json index 07dec53a640..fc5a3ca9c12 100644 --- a/extensions/groovy/cgmanifest.json +++ b/extensions/groovy/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index dc2520b4982..74fa452028a 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -19,7 +19,7 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts index 38589e8a596..09a812ac06f 100644 --- a/extensions/grunt/src/main.ts +++ b/extensions/grunt/src/main.ts @@ -59,6 +59,12 @@ function getOutputChannel(): vscode.OutputChannel { return _channel; } +function showError() { + vscode.window.showWarningMessage(localize('gulpTaskDetectError', 'Problem finding jake tasks. See the output for more information.'), + localize('jakeShowOutput', 'Go to output')).then(() => { + getOutputChannel().show(true); + }); +} interface GruntTaskDefinition extends vscode.TaskDefinition { task: string; file?: string; @@ -120,7 +126,7 @@ class FolderDetector { let { stdout, stderr } = await exec(commandLine, { cwd: rootPath }); if (stderr) { getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); + showError(); } let result: vscode.Task[] = []; if (stdout) { @@ -186,7 +192,7 @@ class FolderDetector { channel.appendLine(err.stdout); } channel.appendLine(localize('execFailed', 'Auto detecting Grunt for folder {0} failed with error: {1}', this.workspaceFolder.name, err.error ? err.error.toString() : 'unknown')); - channel.show(true); + showError(); return emptyTasks; } } diff --git a/extensions/grunt/yarn.lock b/extensions/grunt/yarn.lock index 5e39a356be6..1bcd757b8a1 100644 --- a/extensions/grunt/yarn.lock +++ b/extensions/grunt/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 0c78e7fada0..a9139780f7a 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -19,7 +19,7 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/gulp/src/main.ts b/extensions/gulp/src/main.ts index b8612d511c2..653e34bd27f 100644 --- a/extensions/gulp/src/main.ts +++ b/extensions/gulp/src/main.ts @@ -7,8 +7,8 @@ import * as path from 'path'; import * as fs from 'fs'; import * as cp from 'child_process'; import * as vscode from 'vscode'; - import * as nls from 'vscode-nls'; + const localize = nls.loadMessageBundle(); type AutoDetect = 'on' | 'off'; @@ -60,6 +60,13 @@ function getOutputChannel(): vscode.OutputChannel { return _channel; } +function showError() { + vscode.window.showWarningMessage(localize('gulpTaskDetectError', 'Problem finding gulp tasks. See the output for more information.'), + localize('gulpShowOutput', 'Go to output')).then(() => { + _channel.show(true); + }); +} + interface GulpTaskDefinition extends vscode.TaskDefinition { task: string; file?: string; @@ -113,7 +120,12 @@ class FolderDetector { let gulpCommand: string; let platform = process.platform; if (platform === 'win32' && await exists(path.join(rootPath!, 'node_modules', '.bin', 'gulp.cmd'))) { - gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp.cmd'); + const globalGulp = path.join(process.env.APPDATA ? process.env.APPDATA : '', 'npm', 'gulp.cmd'); + if (await exists(globalGulp)) { + gulpCommand = '"' + globalGulp + '"'; + } else { + gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp.cmd'); + } } else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(rootPath!, 'node_modules', '.bin', 'gulp'))) { gulpCommand = path.join('.', 'node_modules', '.bin', 'gulp'); } else { @@ -125,7 +137,7 @@ class FolderDetector { let { stdout, stderr } = await exec(commandLine, { cwd: rootPath }); if (stderr && stderr.length > 0) { getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); + showError(); } let result: vscode.Task[] = []; if (stdout) { @@ -159,7 +171,7 @@ class FolderDetector { channel.appendLine(err.stdout); } channel.appendLine(localize('execFailed', 'Auto detecting gulp for folder {0} failed with error: {1}', this.workspaceFolder.name, err.error ? err.error.toString() : 'unknown')); - channel.show(true); + showError(); return emptyTasks; } } diff --git a/extensions/gulp/yarn.lock b/extensions/gulp/yarn.lock index 5e39a356be6..1bcd757b8a1 100644 --- a/extensions/gulp/yarn.lock +++ b/extensions/gulp/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/handlebars/cgmanifest.json b/extensions/handlebars/cgmanifest.json index 5befa41e295..39f8efc676d 100644 --- a/extensions/handlebars/cgmanifest.json +++ b/extensions/handlebars/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "daaain/Handlebars", "repositoryUrl": "https://github.com/daaain/Handlebars", - "commitHash": "790f2b0222098a3a236bd9e91bb9a039eeca4d8e" + "commitHash": "85a153a6f759df4e8da7533e1b3651f007867c51" } }, "licenseDetail": [ @@ -29,8 +29,8 @@ "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ], "license": "MIT", - "version": "0.0.0" + "version": "1.8.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/handlebars/language-configuration.json b/extensions/handlebars/language-configuration.json index ab66bc5961a..cb2de742fb0 100644 --- a/extensions/handlebars/language-configuration.json +++ b/extensions/handlebars/language-configuration.json @@ -6,6 +6,7 @@ [""], ["<", ">"], ["{{", "}}"], + ["{{{", "}}}"], ["{", "}"], ["(", ")"] ], @@ -19,6 +20,7 @@ "surroundingPairs": [ { "open": "'", "close": "'" }, { "open": "\"", "close": "\"" }, - { "open": "<", "close": ">" } + { "open": "<", "close": ">" }, + { "open": "{", "close": "}" } ] } diff --git a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json b/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json index 957f16ae035..be8b06fa085 100644 --- a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json +++ b/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/daaain/Handlebars/commit/790f2b0222098a3a236bd9e91bb9a039eeca4d8e", + "version": "https://github.com/daaain/Handlebars/commit/85a153a6f759df4e8da7533e1b3651f007867c51", "name": "Handlebars", "scopeName": "text.html.handlebars", "patterns": [ @@ -653,7 +653,7 @@ ] }, "else_token": { - "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\./]+))?", + "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\.\\(\\s\\)/]+))?", "end": "(~?\\}\\}\\}*)", "name": "meta.function.inline.else.handlebars", "beginCaptures": { diff --git a/extensions/hlsl/cgmanifest.json b/extensions/hlsl/cgmanifest.json index 92137372cc0..44b3a016ee0 100644 --- a/extensions/hlsl/cgmanifest.json +++ b/extensions/hlsl/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/html-language-features/.vscode/launch.json b/extensions/html-language-features/.vscode/launch.json index 28ec3d3dc89..032dc46bb43 100644 --- a/extensions/html-language-features/.vscode/launch.json +++ b/extensions/html-language-features/.vscode/launch.json @@ -17,8 +17,7 @@ ], "stopOnEntry": false, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/client/out/**/*.js"], - "preLaunchTask": "npm" + "outFiles": ["${workspaceFolder}/client/out/**/*.js"] }, { "name": "Launch Tests", @@ -28,8 +27,7 @@ "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/client/out/test" ], "stopOnEntry": false, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/client/out/test/**/*.js"], - "preLaunchTask": "npm" + "outFiles": ["${workspaceFolder}/client/out/test/**/*.js"] }, { "name": "Attach Language Server", diff --git a/extensions/html-language-features/.vscode/settings.json b/extensions/html-language-features/.vscode/settings.json new file mode 100644 index 00000000000..569ac10cf8f --- /dev/null +++ b/extensions/html-language-features/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.insertSpaces": false, + "prettier.semi": true, + "prettier.singleQuote": true, + "prettier.printWidth": 120, +} \ No newline at end of file diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts new file mode 100644 index 00000000000..d716c0ae347 --- /dev/null +++ b/extensions/html-language-features/client/src/customData.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import { workspace, WorkspaceFolder, extensions } from 'vscode'; + +interface ExperimentalConfig { + experimental?: { + customData?: string[]; + }; +} + +export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined): string[] { + const dataPaths: string[] = []; + + if (!workspaceFolders) { + return dataPaths; + } + + workspaceFolders.forEach(wf => { + const allHtmlConfig = workspace.getConfiguration(undefined, wf.uri); + const wfHtmlConfig = allHtmlConfig.inspect('html'); + + if ( + wfHtmlConfig && + wfHtmlConfig.workspaceFolderValue && + wfHtmlConfig.workspaceFolderValue.experimental && + wfHtmlConfig.workspaceFolderValue.experimental.customData + ) { + const customData = wfHtmlConfig.workspaceFolderValue.experimental.customData; + if (Array.isArray(customData)) { + customData.forEach(t => { + if (typeof t === 'string') { + dataPaths.push(path.resolve(wf.uri.fsPath, t)); + } + }); + } + } + }); + + return dataPaths; +} + +export function getCustomDataPathsFromAllExtensions(): string[] { + const dataPaths: string[] = []; + + for (const extension of extensions.all) { + const contributes = extension.packageJSON && extension.packageJSON.contributes; + + if ( + contributes && + contributes.html && + contributes.html.experimental.customData && + Array.isArray(contributes.html.experimental.customData) + ) { + const relativePaths: string[] = contributes.html.experimental.customData; + relativePaths.forEach(rp => { + dataPaths.push(path.resolve(extension.extensionPath, rp)); + }); + } + } + + return dataPaths; +} diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 7a9aa6fc6fa..6fe90190729 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -8,11 +8,12 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString } from 'vscode'; +import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, SelectionRange } 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 { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); @@ -46,9 +47,14 @@ export function activate(context: ExtensionContext) { debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } }; - let documentSelector = ['html', 'handlebars', 'razor']; + let documentSelector = ['html', 'handlebars']; let embeddedLanguages = { css: true, javascript: true }; + let dataPaths = [ + ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), + ...getCustomDataPathsFromAllExtensions() + ]; + // Options to control the language client let clientOptions: LanguageClientOptions = { documentSelector, @@ -56,7 +62,8 @@ export function activate(context: ExtensionContext) { configurationSection: ['html', 'css', 'javascript'], // the settings to synchronize }, initializationOptions: { - embeddedLanguages + embeddedLanguages, + dataPaths } }; @@ -71,7 +78,7 @@ export function activate(context: ExtensionContext) { let param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); return client.sendRequest(TagCloseRequest.type, param); }; - disposable = activateTagClosing(tagRequestor, { html: true, handlebars: true, razor: true }, 'html.autoClosingTags'); + disposable = activateTagClosing(tagRequestor, { html: true, handlebars: true }, 'html.autoClosingTags'); toDispose.push(disposable); disposable = client.onTelemetry(e => { @@ -80,6 +87,26 @@ export function activate(context: ExtensionContext) { } }); toDispose.push(disposable); + + documentSelector.forEach(selector => { + context.subscriptions.push(languages.registerSelectionRangeProvider(selector, { + async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); + if (Array.isArray(rawResult)) { + return rawResult.map(rawSelectionRanges => { + return rawSelectionRanges.reduceRight((parent: SelectionRange | undefined, selectionRange: SelectionRange) => { + return { + range: client.protocol2CodeConverter.asRange(selectionRange.range), + parent + }; + }, undefined)!; + }); + } + return []; + } + })); + }); }); languages.setLanguageConfiguration('html', { @@ -116,24 +143,11 @@ export function activate(context: ExtensionContext) { ], }); - languages.setLanguageConfiguration('razor', { - wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, - onEnterRules: [ - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i, - action: { indentAction: IndentAction.IndentOutdent } - }, - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - action: { indentAction: IndentAction.Indent } - } - ], - }); - const regionCompletionRegExpr = /^(\s*)(<(!(-(-\s*(#\w*)?)?)?)?)?$/; + const htmlSnippetCompletionRegExpr = /^(\s*)(<(h(t(m(l)?)?)?)?)?$/; languages.registerCompletionItemProvider(documentSelector, { provideCompletionItems(doc, pos) { + const results: CompletionItem[] = []; let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos)); let match = lineUntilPos.match(regionCompletionRegExpr); if (match) { @@ -144,15 +158,41 @@ export function activate(context: ExtensionContext) { beginProposal.documentation = localize('folding.start', 'Folding Region Start'); beginProposal.filterText = match[2]; beginProposal.sortText = 'za'; + results.push(beginProposal); let endProposal = new CompletionItem('#endregion', CompletionItemKind.Snippet); endProposal.range = range; endProposal.insertText = new SnippetString(''); endProposal.documentation = localize('folding.end', 'Folding Region End'); endProposal.filterText = match[2]; endProposal.sortText = 'zb'; - return [beginProposal, endProposal]; + results.push(endProposal); } - return null; + let match2 = lineUntilPos.match(htmlSnippetCompletionRegExpr); + if (match2 && doc.getText(new Range(new Position(0, 0), pos)).match(htmlSnippetCompletionRegExpr)) { + let range = new Range(new Position(pos.line, match2[1].length), pos); + let snippetProposal = new CompletionItem('HTML sample', CompletionItemKind.Snippet); + snippetProposal.range = range; + const content = ['', + '', + '', + '\t', + '\t', + '\t${1:Page Title}', + '\t', + '\t', + '\t', + '', + '', + '\t$0', + '', + ''].join('\n'); + snippetProposal.insertText = new SnippetString(content); + snippetProposal.documentation = localize('folding.html', 'Simple HTML5 starting point'); + snippetProposal.filterText = match2[2]; + snippetProposal.sortText = 'za'; + results.push(snippetProposal); + } + return results; } }); } @@ -181,4 +221,4 @@ function readJSONFile(location: string) { export function deactivate(): Promise { return telemetryReporter ? telemetryReporter.dispose() : Promise.resolve(null); -} \ No newline at end of file +} diff --git a/extensions/html-language-features/client/src/tagClosing.ts b/extensions/html-language-features/client/src/tagClosing.ts index a74362a0d3d..35511e63f43 100644 --- a/extensions/html-language-features/client/src/tagClosing.ts +++ b/extensions/html-language-features/client/src/tagClosing.ts @@ -14,7 +14,7 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio updateEnabledState(); window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables); - let timeout: NodeJS.Timer | undefined = void 0; + let timeout: NodeJS.Timer | undefined = undefined; function updateEnabledState() { isEnabled = false; @@ -26,7 +26,7 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio if (!supportedLanguages[document.languageId]) { return; } - if (!workspace.getConfiguration(void 0, document.uri).get(configName)) { + if (!workspace.getConfiguration(undefined, document.uri).get(configName)) { return; } isEnabled = true; @@ -68,7 +68,7 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio } } }); - timeout = void 0; + timeout = undefined; }, 100); } return Disposable.from(...disposables); diff --git a/extensions/html-language-features/client/src/typings/ref.d.ts b/extensions/html-language-features/client/src/typings/ref.d.ts index 9c1a5df18ed..be1d1b0b776 100644 --- a/extensions/html-language-features/client/src/typings/ref.d.ts +++ b/extensions/html-language-features/client/src/typings/ref.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// +/// diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 19ba1cb08dd..e44dc9f7102 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -1,4 +1,5 @@ { + "enableProposedApi": true, "name": "html-language-features", "displayName": "%displayName%", "description": "%description%", @@ -11,8 +12,7 @@ "icon": "icons/html.png", "activationEvents": [ "onLanguage:html", - "onLanguage:handlebars", - "onLanguage:razor" + "onLanguage:handlebars" ], "main": "./client/out/htmlMain", "scripts": { @@ -31,6 +31,15 @@ "type": "object", "title": "HTML", "properties": { + "html.experimental.customData": { + "type": "array", + "description": "A list of JSON file paths that define custom tags, properties and other HTML syntax constructs. Only workspace folder setting will be read.", + "default": [], + "items": { + "type": "string" + }, + "scope": "resource" + }, "html.format.enable": { "type": "boolean", "scope": "window", @@ -112,29 +121,21 @@ "force", "force-aligned", "force-expand-multiline", - "aligned-multiple" + "aligned-multiple", + "preserve", + "preserve-aligned" ], "enumDescriptions": [ "%html.format.wrapAttributes.auto%", "%html.format.wrapAttributes.force%", "%html.format.wrapAttributes.forcealign%", "%html.format.wrapAttributes.forcemultiline%", - "%html.format.wrapAttributes.alignedmultiple%" + "%html.format.wrapAttributes.alignedmultiple%", + "%html.format.wrapAttributes.preserve%", + "%html.format.wrapAttributes.preservealigned%" ], "description": "%html.format.wrapAttributes.desc%" }, - "html.suggest.angular1": { - "type": "boolean", - "scope": "resource", - "default": false, - "description": "%html.suggest.angular1.desc%" - }, - "html.suggest.ionic": { - "type": "boolean", - "scope": "resource", - "default": false, - "description": "%html.suggest.ionic.desc%" - }, "html.suggest.html5": { "type": "boolean", "scope": "resource", @@ -174,11 +175,11 @@ } }, "dependencies": { - "vscode-extension-telemetry": "0.1.0", - "vscode-languageclient": "^5.1.0", + "vscode-extension-telemetry": "0.1.1", + "vscode-languageclient": "^5.2.1", "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" } } diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index c6923ce881e..6a436fa5cb3 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -1,6 +1,6 @@ { "displayName": "HTML Language Features", - "description": "Provides rich language support for HTML, Razor, and Handlebar files", + "description": "Provides rich language support for HTML and Handlebar files", "html.format.enable.desc": "Enable/disable default HTML formatter.", "html.format.wrapLineLength.desc": "Maximum amount of characters per line (0 = disable).", "html.format.unformatted.desc": "List of tags, comma separated, that shouldn't be reformatted. `null` defaults to all tags listed at https://www.w3.org/TR/html5/dom.html#phrasing-content.", @@ -17,8 +17,8 @@ "html.format.wrapAttributes.forcealign": "Wrap each attribute except first and keep aligned.", "html.format.wrapAttributes.forcemultiline": "Wrap each attribute.", "html.format.wrapAttributes.alignedmultiple": "Wrap when line length is exceeded, align attributes vertically.", - "html.suggest.angular1.desc": "Controls whether the built-in HTML language support suggests Angular V1 tags and properties.", - "html.suggest.ionic.desc": "Controls whether the built-in HTML language support suggests Ionic tags, properties and values.", + "html.format.wrapAttributes.preserve": "Preserve wrapping of attributes", + "html.format.wrapAttributes.preservealigned": "Preserve wrapping of attributes but align.", "html.suggest.html5.desc": "Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.", "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", diff --git a/extensions/html-language-features/server/extension.webpack.config.js b/extensions/html-language-features/server/extension.webpack.config.js index a535ddeae9a..77b86e718b1 100644 --- a/extensions/html-language-features/server/extension.webpack.config.js +++ b/extensions/html-language-features/server/extension.webpack.config.js @@ -9,7 +9,6 @@ const withDefaults = require('../../shared.webpack.config'); const path = require('path'); -var webpack = require('webpack'); module.exports = withDefaults({ context: path.join(__dirname), @@ -22,12 +21,5 @@ module.exports = withDefaults({ }, externals: { 'typescript': 'commonjs typescript' - }, - plugins: [ - new webpack.NormalModuleReplacementPlugin( - /[/\\]vscode-languageserver[/\\]lib[/\\]files\.js/, - require.resolve('./build/filesFillIn') - ), - new webpack.IgnorePlugin(/vertx/) - ], + } }); diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 93882a7ec94..7c90bfb52c9 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,16 +9,16 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.12", - "vscode-html-languageservice": "^2.1.10", - "vscode-languageserver": "^5.1.0", - "vscode-languageserver-types": "^3.13.0", + "vscode-css-languageservice": "^4.0.2-next.3", + "vscode-html-languageservice": "^3.0.0-next.7", + "vscode-languageserver": "^5.3.0-next.2", + "vscode-languageserver-types": "^3.14.0", "vscode-nls": "^4.0.0", "vscode-uri": "^1.0.6" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "^8.10.25", + "@types/node": "^10.12.21", "glob": "^7.1.2", "mocha": "^5.2.0", "mocha-junit-reporter": "^1.17.0", diff --git a/extensions/html-language-features/server/src/customData.ts b/extensions/html-language-features/server/src/customData.ts new file mode 100644 index 00000000000..1d550eddf9f --- /dev/null +++ b/extensions/html-language-features/server/src/customData.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IHTMLDataProvider, newHTMLDataProvider } from 'vscode-html-languageservice'; +import * as fs from 'fs'; + +export function getDataProviders(dataPaths?: string[]): IHTMLDataProvider[] { + if (!dataPaths) { + return []; + } + + const providers: IHTMLDataProvider[] = []; + + dataPaths.forEach((path, i) => { + try { + if (fs.existsSync(path)) { + const htmlData = JSON.parse(fs.readFileSync(path, 'utf-8')); + + providers.push(newHTMLDataProvider(`customProvider${i}`, htmlData)); + } + } catch (err) { + console.log(`Failed to load tag from ${path}`); + } + }); + + return providers; +} \ No newline at end of file diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index b836c2628fa..7974284bd77 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -19,6 +19,7 @@ import uri from 'vscode-uri'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { getFoldingRanges } from './modes/htmlFolding'; +import { getDataProviders } from './customData'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); @@ -72,7 +73,7 @@ function getDocumentSettings(textDocument: TextDocument, needsDocumentSettings: } return promise; } - return Promise.resolve(void 0); + return Promise.resolve(undefined); } // After the server has started the client sends an initialize request. The server receives @@ -88,11 +89,16 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } } + const dataPaths: string[] = params.initializationOptions.dataPaths; + const providers = getDataProviders(dataPaths); + const workspace = { get settings() { return globalSettings; }, get folders() { return workspaceFolders; } }; - languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace); + + languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, providers); + documents.onDidClose(e => { languageModes.onDocumentRemoved(e.document); }); @@ -169,7 +175,7 @@ connection.onDidChangeConfiguration((change) => { const enableFormatter = globalSettings && globalSettings.html && globalSettings.html.format && globalSettings.html.format.enable; if (enableFormatter) { if (!formatterRegistration) { - const documentSelector: DocumentSelector = [{ language: 'html' }, { language: 'handlebars' }]; // don't register razor, the formatter does more harm than good + const documentSelector: DocumentSelector = [{ language: 'html' }, { language: 'handlebars' }]; formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector }); } } else if (formatterRegistration) { @@ -449,6 +455,21 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding regions for ${params.textDocument.uri}`, token); }); +connection.onRequest('$/textDocument/selectionRanges', async (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + const positions: Position[] = params.positions; + + if (document) { + const htmlMode = languageModes.getMode('html'); + if (htmlMode && htmlMode.getSelectionRanges) { + return htmlMode.getSelectionRanges(document, positions); + } + } + return Promise.resolve(null); + }, null, `Error while computing selection ranges for ${params.textDocument.uri}`, token); +}); + // Listen on the connection connection.listen(); \ No newline at end of file diff --git a/extensions/html-language-features/server/src/languageModelCache.ts b/extensions/html-language-features/server/src/languageModelCache.ts index 7ce4adca9d4..561de4a9a7a 100644 --- a/extensions/html-language-features/server/src/languageModelCache.ts +++ b/extensions/html-language-features/server/src/languageModelCache.ts @@ -15,7 +15,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime let languageModels: { [uri: string]: { version: number, languageId: string, cTime: number, languageModel: T } } = {}; let nModels = 0; - let cleanupInterval: NodeJS.Timer | undefined = void 0; + let cleanupInterval: NodeJS.Timer | undefined = undefined; if (cleanupIntervalTimeInSec > 0) { cleanupInterval = setInterval(() => { let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; @@ -73,7 +73,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime dispose() { if (typeof cleanupInterval !== 'undefined') { clearInterval(cleanupInterval); - cleanupInterval = void 0; + cleanupInterval = undefined; languageModels = {}; nModels = 0; } diff --git a/extensions/html-language-features/server/src/modes/embeddedSupport.ts b/extensions/html-language-features/server/src/modes/embeddedSupport.ts index 68fc91625d6..de80fa5c8e3 100644 --- a/extensions/html-language-features/server/src/modes/embeddedSupport.ts +++ b/extensions/html-language-features/server/src/modes/embeddedSupport.ts @@ -18,7 +18,7 @@ export interface HTMLDocumentRegions { getImportedScripts(): string[]; } -export var CSS_STYLE_RULE = '__'; +export const CSS_STYLE_RULE = '__'; interface EmbeddedRegion { languageId: string | undefined; start: number; end: number; attributeValue?: boolean; } @@ -59,7 +59,7 @@ export function getDocumentRegions(languageService: LanguageService, document: T if (/["'](module|(text|application)\/(java|ecma)script)["']/.test(scanner.getTokenText())) { languageIdFromType = 'javascript'; } else { - languageIdFromType = void 0; + languageIdFromType = undefined; } } else { let attributeLanguageId = getAttributeLanguage(lastAttributeName!); diff --git a/extensions/html-language-features/server/src/modes/htmlFolding.ts b/extensions/html-language-features/server/src/modes/htmlFolding.ts index c228c14bdcc..cd33bcadf35 100644 --- a/extensions/html-language-features/server/src/modes/htmlFolding.ts +++ b/extensions/html-language-features/server/src/modes/htmlFolding.ts @@ -54,7 +54,7 @@ function limitRanges(ranges: FoldingRange[], maxRanges: number) { // compute each range's nesting level in 'nestingLevels'. // count the number of ranges for each level in 'nestingLevelCounts' - let top: FoldingRange | undefined = void 0; + let top: FoldingRange | undefined = undefined; let previous: FoldingRange[] = []; let nestingLevels: number[] = []; let nestingLevelCounts: number[] = []; diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 3623124e47f..09efb996f6e 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getLanguageModelCache } from '../languageModelCache'; -import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration } from 'vscode-html-languageservice'; +import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration, SelectionRange } from 'vscode-html-languageservice'; import { TextDocument, Position, Range, CompletionItem, FoldingRange } from 'vscode-languageserver-types'; import { LanguageMode, Workspace } from './languageModes'; import { getPathCompletionParticipant } from './pathCompletion'; @@ -15,6 +15,9 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: getId() { return 'html'; }, + getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[][] { + return htmlLanguageService.getSelectionRanges(document, positions); + }, doComplete(document: TextDocument, position: Position, settings = workspace.settings) { let options = settings && settings.html && settings.html.suggest; let doAutoComplete = settings && settings.html && settings.html.autoClosingTags; @@ -78,7 +81,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: } function merge(src: any, dst: any): any { - for (var key in src) { + for (const key in src) { if (src.hasOwnProperty(key)) { dst[key] = src[key]; } diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index fd85f8d8a75..fb1f1a9d758 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -58,7 +58,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache text.substring(start, end), getLength: () => text.length, - getChangeRange: () => void 0 + getChangeRange: () => undefined }; }, getCurrentDirectory: () => '', @@ -173,16 +173,17 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { - return { - range: convertRange(currentTextDocument, entry.textSpan), - kind: (entry.isWriteAccess ? DocumentHighlightKind.Write : DocumentHighlightKind.Text) - }; - }); + const highlights = jsLanguageService.getDocumentHighlights(FILE_NAME, currentTextDocument.offsetAt(position), [FILE_NAME]); + const out: DocumentHighlight[] = []; + for (const entry of highlights || []) { + for (const highlight of entry.highlightSpans) { + out.push({ + range: convertRange(currentTextDocument, highlight.textSpan), + kind: highlight.kind === 'writtenReference' ? DocumentHighlightKind.Write : DocumentHighlightKind.Text + }); + } } - return []; + return out; }, findDocumentSymbols(document: TextDocument): SymbolInformation[] { updateCurrentTextDocument(document); diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index b347c6b6799..94c0b04a293 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getLanguageService as getHTMLLanguageService, DocumentContext } from 'vscode-html-languageservice'; +import { getLanguageService as getHTMLLanguageService, DocumentContext, IHTMLDataProvider, SelectionRange } from 'vscode-html-languageservice'; import { CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range, Hover, DocumentHighlight, CompletionList, Position, FormattingOptions, SymbolInformation, FoldingRange @@ -31,6 +31,7 @@ export interface Workspace { export interface LanguageMode { getId(): string; + getSelectionRanges?: (document: TextDocument, positions: Position[]) => SelectionRange[][]; doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[]; doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList; doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; @@ -65,9 +66,9 @@ export interface LanguageModeRange extends Range { attributeValue?: boolean; } -export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace): LanguageModes { +export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, customDataProviders?: IHTMLDataProvider[]): LanguageModes { + const htmlLanguageService = getHTMLLanguageService({ customDataProviders }); - var htmlLanguageService = getHTMLLanguageService(); let documentRegions = getLanguageModelCache(10, 60, document => getDocumentRegions(htmlLanguageService, document)); let modelCaches: LanguageModelCache[] = []; @@ -87,7 +88,7 @@ export function getLanguageModes(supportedLanguages: { [languageId: string]: boo if (languageId) { return modes[languageId]; } - return void 0; + return undefined; }, getModesInRange(document: TextDocument, range: Range): LanguageModeRange[] { return documentRegions.get(document).getLanguageRanges(range).map(r => { diff --git a/extensions/html-language-features/server/src/modes/pathCompletion.ts b/extensions/html-language-features/server/src/modes/pathCompletion.ts index b0d36d514ef..cb3a20f8e4c 100644 --- a/extensions/html-language-features/server/src/modes/pathCompletion.ts +++ b/extensions/html-language-features/server/src/modes/pathCompletion.ts @@ -139,9 +139,9 @@ function pathToSuggestion(p: string, valueBeforeCursor: string, fullValue: strin } function resolveWorkspaceRoot(activeDoc: TextDocument, workspaceFolders: WorkspaceFolder[]): string | undefined { - for (let i = 0; i < workspaceFolders.length; i++) { - if (startsWith(activeDoc.uri, workspaceFolders[i].uri)) { - return path.resolve(URI.parse(workspaceFolders[i].uri).fsPath); + for (const folder of workspaceFolders) { + if (startsWith(activeDoc.uri, folder.uri)) { + return path.resolve(URI.parse(folder.uri).fsPath); } } return undefined; diff --git a/extensions/html-language-features/server/src/test/embedded.test.ts b/extensions/html-language-features/server/src/test/embedded.test.ts index 961dc96db23..de44dd3d80e 100644 --- a/extensions/html-language-features/server/src/test/embedded.test.ts +++ b/extensions/html-language-features/server/src/test/embedded.test.ts @@ -89,7 +89,7 @@ suite('HTML Embedded Support', () => { assertLanguageId('', 'javascript'); assertLanguageId('', 'javascript'); assertLanguageId('', 'javascript'); - assertLanguageId('', void 0); + assertLanguageId('', undefined); assertLanguageId('', 'javascript'); }); diff --git a/extensions/html-language-features/server/src/test/folding.test.ts b/extensions/html-language-features/server/src/test/folding.test.ts index 1ca958dcdf3..3ce2816185b 100644 --- a/extensions/html-language-features/server/src/test/folding.test.ts +++ b/extensions/html-language-features/server/src/test/folding.test.ts @@ -200,7 +200,7 @@ suite('HTML Folding', () => { /*19*/' ', /*20*/'', ]; - assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'no limit', void 0); + assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'no limit', undefined); assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(9, 10), r(13, 14), r(16, 17)], 'limit 8', 8); assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(6, 7), r(13, 14), r(16, 17)], 'limit 7', 7); assertRanges(input, [r(0, 19), r(1, 18), r(2, 3), r(5, 11), r(13, 14), r(16, 17)], 'limit 6', 6); diff --git a/extensions/html-language-features/server/src/test/formatting.test.ts b/extensions/html-language-features/server/src/test/formatting.test.ts index 46de4b56d7c..702aa76da6c 100644 --- a/extensions/html-language-features/server/src/test/formatting.test.ts +++ b/extensions/html-language-features/server/src/test/formatting.test.ts @@ -38,7 +38,7 @@ suite('HTML Embedded Formatting', () => { formatOptions = FormattingOptions.create(2, true); } - let result = format(languageModes, document, range, formatOptions, void 0, { css: true, javascript: true }); + let result = format(languageModes, document, range, formatOptions, undefined, { css: true, javascript: true }); let actual = TextDocument.applyEdits(document, result); assert.equal(actual, expected, message); @@ -67,8 +67,8 @@ suite('HTML Embedded Formatting', () => { test('HTLM & Scripts - Fixtures', function () { assertFormatWithFixture('19813.html', '19813.html'); - assertFormatWithFixture('19813.html', '19813-4spaces.html', void 0, FormattingOptions.create(4, true)); - assertFormatWithFixture('19813.html', '19813-tab.html', void 0, FormattingOptions.create(1, false)); + assertFormatWithFixture('19813.html', '19813-4spaces.html', undefined, FormattingOptions.create(4, true)); + assertFormatWithFixture('19813.html', '19813-tab.html', undefined, FormattingOptions.create(1, false)); assertFormatWithFixture('21634.html', '21634.html'); }); diff --git a/extensions/html-language-features/server/src/utils/arrays.ts b/extensions/html-language-features/server/src/utils/arrays.ts index bfda675bbb0..16b5b46475d 100644 --- a/extensions/html-language-features/server/src/utils/arrays.ts +++ b/extensions/html-language-features/server/src/utils/arrays.ts @@ -5,8 +5,8 @@ export function pushAll(to: T[], from: T[]) { if (from) { - for (var i = 0; i < from.length; i++) { - to.push(from[i]); + for (const e of from) { + to.push(e); } } } diff --git a/extensions/html-language-features/server/src/utils/documentContext.ts b/extensions/html-language-features/server/src/utils/documentContext.ts index cd00f40358d..bfd26d1aa8d 100644 --- a/extensions/html-language-features/server/src/utils/documentContext.ts +++ b/extensions/html-language-features/server/src/utils/documentContext.ts @@ -19,7 +19,7 @@ export function getDocumentContext(documentUri: string, workspaceFolders: Worksp return folderURI; } } - return void 0; + return undefined; } return { diff --git a/extensions/html-language-features/server/src/utils/strings.ts b/extensions/html-language-features/server/src/utils/strings.ts index bf5fdc7b112..ff62faeee75 100644 --- a/extensions/html-language-features/server/src/utils/strings.ts +++ b/extensions/html-language-features/server/src/utils/strings.ts @@ -52,7 +52,7 @@ export function endsWith(haystack: string, needle: string): boolean { } export function repeat(value: string, count: number) { - var s = ''; + let s = ''; while (count > 0) { if ((count & 1) === 1) { s += value; diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 6530713c465..6bdbac4e1a2 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== ansi-regex@^3.0.0: version "3.0.0" @@ -229,20 +229,20 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.12: - version "3.0.12" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.12.tgz#fb4aac5ae3c5b761b1db1d7224b78ff824284dc3" - integrity sha512-+FLQ9LcukIhnxaGTjDOqb3Nb1hesz9BLXf5yeoZxUsuK7joADPLPdxLwlZugFcMAvgmtnaFIGnzkQhGOVqf5yw== +vscode-css-languageservice@^4.0.2-next.3: + version "4.0.2-next.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.2-next.3.tgz#f81925d6037f05724d1c1fe00e8fb7fcbece72ff" + integrity sha512-Th6ESBGTdNo4CbZEeKNVBKi4DwGjafS1+05kuoH3hO5mFCKr6ttdvu4GxMHca7nGN1efv5tiZ6slO8PCN1bLNA== dependencies: - vscode-languageserver-types "^3.13.0" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.10: - version "2.1.10" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.10.tgz#3433fd53e188cb25d5ea190b61a148fe9965ff91" - integrity sha512-nuzLd7a3J+Ttvk/9Pg2H0vS7rV2oZRfsQYPRheHnUNJNqivkcieSI8ZCGvZjmr3NDBG2QQaRFambnCtceYAj3A== +vscode-html-languageservice@^3.0.0-next.7: + version "3.0.0-next.7" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.0-next.7.tgz#6590cb0a6fa5bb257afca03669bbf6ad8adb58c6" + integrity sha512-s9dVSMVKGlrAymj6WByoEheZMen82yHqiXh7F8cz9KCqDHJ3LRpcJMKrIdOFQJ6qTcFW9I7VqO77VSB9d1RZyQ== dependencies: - vscode-languageserver-types "^3.13.0" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" vscode-uri "^1.0.6" @@ -251,25 +251,25 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.15.0-next.1: + version "3.15.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" + integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0, vscode-languageserver-types@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== -vscode-languageserver@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.1.0.tgz#012a28f154cc7a848c443d217894942e4c3eeb39" - integrity sha512-CIsrgx2Y5VHS317g/HwkSTWYBIQmy0DwEyZPmB2pEpVOhYFwVsYpbiJwHIIyLQsQtmRaO4eA2xM8KPjNSdXpBw== +vscode-languageserver@^5.3.0-next.2: + version "5.3.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" + integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== dependencies: - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.15.0-next.1" vscode-uri "^1.0.6" vscode-nls@^4.0.0: diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 236dc76b3f5..a0667775d59 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -2,15 +2,15 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -33,38 +33,38 @@ semver@^5.3.0, semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-jsonrpc@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageclient@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.1.0.tgz#650ab0dc9fd0daaade058a8471aaff5bc3f9580e" - integrity sha512-Z95Kps8UqD4o17HE3uCkZuvenOsxHVH46dKmaGVpGixEFZigPaVuVxLM/JWeIY9aRenoC0ZD9CK1O7L4jpffKg== +vscode-languageclient@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" + integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.14.1" -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" + integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/html/cgmanifest.json b/extensions/html/cgmanifest.json index 7f3cc9396df..78125476f1e 100644 --- a/extensions/html/cgmanifest.json +++ b/extensions/html/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "textmate/html.tmbundle", "repositoryUrl": "https://github.com/textmate/html.tmbundle", - "commitHash": "390c8870273a2ae80244dae6db6ba064a802f407" + "commitHash": "0c3d5ee54de3a993f747f54186b73a4d2d3c44a2" } }, "licenseDetail": [ @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/html/package.json b/extensions/html/package.json index cd93962a56a..9c63bdcb459 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -74,12 +74,6 @@ "meta.tag string.quoted": "other" } } - ], - "snippets": [ - { - "language": "html", - "path": "./snippets/html.snippets.json" - } ] } } diff --git a/extensions/html/snippets/html.snippets.json b/extensions/html/snippets/html.snippets.json deleted file mode 100644 index 7a858455a69..00000000000 --- a/extensions/html/snippets/html.snippets.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "HTML template": { - "prefix": "html", - "body": [ - "", - "", - "", - "\t", - "\t", - "\t${1:Page Title}", - "\t", - "\t", - "\t", - "", - "", - "\t$0", - "", - "" - ], - "description": "Simple HTML5 starting point" - } -} diff --git a/extensions/html/syntaxes/html.tmLanguage.json b/extensions/html/syntaxes/html.tmLanguage.json index a5ccfed6f36..a071d90a9e3 100644 --- a/extensions/html/syntaxes/html.tmLanguage.json +++ b/extensions/html/syntaxes/html.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/html.tmbundle/commit/390c8870273a2ae80244dae6db6ba064a802f407", + "version": "https://github.com/textmate/html.tmbundle/commit/0c3d5ee54de3a993f747f54186b73a4d2d3c44a2", "name": "HTML", "scopeName": "text.html.basic", "injections": { @@ -222,7 +222,32 @@ ] } }, - "match": "[^\\n\"]+" + "match": "([^\\n\"/]|/(?![/*]))+" + }, + { + "begin": "//", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.js" + } + }, + "end": "(?=\")|\\n", + "name": "comment.line.double-slash.js" + }, + { + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.js" + } + }, + "end": "(?=\")|\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.js" + } + }, + "name": "comment.block.js" } ] }, @@ -255,7 +280,32 @@ ] } }, - "match": "[^\\n']+" + "match": "([^\\n'/]|/(?![/*]))+" + }, + { + "begin": "//", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.js" + } + }, + "end": "(?=')|\\n", + "name": "comment.line.double-slash.js" + }, + { + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.js" + } + }, + "end": "(?=')|\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.js" + } + }, + "name": "comment.block.js" } ] } diff --git a/extensions/html/test/colorize-results/12750_html.json b/extensions/html/test/colorize-results/12750_html.json index a248a54eb8d..c70c7afdb8b 100644 --- a/extensions/html/test/colorize-results/12750_html.json +++ b/extensions/html/test/colorize-results/12750_html.json @@ -111,13 +111,13 @@ }, { "c": "window", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.variable.dom.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js variable.other.object.js", "r": { - "dark_plus": "support.variable: #9CDCFE", - "light_plus": "support.variable: #001080", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.variable: #9CDCFE" + "hc_black": "variable: #9CDCFE" } }, { @@ -133,13 +133,13 @@ }, { "c": "alert", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.function.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js entity.name.function.js", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { @@ -298,13 +298,13 @@ }, { "c": "window", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.variable.dom.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js variable.other.object.js", "r": { - "dark_plus": "support.variable: #9CDCFE", - "light_plus": "support.variable: #001080", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.variable: #9CDCFE" + "hc_black": "variable: #9CDCFE" } }, { @@ -320,13 +320,13 @@ }, { "c": "alert", - "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js support.function.js", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js entity.name.function.js", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { diff --git a/extensions/ini/cgmanifest.json b/extensions/ini/cgmanifest.json index ef9fa28b58a..772dc25a3ac 100644 --- a/extensions/ini/cgmanifest.json +++ b/extensions/ini/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/ini/package.json b/extensions/ini/package.json index ea3df33d8df..abe1dbecc65 100644 --- a/extensions/ini/package.json +++ b/extensions/ini/package.json @@ -18,7 +18,8 @@ { "id": "properties", "extensions": [ ".properties", ".cfg", ".conf", ".desktop", ".directory" ], - "filenames": [ "config", ".gitattributes", ".gitconfig", "gitconfig", ".editorconfig" ], + "filenames": [ ".gitattributes", ".gitconfig", "gitconfig", ".editorconfig" ], + "filenamePatterns": [ "**/.git/config" ], "aliases": [ "Properties", "properties" ], "configuration": "./properties.language-configuration.json" }], diff --git a/extensions/jake/package.json b/extensions/jake/package.json index f35e49fee26..30962bd7616 100644 --- a/extensions/jake/package.json +++ b/extensions/jake/package.json @@ -19,7 +19,7 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/jake/src/main.ts b/extensions/jake/src/main.ts index 938ca51bec9..d778dcedab7 100644 --- a/extensions/jake/src/main.ts +++ b/extensions/jake/src/main.ts @@ -59,6 +59,13 @@ function getOutputChannel(): vscode.OutputChannel { return _channel; } +function showError() { + vscode.window.showWarningMessage(localize('gulpTaskDetectError', 'Problem finding jake tasks. See the output for more information.'), + localize('jakeShowOutput', 'Go to output')).then(() => { + getOutputChannel().show(true); + }); +} + interface JakeTaskDefinition extends vscode.TaskDefinition { task: string; file?: string; @@ -124,7 +131,7 @@ class FolderDetector { let { stdout, stderr } = await exec(commandLine, { cwd: rootPath }); if (stderr) { getOutputChannel().appendLine(stderr); - getOutputChannel().show(true); + showError(); } let result: vscode.Task[] = []; if (stdout) { @@ -163,7 +170,7 @@ class FolderDetector { channel.appendLine(err.stdout); } channel.appendLine(localize('execFailed', 'Auto detecting Jake for folder {0} failed with error: {1}', this.workspaceFolder.name, err.error ? err.error.toString() : 'unknown')); - channel.show(true); + showError(); return emptyTasks; } } diff --git a/extensions/jake/yarn.lock b/extensions/jake/yarn.lock index 5e39a356be6..1bcd757b8a1 100644 --- a/extensions/jake/yarn.lock +++ b/extensions/jake/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/java/cgmanifest.json b/extensions/java/cgmanifest.json index cff02de7cb2..8616c4fcc53 100644 --- a/extensions/java/cgmanifest.json +++ b/extensions/java/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "atom/language-java", "repositoryUrl": "https://github.com/atom/language-java", - "commitHash": "95ebcd0b15c369666ecc4d1593495466132dd5bf" + "commitHash": "9fc8f699e55284c0a8ddf03d929504064eb4f757" } }, "license": "MIT", - "version": "0.0.0" + "version": "0.31.2" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index 2ccdc0733de..736861a6043 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/95ebcd0b15c369666ecc4d1593495466132dd5bf", + "version": "https://github.com/atom/language-java/commit/9fc8f699e55284c0a8ddf03d929504064eb4f757", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -124,15 +124,15 @@ "annotations": { "patterns": [ { - "begin": "((@)[^\\s(]+)(\\()", + "begin": "((@)\\s*([^\\s(]+))(\\()", "beginCaptures": { - "1": { - "name": "storage.type.annotation.java" - }, "2": { "name": "punctuation.definition.annotation.java" }, "3": { + "name": "storage.type.annotation.java" + }, + "4": { "name": "punctuation.definition.annotation-arguments.begin.bracket.round.java" } }, @@ -161,7 +161,7 @@ ] }, { - "match": "(@)(interface)\\s+(\\w*)|((@)\\w*)", + "match": "(@)(interface)\\s+(\\w*)|((@)\\s*(\\w+))", "name": "meta.declaration.annotation.java", "captures": { "1": { @@ -173,11 +173,11 @@ "3": { "name": "storage.type.annotation.java" }, - "4": { - "name": "storage.type.annotation.java" - }, "5": { "name": "punctuation.definition.annotation.java" + }, + "6": { + "name": "storage.type.annotation.java" } } } @@ -269,7 +269,7 @@ ] }, "class": { - "begin": "(?=\\w?[\\w\\s]*(?:class|(?(\\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)", + "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n \\s*\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n \\s*\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", "end": "(?=\\=|:|;)", "name": "meta.definition.variable.java", "patterns": [ diff --git a/extensions/java/test/colorize-results/basic_java.json b/extensions/java/test/colorize-results/basic_java.json index b41a14df569..0032d405471 100644 --- a/extensions/java/test/colorize-results/basic_java.json +++ b/extensions/java/test/colorize-results/basic_java.json @@ -1068,13 +1068,13 @@ }, { "c": "@", - "t": "source.java meta.class.java meta.class.body.java meta.declaration.annotation.java storage.type.annotation.java punctuation.definition.annotation.java", + "t": "source.java meta.class.java meta.class.body.java meta.declaration.annotation.java punctuation.definition.annotation.java", "r": { - "dark_plus": "storage.type.annotation.java: #4EC9B0", - "light_plus": "storage.type.annotation.java: #267F99", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type.annotation.java: #4EC9B0" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { @@ -1871,13 +1871,13 @@ }, { "c": "@", - "t": "source.java meta.class.java meta.class.body.java meta.declaration.annotation.java storage.type.annotation.java punctuation.definition.annotation.java", + "t": "source.java meta.class.java meta.class.body.java meta.declaration.annotation.java punctuation.definition.annotation.java", "r": { - "dark_plus": "storage.type.annotation.java: #4EC9B0", - "light_plus": "storage.type.annotation.java: #267F99", - "dark_vs": "storage.type: #569CD6", - "light_vs": "storage.type: #0000FF", - "hc_black": "storage.type.annotation.java: #4EC9B0" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 50fa8fca327..9ea6c3cec1b 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/3133e3d914db9a2bb8812119f9273727a305f16b", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3508c88a4ac6112934e0c34de7942c67682b2321", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -132,6 +132,9 @@ "name": "keyword.control.switch.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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" + }, + "2": { + "name": "keyword.operator.definiteassignment.js" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -438,10 +444,13 @@ }, { "name": "meta.var-single-variable.expr.js", - "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.js variable.other.constant.js" + }, + "2": { + "name": "keyword.operator.definiteassignment.js" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -453,10 +462,13 @@ }, { "name": "meta.var-single-variable.expr.js", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.js variable.other.readwrite.js" + }, + "2": { + "name": "keyword.operator.definiteassignment.js" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -472,7 +484,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" @@ -593,7 +605,7 @@ "include": "#comment" }, { - "begin": "(?x)(?=((\\b(?)\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)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -944,7 +968,7 @@ "include": "#comment" }, { - "begin": "(?x)(?=((\\b(?)\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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" + }, + "3": { + "name": "keyword.operator.definiteassignment.js" } } }, @@ -1095,19 +1128,23 @@ { "name": "keyword.operator.optional.js", "match": "\\?" + }, + { + "name": "keyword.operator.definiteassignment.js", + "match": "\\!" } ] }, "variable-initializer": { "patterns": [ { - "begin": "(?\\s*$)", "beginCaptures": { "1": { "name": "keyword.operator.assignment.js" } }, - "end": "(?=$|^|[,);}\\]])", + "end": "(?=$|^|[,);}\\]]|(\\s+(of|in)\\s+))", "patterns": [ { "include": "#expression" @@ -1121,7 +1158,7 @@ "name": "keyword.operator.assignment.js" } }, - "end": "(?=[,);}\\]])|(?=^\\s*$)|(?<=\\S)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.js" @@ -1281,7 +1318,7 @@ }, { "name": "meta.method.declaration.js", - "begin": "(?x)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.js" @@ -1313,7 +1350,7 @@ }, "object-literal-method-declaration": { "name": "meta.method.declaration.js", - "begin": "(?x)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -1334,7 +1371,7 @@ "include": "#function-body" }, { - "begin": "(?x)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -1356,7 +1393,7 @@ ] }, "method-declaration-name": { - "begin": "(?x)(?=((\\b(? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -1547,7 +1584,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -1584,9 +1621,12 @@ }, "class-expression": { "name": "meta.class.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)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js" @@ -2524,6 +2647,33 @@ } } }, + { + "name": "meta.object.member.js", + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "include": "#literal" - }, - { - "include": "#support-objects" - }, - { - "include": "#object-identifiers" - }, - { - "include": "#punctuation-accessor" - }, - { - "name": "keyword.operator.expression.import.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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "captures": { + "1": { + "name": "storage.modifier.js" + }, + "2": { + "name": "keyword.operator.rest.js" + }, + "3": { + "name": "entity.name.function.js variable.language.this.js" + }, + "4": { + "name": "entity.name.function.js" + }, + "5": { + "name": "keyword.operator.optional.js" + } + } + }, + { + "match": "(?x)(?:(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\())\n |\n (?:(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\b(?!\\$)))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3211,34 +3485,6 @@ } } }, - { - "match": "(?x) (?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\()", - "captures": { - "1": { - "name": "punctuation.accessor.js" - }, - "2": { - "name": "punctuation.accessor.optional.js" - }, - "3": { - "name": "support.constant.dom.js" - }, - "4": { - "name": "support.variable.property.dom.js" - } - } - }, { "name": "support.class.node.js", "match": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3439,6 +3662,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.js", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.js" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { @@ -3592,6 +3834,13 @@ "name": "punctuation.definition.typeparameters.end.js" } }, + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + "type-arguments-body": { "patterns": [ { "match": "(?)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type-parameters" + } + ] }, { "name": "meta.type.constructor.js", @@ -3994,6 +4259,58 @@ }, "type-name": { "patterns": [ + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(<)", + "captures": { + "1": { + "name": "entity.name.type.module.js" + }, + "2": { + "name": "punctuation.accessor.js" + }, + "3": { + "name": "punctuation.accessor.optional.js" + }, + "4": { + "name": "meta.type.parameters.js punctuation.definition.typeparameters.begin.js" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.js punctuation.definition.typeparameters.end.js" + } + }, + "contentName": "meta.type.parameters.js", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(<)", + "beginCaptures": { + "1": { + "name": "entity.name.type.js" + }, + "2": { + "name": "meta.type.parameters.js punctuation.definition.typeparameters.begin.js" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.js punctuation.definition.typeparameters.end.js" + } + }, + "contentName": "meta.type.parameters.js", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, { "match": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))", "captures": { @@ -4100,7 +4417,30 @@ "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*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "end": "(?=`)", + "patterns": [ + { + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "patterns": [ + { + "include": "#support-function-call-identifiers" + }, + { + "name": "entity.name.function.tagged-template.js", + "match": "([_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + { + "include": "#type-arguments" + } + ] + }, + { + "name": "string.template.js", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js" @@ -4166,7 +4506,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" @@ -4189,7 +4529,7 @@ }, { "name": "string.regexp.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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx entity.name.function.js.jsx" + }, + "2": { + "name": "keyword.operator.definiteassignment.js.jsx" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -438,10 +444,13 @@ }, { "name": "meta.var-single-variable.expr.js.jsx", - "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx variable.other.constant.js.jsx" + }, + "2": { + "name": "keyword.operator.definiteassignment.js.jsx" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -453,10 +462,13 @@ }, { "name": "meta.var-single-variable.expr.js.jsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx variable.other.readwrite.js.jsx" + }, + "2": { + "name": "keyword.operator.definiteassignment.js.jsx" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -472,7 +484,7 @@ "patterns": [ { "name": "meta.var-single-variable.expr.js.jsx", - "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" @@ -593,7 +605,7 @@ "include": "#comment" }, { - "begin": "(?x)(?=((\\b(?)\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)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -944,7 +968,7 @@ "include": "#comment" }, { - "begin": "(?x)(?=((\\b(?)\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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" + }, + "3": { + "name": "keyword.operator.definiteassignment.js.jsx" } } }, @@ -1095,19 +1128,23 @@ { "name": "keyword.operator.optional.js.jsx", "match": "\\?" + }, + { + "name": "keyword.operator.definiteassignment.js.jsx", + "match": "\\!" } ] }, "variable-initializer": { "patterns": [ { - "begin": "(?\\s*$)", "beginCaptures": { "1": { "name": "keyword.operator.assignment.js.jsx" } }, - "end": "(?=$|^|[,);}\\]])", + "end": "(?=$|^|[,);}\\]]|(\\s+(of|in)\\s+))", "patterns": [ { "include": "#expression" @@ -1121,7 +1158,7 @@ "name": "keyword.operator.assignment.js.jsx" } }, - "end": "(?=[,);}\\]])|(?=^\\s*$)|(?<=\\S)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.js.jsx" @@ -1281,7 +1318,7 @@ }, { "name": "meta.method.declaration.js.jsx", - "begin": "(?x)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.js.jsx" @@ -1313,7 +1350,7 @@ }, "object-literal-method-declaration": { "name": "meta.method.declaration.js.jsx", - "begin": "(?x)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -1334,7 +1371,7 @@ "include": "#function-body" }, { - "begin": "(?x)(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -1356,7 +1393,7 @@ ] }, "method-declaration-name": { - "begin": "(?x)(?=((\\b(? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (? is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -1547,7 +1584,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -1584,9 +1621,12 @@ }, "class-expression": { "name": "meta.class.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)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js.jsx" @@ -2524,6 +2647,33 @@ } } }, + { + "name": "meta.object.member.js.jsx", + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "include": "#literal" - }, - { - "include": "#support-objects" - }, - { - "include": "#object-identifiers" - }, - { - "include": "#punctuation-accessor" - }, - { - "name": "keyword.operator.expression.import.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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "captures": { + "1": { + "name": "storage.modifier.js.jsx" + }, + "2": { + "name": "keyword.operator.rest.js.jsx" + }, + "3": { + "name": "entity.name.function.js.jsx variable.language.this.js.jsx" + }, + "4": { + "name": "entity.name.function.js.jsx" + }, + "5": { + "name": "keyword.operator.optional.js.jsx" + } + } + }, + { + "match": "(?x)(?:(?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\())\n |\n (?:(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\b(?!\\$)))", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3211,34 +3485,6 @@ } } }, - { - "match": "(?x) (?]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\()", - "captures": { - "1": { - "name": "punctuation.accessor.js.jsx" - }, - "2": { - "name": "punctuation.accessor.optional.js.jsx" - }, - "3": { - "name": "support.constant.dom.js.jsx" - }, - "4": { - "name": "support.variable.property.dom.js.jsx" - } - } - }, { "name": "support.class.node.js.jsx", "match": "(?x)(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3439,6 +3662,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.js.jsx", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.js.jsx" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { @@ -3592,6 +3834,13 @@ "name": "punctuation.definition.typeparameters.end.js.jsx" } }, + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + "type-arguments-body": { "patterns": [ { "match": "(?)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type-parameters" + } + ] }, { "name": "meta.type.constructor.js.jsx", @@ -3994,6 +4259,58 @@ }, "type-name": { "patterns": [ + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(<)", + "captures": { + "1": { + "name": "entity.name.type.module.js.jsx" + }, + "2": { + "name": "punctuation.accessor.js.jsx" + }, + "3": { + "name": "punctuation.accessor.optional.js.jsx" + }, + "4": { + "name": "meta.type.parameters.js.jsx punctuation.definition.typeparameters.begin.js.jsx" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.js.jsx punctuation.definition.typeparameters.end.js.jsx" + } + }, + "contentName": "meta.type.parameters.js.jsx", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(<)", + "beginCaptures": { + "1": { + "name": "entity.name.type.js.jsx" + }, + "2": { + "name": "meta.type.parameters.js.jsx punctuation.definition.typeparameters.begin.js.jsx" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.js.jsx punctuation.definition.typeparameters.end.js.jsx" + } + }, + "contentName": "meta.type.parameters.js.jsx", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, { "match": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))", "captures": { @@ -4100,7 +4417,30 @@ "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*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "end": "(?=`)", + "patterns": [ + { + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?`)", + "patterns": [ + { + "include": "#support-function-call-identifiers" + }, + { + "name": "entity.name.function.tagged-template.js.jsx", + "match": "([_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + { + "include": "#type-arguments" + } + ] + }, + { + "name": "string.template.js.jsx", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js.jsx" @@ -4166,7 +4506,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" @@ -4189,7 +4529,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "(? { let uri = Uri.parse(uriPath); - return workspace.openTextDocument(uri).then(doc => { - return doc.getText(); - }, error => { - return Promise.reject(error); - }); + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return workspace.openTextDocument(uri).then(doc => { + return doc.getText(); + }, error => { + return Promise.reject(error); + }); + } else { + const headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uriPath, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); + } }); let handleContentChange = (uri: Uri) => { @@ -193,8 +207,34 @@ export function activate(context: ExtensionContext) { toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + + extensions.onDidChange(_ => { + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); + }); + + documentSelector.forEach(selector => { + toDispose.push(languages.registerSelectionRangeProvider(selector, { + async provideSelectionRanges(document: TextDocument, positions: Position[]): Promise { + const textDocument = client.code2ProtocolConverter.asTextDocumentIdentifier(document); + const rawResult = await client.sendRequest('$/textDocument/selectionRanges', { textDocument, positions: positions.map(client.code2ProtocolConverter.asPosition) }); + if (Array.isArray(rawResult)) { + return rawResult.map(rawSelectionRanges => { + return rawSelectionRanges.reduceRight((parent: SelectionRange | undefined, selectionRange: SelectionRange) => { + return { + range: client.protocol2CodeConverter.asRange(selectionRange.range), + parent, + }; + }, undefined)!; + }); + } + return []; + } + })); + }); }); + + let languageConfiguration: LanguageConfiguration = { wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/, indentationRules: { @@ -225,6 +265,7 @@ function getSchemaAssociation(_context: ExtensionContext): ISchemaAssociations { } if (fileMatch[0] === '%') { fileMatch = fileMatch.replace(/%APP_SETTINGS_HOME%/, '/User'); + fileMatch = fileMatch.replace(/%MACHINE_SETTINGS_HOME%/, '/Machine'); fileMatch = fileMatch.replace(/%APP_WORKSPACES_HOME%/, '/Workspaces'); } else if (fileMatch.charAt(0) !== '/' && !fileMatch.match(/\w+:\/\//)) { fileMatch = '/' + fileMatch; @@ -338,7 +379,7 @@ function getPackageInfo(context: ExtensionContext): IPackageInfo | undefined { aiKey: extensionPackage.aiKey }; } - return void 0; + return undefined; } function readJSONFile(location: string) { diff --git a/extensions/json-language-features/client/src/typings/ref.d.ts b/extensions/json-language-features/client/src/typings/ref.d.ts index 9c1a5df18ed..be1d1b0b776 100644 --- a/extensions/json-language-features/client/src/typings/ref.d.ts +++ b/extensions/json-language-features/client/src/typings/ref.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// +/// diff --git a/extensions/json-language-features/extension.webpack.config.js b/extensions/json-language-features/extension.webpack.config.js index 4c2ab99cf25..a4d4ff955d7 100644 --- a/extensions/json-language-features/extension.webpack.config.js +++ b/extensions/json-language-features/extension.webpack.config.js @@ -9,8 +9,9 @@ const withDefaults = require('../shared.webpack.config'); const path = require('path'); +var webpack = require('webpack'); -module.exports = withDefaults({ +const config = withDefaults({ context: path.join(__dirname, 'client'), entry: { extension: './src/jsonMain.ts', @@ -20,3 +21,8 @@ module.exports = withDefaults({ path: path.join(__dirname, 'client', 'dist') } }); + +// add plugin, don't replace inherited +config.plugins.push(new webpack.IgnorePlugin(/vertx/)); // request-light dependency + +module.exports = config; \ No newline at end of file diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index a1a5204c8b5..9dc4f87d9f0 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -14,6 +14,7 @@ "onLanguage:jsonc" ], "main": "./client/out/jsonMain", + "enableProposedApi": true, "scripts": { "compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", "watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server", @@ -100,11 +101,12 @@ } }, "dependencies": { - "vscode-extension-telemetry": "0.1.0", - "vscode-languageclient": "^5.1.0", - "vscode-nls": "^4.0.0" + "vscode-extension-telemetry": "0.1.1", + "vscode-languageclient": "^5.2.1", + "vscode-nls": "^4.0.0", + "request-light": "^0.2.4" }, "devDependencies": { - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" } } diff --git a/extensions/json-language-features/server/extension.webpack.config.js b/extensions/json-language-features/server/extension.webpack.config.js index b31abe55f25..22b23c1d94b 100644 --- a/extensions/json-language-features/server/extension.webpack.config.js +++ b/extensions/json-language-features/server/extension.webpack.config.js @@ -11,7 +11,7 @@ const withDefaults = require('../../shared.webpack.config'); const path = require('path'); var webpack = require('webpack'); -module.exports = withDefaults({ +const config = withDefaults({ context: path.join(__dirname), entry: { extension: './src/jsonServerMain.ts', @@ -19,12 +19,10 @@ module.exports = withDefaults({ output: { filename: 'jsonServerMain.js', path: path.join(__dirname, 'dist') - }, - plugins: [ - new webpack.NormalModuleReplacementPlugin( - /[/\\]vscode-languageserver[/\\]lib[/\\]files\.js/, - require.resolve('./build/filesFillIn') - ), - new webpack.IgnorePlugin(/vertx/) - ], + } }); + +// add plugin, don't replace inherited +config.plugins.push(new webpack.IgnorePlugin(/vertx/)); // request-light dependency + +module.exports = config; diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index db0182fa088..1f73cea2295 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,16 +12,16 @@ }, "main": "./out/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.0.2", + "jsonc-parser": "^2.0.3", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.2.1", - "vscode-languageserver": "^5.1.0", + "vscode-json-languageservice": "^3.3.0-next.7", + "vscode-languageserver": "^5.3.0-next.2", "vscode-nls": "^4.0.0", "vscode-uri": "^1.0.6" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" }, "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 8a8ce642331..a01a4ea3a3f 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -13,7 +13,6 @@ import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDes import * as fs from 'fs'; import URI from 'vscode-uri'; import * as URL from 'url'; -import { startsWith } from './utils/strings'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -57,46 +56,51 @@ const workspaceContext = { return URL.resolve(resource, relativePath); } }; +function getSchemaRequestService(handledSchemas: { [schema: string]: boolean }) { -const schemaRequestService = (uri: string): Thenable => { - if (startsWith(uri, 'file://')) { - const fsPath = URI.parse(uri).fsPath; - return new Promise((c, e) => { - fs.readFile(fsPath, 'UTF-8', (err, result) => { - err ? e(err.message || err.toString()) : c(result.toString()); - }); - }); - } else if (startsWith(uri, 'vscode://')) { + return (uri: string): Thenable => { + const protocol = uri.substr(0, uri.indexOf(':')); + + if (!handledSchemas || handledSchemas[protocol]) { + if (protocol === 'file') { + const fsPath = URI.parse(uri).fsPath; + return new Promise((c, e) => { + fs.readFile(fsPath, 'UTF-8', (err, result) => { + err ? e(err.message || err.toString()) : c(result.toString()); + }); + }); + } else if (protocol === 'http' || protocol === 'https') { + if (uri.indexOf('//schema.management.azure.com/') !== -1) { + /* __GDPR__ + "json.schema" : { + "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + connection.telemetry.logEvent({ + key: 'json.schema', + value: { + schemaURL: uri + } + }); + } + const headers = { 'Accept-Encoding': 'gzip, deflate' }; + return xhr({ url: uri, followRedirects: 5, headers }).then(response => { + return response.responseText; + }, (error: XHRResponse) => { + return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); + }); + } + } return connection.sendRequest(VSCodeContentRequest.type, uri).then(responseText => { return responseText; }, error => { return Promise.reject(error.message); }); - } - if (uri.indexOf('//schema.management.azure.com/') !== -1) { - /* __GDPR__ - "json.schema" : { - "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - connection.telemetry.logEvent({ - key: 'json.schema', - value: { - schemaURL: uri - } - }); - } - const headers = { 'Accept-Encoding': 'gzip, deflate' }; - return xhr({ url: uri, followRedirects: 5, headers }).then(response => { - return response.responseText; - }, (error: XHRResponse) => { - return Promise.reject(error.responseText || getErrorStatusDescription(error.status) || error.toString()); - }); -}; + }; +} // create the JSON language service let languageService = getLanguageService({ - schemaRequestService, workspaceContext, contributions: [], }); @@ -117,8 +121,10 @@ let hierarchicalDocumentSymbolSupport = false; // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { + const handledProtocols = params.initializationOptions && params.initializationOptions['handledSchemaProtocols']; + languageService = getLanguageService({ - schemaRequestService, + schemaRequestService: getSchemaRequestService(handledProtocols), workspaceContext, contributions: [], clientCapabilities: params.capabilities @@ -143,7 +149,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { 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, + completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : undefined, hoverProvider: true, documentSymbolProvider: true, documentRangeFormattingProvider: false, @@ -174,13 +180,13 @@ interface JSONSchemaSettings { schema?: JSONSchema; } -let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = void 0; -let schemaAssociations: ISchemaAssociations | undefined = void 0; +let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; +let schemaAssociations: ISchemaAssociations | undefined = undefined; let formatterRegistration: Thenable | null = null; // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { - var settings = change.settings; + let settings = change.settings; configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL); jsonConfigurationSettings = settings.json && settings.json.schemas; @@ -233,7 +239,7 @@ function updateConfiguration() { schemas: new Array() }; if (schemaAssociations) { - for (var pattern in schemaAssociations) { + for (const pattern in schemaAssociations) { const association = schemaAssociations[pattern]; if (Array.isArray(association)) { association.forEach(uri => { @@ -427,5 +433,16 @@ connection.onFoldingRanges((params, token) => { }, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token); }); +connection.onRequest('$/textDocument/selectionRanges', async (params, token) => { + return runSafe(() => { + const document = documents.get(params.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.getSelectionRanges(document, params.positions, jsonDocument); + } + return []; + }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); +}); + // Listen on the connection connection.listen(); diff --git a/extensions/json-language-features/server/src/languageModelCache.ts b/extensions/json-language-features/server/src/languageModelCache.ts index 7ce4adca9d4..561de4a9a7a 100644 --- a/extensions/json-language-features/server/src/languageModelCache.ts +++ b/extensions/json-language-features/server/src/languageModelCache.ts @@ -15,7 +15,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime let languageModels: { [uri: string]: { version: number, languageId: string, cTime: number, languageModel: T } } = {}; let nModels = 0; - let cleanupInterval: NodeJS.Timer | undefined = void 0; + let cleanupInterval: NodeJS.Timer | undefined = undefined; if (cleanupIntervalTimeInSec > 0) { cleanupInterval = setInterval(() => { let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; @@ -73,7 +73,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime dispose() { if (typeof cleanupInterval !== 'undefined') { clearInterval(cleanupInterval); - cleanupInterval = void 0; + cleanupInterval = undefined; languageModels = {}; nModels = 0; } diff --git a/extensions/json-language-features/server/src/utils/strings.ts b/extensions/json-language-features/server/src/utils/strings.ts index 837f7238737..42a617f8a8a 100644 --- a/extensions/json-language-features/server/src/utils/strings.ts +++ b/extensions/json-language-features/server/src/utils/strings.ts @@ -3,20 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export function startsWith(haystack: string, needle: string): boolean { - if (haystack.length < needle.length) { - return false; - } - - for (let i = 0; i < needle.length; i++) { - if (haystack[i] !== needle[i]) { - return false; - } - } - - return true; -} - /** * Determines if haystack ends with needle. */ diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 012e346360a..3f0383a8c4f 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== agent-base@4, agent-base@^4.1.0: version "4.1.2" @@ -54,10 +54,10 @@ https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" -jsonc-parser@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d" - integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ== +jsonc-parser@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.3.tgz#6d4199ccab7f21ff5d2a4225050c54e981fb21a2" + integrity sha512-WJi9y9ABL01C8CxTKxRRQkkSpY/x2bo4Gy0WuiZGrInxQqgxQpvkBCLNcDYcHOSdhx4ODgbFcgAvfL49C+PHgQ== ms@2.0.0: version "2.0.0" @@ -73,13 +73,13 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.2.1.tgz#991d51128ebd81c5525d0578cabfa5b03e3cba2a" - integrity sha512-ee9MJ70/xR55ywvm0bZsDLhA800HCRE27AYgMNTU14RSg20Y+ngHdQnUt6OmiTXrQDI/7sne6QUOtHIN0hPQYA== +vscode-json-languageservice@^3.3.0-next.7: + version "3.3.0-next.7" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.3.0-next.7.tgz#3ad4bf31f37fa110676b2c0db69b5f4810bdd4b9" + integrity sha512-uKXnzoZrqNOPRa+FmUdoCpNU5KCLhy7yDGCAzzfn0mocsEllPcspjHcBDu9HJCfT165UkhulZ2gl5vVX5NzBVA== dependencies: - jsonc-parser "^2.0.2" - vscode-languageserver-types "^3.13.0" + jsonc-parser "^2.0.3" + vscode-languageserver-types "^3.14.0" vscode-nls "^4.0.0" vscode-uri "^1.0.6" @@ -88,25 +88,25 @@ vscode-jsonrpc@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.15.0-next.1: + version "3.15.0-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" + integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0, vscode-languageserver-types@^3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== -vscode-languageserver@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.1.0.tgz#012a28f154cc7a848c443d217894942e4c3eeb39" - integrity sha512-CIsrgx2Y5VHS317g/HwkSTWYBIQmy0DwEyZPmB2pEpVOhYFwVsYpbiJwHIIyLQsQtmRaO4eA2xM8KPjNSdXpBw== +vscode-languageserver@^5.3.0-next.2: + version "5.3.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" + integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== dependencies: - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.15.0-next.1" vscode-uri "^1.0.6" vscode-nls@^4.0.0: diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index cfcbd4f4cbe..758f96fd511 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -2,20 +2,41 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +agent-base@4, agent-base@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -28,6 +49,53 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" +es6-promise@^4.0.3: + version "4.2.6" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" + integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +https-proxy-agent@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" + integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== + dependencies: + agent-base "^4.1.0" + debug "^3.1.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +request-light@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.4.tgz#3cea29c126682e6bcadf7915353322eeba01a755" + integrity sha512-pM9Fq5jRnSb+82V7M97rp8FE9/YNeP2L9eckB4Szd7lyeclSIx02aIpPO/6e4m6Dy31+FBN/zkFMTd2HkNO3ow== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + vscode-nls "^4.0.0" + semver@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -38,38 +106,38 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-jsonrpc@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== -vscode-languageclient@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.1.0.tgz#650ab0dc9fd0daaade058a8471aaff5bc3f9580e" - integrity sha512-Z95Kps8UqD4o17HE3uCkZuvenOsxHVH46dKmaGVpGixEFZigPaVuVxLM/JWeIY9aRenoC0ZD9CK1O7L4jpffKg== +vscode-languageclient@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" + integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.13.0" + vscode-languageserver-protocol "3.14.1" -vscode-languageserver-protocol@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" - integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== +vscode-languageserver-protocol@3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" + integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== dependencies: vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.13.0" + vscode-languageserver-types "3.14.0" -vscode-languageserver-types@3.13.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" - integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== +vscode-languageserver-types@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" + integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/json/cgmanifest.json b/extensions/json/cgmanifest.json index d0b23b7e064..fabb7a93aba 100644 --- a/extensions/json/cgmanifest.json +++ b/extensions/json/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/json/package.json b/extensions/json/package.json index e8dc83372ad..fd0dd54ff26 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -24,12 +24,13 @@ ".jshintrc", ".jscsrc", ".eslintrc", - ".babelrc", + ".swcrc", ".webmanifest", ".js.map", ".css.map" ], "filenames": [ + "composer.lock", ".watchmanconfig", ".ember-cli" ], @@ -45,6 +46,8 @@ "JSON with Comments" ], "extensions": [ + ".hintrc", + ".babelrc", ".jsonc" ], "configuration": "./language-configuration.json" @@ -69,4 +72,4 @@ } ] } -} +} \ No newline at end of file diff --git a/extensions/less/cgmanifest.json b/extensions/less/cgmanifest.json index 0b6d4d47181..1951ef04bee 100644 --- a/extensions/less/cgmanifest.json +++ b/extensions/less/cgmanifest.json @@ -10,8 +10,8 @@ } }, "license": "MIT", - "version": "0.0.0" + "version": "0.34.2" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/log/cgmanifest.json b/extensions/log/cgmanifest.json index fc6eb3965e7..f1704efec87 100644 --- a/extensions/log/cgmanifest.json +++ b/extensions/log/cgmanifest.json @@ -10,8 +10,8 @@ } }, "license": "MIT", - "version": "1.2.0" + "version": "2.4.1" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/log/package.json b/extensions/log/package.json index 96fa29a0b30..38da616a8d4 100644 --- a/extensions/log/package.json +++ b/extensions/log/package.json @@ -19,7 +19,7 @@ "*.log.?" ], "aliases": [ - "log" + "Log" ] } ], diff --git a/extensions/lua/cgmanifest.json b/extensions/lua/cgmanifest.json index 78d8538bf08..6cefc1ca59e 100644 --- a/extensions/lua/cgmanifest.json +++ b/extensions/lua/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/lua/language-configuration.json b/extensions/lua/language-configuration.json index 73618cfe630..89e5c45b9ff 100644 --- a/extensions/lua/language-configuration.json +++ b/extensions/lua/language-configuration.json @@ -23,7 +23,7 @@ ["'", "'"] ], "indentationRules": { - "increaseIndentPattern": "((\\b(else|function|then|do|repeat)\\b((?!\\b(end|until)\\b).)*)|(\\{\\s*))$", + "increaseIndentPattern": "^((?!(\\-\\-)).)*((\\b(else|function|then|do|repeat)\\b((?!\\b(end|until)\\b).)*)|(\\{\\s*))$", "decreaseIndentPattern": "^\\s*((\\b(elseif|else|end|until)\\b)|(\\})|(\\)))" } } diff --git a/extensions/make/cgmanifest.json b/extensions/make/cgmanifest.json index 6ac84e7b199..ed80096c8ce 100644 --- a/extensions/make/cgmanifest.json +++ b/extensions/make/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "fadeevab/make.tmbundle", "repositoryUrl": "https://github.com/fadeevab/make.tmbundle", - "commitHash": "c6b3ae6e4f22e01fc483be5bb34a9682c28f3219" + "commitHash": "bd71f44ea55d61be711bd7676e600a7333cc79ea" } }, "licenseDetail": [ @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/make/syntaxes/make.tmLanguage.json b/extensions/make/syntaxes/make.tmLanguage.json index cae70e5ec98..f2e43e163ee 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/c6b3ae6e4f22e01fc483be5bb34a9682c28f3219", + "version": "https://github.com/fadeevab/make.tmbundle/commit/bd71f44ea55d61be711bd7676e600a7333cc79ea", "name": "Makefile", "scopeName": "source.makefile", "patterns": [ @@ -260,37 +260,6 @@ } ] }, - "shell-interpolation": { - "begin": "(?=`)", - "end": "(?!\\G)", - "name": "meta.embedded.line.shell", - "patterns": [ - { - "begin": "`", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.makefile" - } - }, - "contentName": "source.shell", - "end": "(`)", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.makefile" - }, - "1": { - "name": "source.shell" - } - }, - "name": "string.interpolated.backtick.makefile", - "patterns": [ - { - "include": "source.shell" - } - ] - } - ] - }, "target": { "begin": "^(?!\\t)([^:]*)(:)(?!\\=)", "beginCaptures": { @@ -351,7 +320,12 @@ ] }, "recipe": { - "begin": "^\\t", + "begin": "^\\t([+\\-@]*)", + "beginCaptures": { + "1": { + "name": "keyword.control.$1.makefile" + } + }, "end": "[^\\\\]$", "name": "meta.scope.recipe.makefile", "patterns": [ @@ -361,9 +335,6 @@ }, { "include": "#variables" - }, - { - "include": "source.shell" } ] }, @@ -371,7 +342,12 @@ "begin": "(^[ ]*|\\G\\s*)([^\\s]+)\\s*((?) ?", + "captures": { + "2": { + "name": "punctuation.definition.quote.begin.markdown" + } + }, + "name": "markup.quote.markdown", + "patterns": [ + { + "include": "#block" + } + ], + "while": "(^|\\G)\\s*(>) ?" + }, + "fenced_code_block_css": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(css|css.erb)(\\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.css", + "patterns": [ + { + "include": "source.css" + } + ] + } + ] + }, + "fenced_code_block_basic": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(html|htm|shtml|xhtml|inc|tmpl|tpl)(\\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.html", + "patterns": [ + { + "include": "text.html.basic" + } + ] + } + ] + }, + "fenced_code_block_ini": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(ini|conf)(\\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.ini", + "patterns": [ + { + "include": "source.ini" + } + ] + } + ] + }, + "fenced_code_block_java": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(java|bsh)(\\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.java", + "patterns": [ + { + "include": "source.java" + } + ] + } + ] + }, + "fenced_code_block_lua": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(lua)(\\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.lua", + "patterns": [ + { + "include": "source.lua" + } + ] + } + ] + }, + "fenced_code_block_makefile": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(Makefile|makefile|GNUmakefile|OCamlMakefile)(\\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.makefile", + "patterns": [ + { + "include": "source.makefile" + } + ] + } + ] + }, + "fenced_code_block_perl": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(perl|pl|pm|pod|t|PL|psgi|vcl)(\\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.perl", + "patterns": [ + { + "include": "source.perl" + } + ] + } + ] + }, + "fenced_code_block_r": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(R|r|s|S|Rprofile)(\\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.r", + "patterns": [ + { + "include": "source.r" + } + ] + } + ] + }, + "fenced_code_block_ruby": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\\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.ruby", + "patterns": [ + { + "include": "source.ruby" + } + ] + } + ] + }, + "fenced_code_block_php": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(php|php3|php4|php5|phpt|phtml|aw|ctp)(\\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.php", + "patterns": [ + { + "include": "text.html.basic" + }, + { + "include": "source.php" + } + ] + } + ] + }, + "fenced_code_block_sql": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(sql|ddl|dml)(\\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.sql", + "patterns": [ + { + "include": "source.sql" + } + ] + } + ] + }, + "fenced_code_block_vs_net": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(vb)(\\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.vs_net", + "patterns": [ + { + "include": "source.asp.vb.net" + } + ] + } + ] + }, + "fenced_code_block_xml": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\\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.xml", + "patterns": [ + { + "include": "text.xml" + } + ] + } + ] + }, + "fenced_code_block_xsl": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(xsl|xslt)(\\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.xsl", + "patterns": [ + { + "include": "text.xml.xsl" + } + ] + } + ] + }, + "fenced_code_block_yaml": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(yaml|yml)(\\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.yaml", + "patterns": [ + { + "include": "source.yaml" + } + ] + } + ] + }, + "fenced_code_block_dosbatch": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(bat|batch)(\\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.dosbatch", + "patterns": [ + { + "include": "source.batchfile" + } + ] + } + ] + }, + "fenced_code_block_clojure": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(clj|cljs|clojure)(\\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.clojure", + "patterns": [ + { + "include": "source.clojure" + } + ] + } + ] + }, + "fenced_code_block_coffee": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(coffee|Cakefile|coffee.erb)(\\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.coffee", + "patterns": [ + { + "include": "source.coffee" + } + ] + } + ] + }, + "fenced_code_block_c": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(c|h)(\\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.c", + "patterns": [ + { + "include": "source.c" + } + ] + } + ] + }, + "fenced_code_block_cpp": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(cpp|c\\+\\+|cxx)(\\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.cpp", + "patterns": [ + { + "include": "source.cpp" + } + ] + } + ] + }, + "fenced_code_block_diff": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(patch|diff|rej)(\\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.diff", + "patterns": [ + { + "include": "source.diff" + } + ] + } + ] + }, + "fenced_code_block_dockerfile": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(dockerfile|Dockerfile)(\\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.dockerfile", + "patterns": [ + { + "include": "source.dockerfile" + } + ] + } + ] + }, + "fenced_code_block_git_commit": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(COMMIT_EDITMSG|MERGE_MSG)(\\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.git_commit", + "patterns": [ + { + "include": "text.git-commit" + } + ] + } + ] + }, + "fenced_code_block_git_rebase": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(git-rebase-todo)(\\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.git_rebase", + "patterns": [ + { + "include": "text.git-rebase" + } + ] + } + ] + }, + "fenced_code_block_go": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(go|golang)(\\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.go", + "patterns": [ + { + "include": "source.go" + } + ] + } + ] + }, + "fenced_code_block_groovy": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(groovy|gvy)(\\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.groovy", + "patterns": [ + { + "include": "source.groovy" + } + ] + } + ] + }, + "fenced_code_block_pug": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(jade|pug)(\\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.pug", + "patterns": [ + { + "include": "text.pug" + } + ] + } + ] + }, + "fenced_code_block_js": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs)(\\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.javascript", + "patterns": [ + { + "include": "source.js" + } + ] + } + ] + }, + "fenced_code_block_js_regexp": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(regexp)(\\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.js_regexp", + "patterns": [ + { + "include": "source.js.regexp" + } + ] + } + ] + }, + "fenced_code_block_json": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(json|json5|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\\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.json", + "patterns": [ + { + "include": "source.json" + } + ] + } + ] + }, + "fenced_code_block_jsonc": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(jsonc)(\\s+[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "5": { + "name": "fenced_code.block.language" + }, + "6": { + "name": "fenced_code.block.language.attributes" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.jsonc", + "patterns": [ + { + "include": "source.json.comments" + } + ] + } + ] + }, + "fenced_code_block_less": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(less)(\\s+[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "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.less", + "patterns": [ + { + "include": "source.css.less" + } + ] + } + ] + }, + "fenced_code_block_objc": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(objectivec|objective-c|mm|objc|obj-c|m|h)(\\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.objc", + "patterns": [ + { + "include": "source.objc" + } + ] + } + ] + }, + "fenced_code_block_swift": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(swift)(\\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.swift", + "patterns": [ + { + "include": "source.swift" + } + ] + } + ] + }, + "fenced_code_block_scss": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(scss)(\\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.scss", + "patterns": [ + { + "include": "source.css.scss" + } + ] + } + ] + }, + "fenced_code_block_perl6": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(perl6|p6|pl6|pm6|nqp)(\\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.perl6", + "patterns": [ + { + "include": "source.perl.6" + } + ] + } + ] + }, + "fenced_code_block_powershell": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(powershell|ps1|psm1|psd1)(\\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.powershell", + "patterns": [ + { + "include": "source.powershell" + } + ] + } + ] + }, + "fenced_code_block_python": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\\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.python", + "patterns": [ + { + "include": "source.python" + } + ] + } + ] + }, + "fenced_code_block_regexp_python": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(re)(\\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.regexp_python", + "patterns": [ + { + "include": "source.regexp.python" + } + ] + } + ] + }, + "fenced_code_block_rust": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(rust|rs)(\\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.rust", + "patterns": [ + { + "include": "source.rust" + } + ] + } + ] + }, + "fenced_code_block_scala": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(scala|sbt)(\\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.scala", + "patterns": [ + { + "include": "source.scala" + } + ] + } + ] + }, + "fenced_code_block_shell": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\\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.shellscript", + "patterns": [ + { + "include": "source.shell" + } + ] + } + ] + }, + "fenced_code_block_ts": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(typescript|ts)(\\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.typescript", + "patterns": [ + { + "include": "source.ts" + } + ] + } + ] + }, + "fenced_code_block_tsx": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(tsx)(\\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.typescriptreact", + "patterns": [ + { + "include": "source.tsx" + } + ] + } + ] + }, + "fenced_code_block_csharp": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(cs|csharp|c#)(\\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.csharp", + "patterns": [ + { + "include": "source.cs" + } + ] + } + ] + }, + "fenced_code_block_fsharp": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(fs|fsharp|f#)(\\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.fsharp", + "patterns": [ + { + "include": "source.fsharp" + } + ] + } + ] + }, + "fenced_code_block_dart": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(dart)(\\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.dart", + "patterns": [ + { + "include": "source.dart" + } + ] + } + ] + }, + "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_markdown": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(markdown|md)(\\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.markdown", + "patterns": [ + { + "include": "text.html.markdown" + } + ] + } + ] + }, + "fenced_code_block": { + "patterns": [ { "include": "#fenced_code_block_css" }, @@ -129,6 +1783,9 @@ { "include": "#fenced_code_block_objc" }, + { + "include": "#fenced_code_block_swift" + }, { "include": "#fenced_code_block_scss" }, @@ -176,1936 +1833,320 @@ }, { "include": "#fenced_code_block_unknown" - }, - { - "include": "#raw_block" - }, - { - "include": "#link-def" - }, - { - "include": "#html" - }, - { - "include": "#paragraph" } - ], - "repository": { - "blockquote": { - "begin": "(^|\\G)[ ]{0,3}(>) ?", + ] + }, + "fenced_code_block_unknown": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?=([^`~]*)?$)", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "4": { + "name": "fenced_code.block.language" + } + }, + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "name": "markup.fenced_code.block.markdown" + }, + "heading": { + "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s*(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", + "captures": { + "1": { + "patterns": [ + { + "match": "(#{6})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "name": "heading.6.markdown", + "captures": { + "1": { + "name": "punctuation.definition.heading.markdown" + }, + "2": { + "name": "entity.name.section.markdown" + }, + "3": { + "name": "punctuation.definition.heading.markdown" + } + } + }, + { + "match": "(#{5})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "name": "heading.5.markdown", + "captures": { + "1": { + "name": "punctuation.definition.heading.markdown" + }, + "2": { + "name": "entity.name.section.markdown" + }, + "3": { + "name": "punctuation.definition.heading.markdown" + } + } + }, + { + "match": "(#{4})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "name": "heading.4.markdown", + "captures": { + "1": { + "name": "punctuation.definition.heading.markdown" + }, + "2": { + "name": "entity.name.section.markdown" + }, + "3": { + "name": "punctuation.definition.heading.markdown" + } + } + }, + { + "match": "(#{3})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "name": "heading.3.markdown", + "captures": { + "1": { + "name": "punctuation.definition.heading.markdown" + }, + "2": { + "name": "entity.name.section.markdown" + }, + "3": { + "name": "punctuation.definition.heading.markdown" + } + } + }, + { + "match": "(#{2})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "name": "heading.2.markdown", + "captures": { + "1": { + "name": "punctuation.definition.heading.markdown" + }, + "2": { + "name": "entity.name.section.markdown" + }, + "3": { + "name": "punctuation.definition.heading.markdown" + } + } + }, + { + "match": "(#{1})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "name": "heading.1.markdown", + "captures": { + "1": { + "name": "punctuation.definition.heading.markdown" + }, + "2": { + "name": "entity.name.section.markdown" + }, + "3": { + "name": "punctuation.definition.heading.markdown" + } + } + } + ] + } + }, + "name": "markup.heading.markdown", + "patterns": [ + { + "include": "#inline" + } + ] + }, + "heading-setext": { + "patterns": [ + { + "match": "^(={3,})(?=[ \\t]*$\\n?)", + "name": "markup.heading.setext.1.markdown" + }, + { + "match": "^(-{3,})(?=[ \\t]*$\\n?)", + "name": "markup.heading.setext.2.markdown" + } + ] + }, + "html": { + "patterns": [ + { + "begin": "(^|\\G)\\s*()", + "name": "comment.block.html" + }, + { + "begin": "(^|\\G)\\s*(?=<(script|style|pre)(\\s|$|>)(?!.*?))", + "end": "(?=.*)", + "patterns": [ + { + "begin": "(\\s*|$)", + "patterns": [ + { + "include": "text.html.basic" + } + ], + "while": "^(?!.*)" + } + ] + }, + { + "begin": "(^|\\G)\\s*(?=))", + "patterns": [ + { + "include": "text.html.basic" + } + ], + "while": "^(?!\\s*$)" + }, + { + "begin": "(^|\\G)\\s*(?=(<[a-zA-Z0-9\\-](/?>|\\s.*?>)|)\\s*$)", + "patterns": [ + { + "include": "text.html.basic" + } + ], + "while": "^(?!\\s*$)" + } + ] + }, + "link-def": { + "captures": { + "1": { + "name": "punctuation.definition.constant.markdown" + }, + "2": { + "name": "constant.other.reference.link.markdown" + }, + "3": { + "name": "punctuation.definition.constant.markdown" + }, + "4": { + "name": "punctuation.separator.key-value.markdown" + }, + "5": { + "name": "punctuation.definition.link.markdown" + }, + "6": { + "name": "markup.underline.link.markdown" + }, + "7": { + "name": "punctuation.definition.link.markdown" + }, + "8": { + "name": "string.other.link.description.title.markdown" + }, + "9": { + "name": "punctuation.definition.string.begin.markdown" + }, + "10": { + "name": "punctuation.definition.string.end.markdown" + }, + "11": { + "name": "string.other.link.description.title.markdown" + }, + "12": { + "name": "punctuation.definition.string.begin.markdown" + }, + "13": { + "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", + "name": "meta.link.reference.def.markdown" + }, + "list_paragraph": { + "begin": "(^|\\G)(?=\\S)(?![*+->]\\s|[0-9]+\\.\\s)", + "name": "meta.paragraph.markdown", + "patterns": [ + { + "include": "#inline" + }, + { + "include": "text.html.basic" + }, + { + "include": "#heading-setext" + } + ], + "while": "(^|\\G)(?!\\s*$|#|[ ]{0,3}([-*_>][ ]{2,}){3,}[ \\t]*$\\n?|[ ]{0,3}[*+->]|[ ]{0,3}[0-9]+\\.)" + }, + "lists": { + "patterns": [ + { + "begin": "(^|\\G)([ ]{0,3})([*+-])([ \\t])", + "beginCaptures": { + "3": { + "name": "punctuation.definition.list.begin.markdown" + } + }, + "comment": "Currently does not support un-indented second lines.", + "name": "markup.list.unnumbered.markdown", "patterns": [ { "include": "#block" + }, + { + "include": "#list_paragraph" } ], - "while": "(^|\\G)\\s*(>) ?" + "while": "((^|\\G)([ ]{2,4}|\\t))|(^[ \\t]*$)" }, - "fenced_code_block_css": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(css|css.erb)(\\s+[^`~]*)?$)", - "name": "markup.fenced_code.block.markdown", - "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + { + "begin": "(^|\\G)([ ]{0,3})([0-9]+\\.)([ \\t])", "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" + "name": "punctuation.definition.list.begin.markdown" } }, + "name": "markup.list.numbered.markdown", "patterns": [ { - "begin": "(^|\\G)(\\s*)(.*)", - "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", - "contentName": "meta.embedded.block.css", - "patterns": [ - { - "include": "source.css" - } - ] - } - ] - }, - "fenced_code_block_basic": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(html|htm|shtml|xhtml|inc|tmpl|tpl)(\\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.html", - "patterns": [ - { - "include": "text.html.basic" - } - ] - } - ] - }, - "fenced_code_block_ini": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(ini|conf)(\\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.ini", - "patterns": [ - { - "include": "source.ini" - } - ] - } - ] - }, - "fenced_code_block_java": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(java|bsh)(\\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.java", - "patterns": [ - { - "include": "source.java" - } - ] - } - ] - }, - "fenced_code_block_lua": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(lua)(\\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.lua", - "patterns": [ - { - "include": "source.lua" - } - ] - } - ] - }, - "fenced_code_block_makefile": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(Makefile|makefile|GNUmakefile|OCamlMakefile)(\\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.makefile", - "patterns": [ - { - "include": "source.makefile" - } - ] - } - ] - }, - "fenced_code_block_perl": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(perl|pl|pm|pod|t|PL|psgi|vcl)(\\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.perl", - "patterns": [ - { - "include": "source.perl" - } - ] - } - ] - }, - "fenced_code_block_r": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(R|r|s|S|Rprofile)(\\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.r", - "patterns": [ - { - "include": "source.r" - } - ] - } - ] - }, - "fenced_code_block_ruby": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(ruby|rb|rbx|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)(\\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.ruby", - "patterns": [ - { - "include": "source.ruby" - } - ] - } - ] - }, - "fenced_code_block_php": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(php|php3|php4|php5|phpt|phtml|aw|ctp)(\\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.php", - "patterns": [ - { - "include": "text.html.basic" - }, - { - "include": "source.php" - } - ] - } - ] - }, - "fenced_code_block_sql": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(sql|ddl|dml)(\\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.sql", - "patterns": [ - { - "include": "source.sql" - } - ] - } - ] - }, - "fenced_code_block_vs_net": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(vb)(\\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.vs_net", - "patterns": [ - { - "include": "source.asp.vb.net" - } - ] - } - ] - }, - "fenced_code_block_xml": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)(\\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.xml", - "patterns": [ - { - "include": "text.xml" - } - ] - } - ] - }, - "fenced_code_block_xsl": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(xsl|xslt)(\\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.xsl", - "patterns": [ - { - "include": "text.xml.xsl" - } - ] - } - ] - }, - "fenced_code_block_yaml": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(yaml|yml)(\\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.yaml", - "patterns": [ - { - "include": "source.yaml" - } - ] - } - ] - }, - "fenced_code_block_dosbatch": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(bat|batch)(\\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.dosbatch", - "patterns": [ - { - "include": "source.batchfile" - } - ] - } - ] - }, - "fenced_code_block_clojure": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(clj|cljs|clojure)(\\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.clojure", - "patterns": [ - { - "include": "source.clojure" - } - ] - } - ] - }, - "fenced_code_block_coffee": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(coffee|Cakefile|coffee.erb)(\\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.coffee", - "patterns": [ - { - "include": "source.coffee" - } - ] - } - ] - }, - "fenced_code_block_c": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(c|h)(\\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.c", - "patterns": [ - { - "include": "source.c" - } - ] - } - ] - }, - "fenced_code_block_cpp": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(cpp|c\\+\\+|cxx)(\\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.cpp", - "patterns": [ - { - "include": "source.cpp" - } - ] - } - ] - }, - "fenced_code_block_diff": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(patch|diff|rej)(\\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.diff", - "patterns": [ - { - "include": "source.diff" - } - ] - } - ] - }, - "fenced_code_block_dockerfile": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(dockerfile|Dockerfile)(\\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.dockerfile", - "patterns": [ - { - "include": "source.dockerfile" - } - ] - } - ] - }, - "fenced_code_block_git_commit": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(COMMIT_EDITMSG|MERGE_MSG)(\\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.git_commit", - "patterns": [ - { - "include": "text.git-commit" - } - ] - } - ] - }, - "fenced_code_block_git_rebase": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(git-rebase-todo)(\\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.git_rebase", - "patterns": [ - { - "include": "text.git-rebase" - } - ] - } - ] - }, - "fenced_code_block_go": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(go|golang)(\\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.go", - "patterns": [ - { - "include": "source.go" - } - ] - } - ] - }, - "fenced_code_block_groovy": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(groovy|gvy)(\\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.groovy", - "patterns": [ - { - "include": "source.groovy" - } - ] - } - ] - }, - "fenced_code_block_pug": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(jade|pug)(\\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.pug", - "patterns": [ - { - "include": "text.pug" - } - ] - } - ] - }, - "fenced_code_block_js": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs)(\\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.javascript", - "patterns": [ - { - "include": "source.js" - } - ] - } - ] - }, - "fenced_code_block_js_regexp": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(regexp)(\\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.js_regexp", - "patterns": [ - { - "include": "source.js.regexp" - } - ] - } - ] - }, - "fenced_code_block_json": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(json|json5|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)(\\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.json", - "patterns": [ - { - "include": "source.json" - } - ] - } - ] - }, - "fenced_code_block_jsonc": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(jsonc)(\\s+[^`~]*)?$)", - "name": "markup.fenced_code.block.markdown", - "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", - "beginCaptures": { - "3": { - "name": "punctuation.definition.markdown" - }, - "5": { - "name": "fenced_code.block.language" - }, - "6": { - "name": "fenced_code.block.language.attributes" - } - }, - "endCaptures": { - "3": { - "name": "punctuation.definition.markdown" - } - }, - "patterns": [ - { - "begin": "(^|\\G)(\\s*)(.*)", - "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", - "contentName": "meta.embedded.block.jsonc", - "patterns": [ - { - "include": "source.json.comments" - } - ] - } - ] - }, - "fenced_code_block_less": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(less)(\\s+[^`~]*)?$)", - "name": "markup.fenced_code.block.markdown", - "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.less", - "patterns": [ - { - "include": "source.css.less" - } - ] - } - ] - }, - "fenced_code_block_objc": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(objectivec|objective-c|mm|objc|obj-c|m|h)(\\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.objc", - "patterns": [ - { - "include": "source.objc" - } - ] - } - ] - }, - "fenced_code_block_scss": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(scss)(\\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.scss", - "patterns": [ - { - "include": "source.css.scss" - } - ] - } - ] - }, - "fenced_code_block_perl6": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(perl6|p6|pl6|pm6|nqp)(\\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.perl6", - "patterns": [ - { - "include": "source.perl.6" - } - ] - } - ] - }, - "fenced_code_block_powershell": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(powershell|ps1|psm1|psd1)(\\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.powershell", - "patterns": [ - { - "include": "source.powershell" - } - ] - } - ] - }, - "fenced_code_block_python": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(python|py|py3|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gyp|gypi)(\\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.python", - "patterns": [ - { - "include": "source.python" - } - ] - } - ] - }, - "fenced_code_block_regexp_python": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(re)(\\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.regexp_python", - "patterns": [ - { - "include": "source.regexp.python" - } - ] - } - ] - }, - "fenced_code_block_rust": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(rust|rs)(\\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.rust", - "patterns": [ - { - "include": "source.rust" - } - ] - } - ] - }, - "fenced_code_block_scala": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(scala|sbt)(\\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.scala", - "patterns": [ - { - "include": "source.scala" - } - ] - } - ] - }, - "fenced_code_block_shell": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init)(\\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.shellscript", - "patterns": [ - { - "include": "source.shell" - } - ] - } - ] - }, - "fenced_code_block_ts": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(typescript|ts)(\\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.typescript", - "patterns": [ - { - "include": "source.ts" - } - ] - } - ] - }, - "fenced_code_block_tsx": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(tsx)(\\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.typescriptreact", - "patterns": [ - { - "include": "source.tsx" - } - ] - } - ] - }, - "fenced_code_block_csharp": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(cs|csharp|c#)(\\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.csharp", - "patterns": [ - { - "include": "source.cs" - } - ] - } - ] - }, - "fenced_code_block_fsharp": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(fs|fsharp|f#)(\\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.fsharp", - "patterns": [ - { - "include": "source.fsharp" - } - ] - } - ] - }, - "fenced_code_block_dart": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(dart)(\\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.dart", - "patterns": [ - { - "include": "source.dart" - } - ] - } - ] - }, - "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_markdown": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(markdown|md)(\\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.markdown", - "patterns": [ - { - "include": "text.html.markdown" - } - ] - } - ] - }, - "fenced_code_block_unknown": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?=([^`~]*)?$)", - "beginCaptures": { - "3": { - "name": "punctuation.definition.markdown" - }, - "4": { - "name": "fenced_code.block.language" - } - }, - "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", - "endCaptures": { - "3": { - "name": "punctuation.definition.markdown" - } - }, - "name": "markup.fenced_code.block.markdown" - }, - "heading": { - "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s*(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", - "captures": { - "1": { - "patterns": [ - { - "match": "(#{6})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", - "name": "heading.6.markdown", - "captures": { - "1": { - "name": "punctuation.definition.heading.markdown" - }, - "2": { - "name": "entity.name.section.markdown" - }, - "3": { - "name": "punctuation.definition.heading.markdown" - } - } - }, - { - "match": "(#{5})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", - "name": "heading.5.markdown", - "captures": { - "1": { - "name": "punctuation.definition.heading.markdown" - }, - "2": { - "name": "entity.name.section.markdown" - }, - "3": { - "name": "punctuation.definition.heading.markdown" - } - } - }, - { - "match": "(#{4})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", - "name": "heading.4.markdown", - "captures": { - "1": { - "name": "punctuation.definition.heading.markdown" - }, - "2": { - "name": "entity.name.section.markdown" - }, - "3": { - "name": "punctuation.definition.heading.markdown" - } - } - }, - { - "match": "(#{3})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", - "name": "heading.3.markdown", - "captures": { - "1": { - "name": "punctuation.definition.heading.markdown" - }, - "2": { - "name": "entity.name.section.markdown" - }, - "3": { - "name": "punctuation.definition.heading.markdown" - } - } - }, - { - "match": "(#{2})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", - "name": "heading.2.markdown", - "captures": { - "1": { - "name": "punctuation.definition.heading.markdown" - }, - "2": { - "name": "entity.name.section.markdown" - }, - "3": { - "name": "punctuation.definition.heading.markdown" - } - } - }, - { - "match": "(#{1})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", - "name": "heading.1.markdown", - "captures": { - "1": { - "name": "punctuation.definition.heading.markdown" - }, - "2": { - "name": "entity.name.section.markdown" - }, - "3": { - "name": "punctuation.definition.heading.markdown" - } - } - } - ] - } - }, - "name": "markup.heading.markdown", - "patterns": [ - { - "include": "#inline" - } - ] - }, - "heading-setext": { - "patterns": [ - { - "match": "^(={3,})(?=[ \\t]*$\\n?)", - "name": "markup.heading.setext.1.markdown" + "include": "#block" }, { - "match": "^(-{3,})(?=[ \\t]*$\\n?)", - "name": "markup.heading.setext.2.markdown" - } - ] - }, - "html": { - "patterns": [ - { - "begin": "(^|\\G)\\s*()", - "name": "comment.block.html" - }, - { - "begin": "(^|\\G)\\s*(?=<(script|style|pre)(\\s|$|>)(?!.*?))", - "end": "(?=.*)", - "patterns": [ - { - "begin": "(\\s*|$)", - "patterns": [ - { - "include": "text.html.basic" - } - ], - "while": "^(?!.*)" - } - ] - }, - { - "begin": "(^|\\G)\\s*(?=))", - "patterns": [ - { - "include": "text.html.basic" - } - ], - "while": "^(?!\\s*$)" - }, - { - "begin": "(^|\\G)\\s*(?=(<[a-zA-Z0-9\\-](/?>|\\s.*?>)|)\\s*$)", - "patterns": [ - { - "include": "text.html.basic" - } - ], - "while": "^(?!\\s*$)" - } - ] - }, - "link-def": { - "captures": { - "1": { - "name": "punctuation.definition.constant.markdown" - }, - "2": { - "name": "constant.other.reference.link.markdown" - }, - "3": { - "name": "punctuation.definition.constant.markdown" - }, - "4": { - "name": "punctuation.separator.key-value.markdown" - }, - "5": { - "name": "punctuation.definition.link.markdown" - }, - "6": { - "name": "markup.underline.link.markdown" - }, - "7": { - "name": "punctuation.definition.link.markdown" - }, - "8": { - "name": "string.other.link.description.title.markdown" - }, - "9": { - "name": "punctuation.definition.string.begin.markdown" - }, - "10": { - "name": "punctuation.definition.string.end.markdown" - }, - "11": { - "name": "string.other.link.description.title.markdown" - }, - "12": { - "name": "punctuation.definition.string.begin.markdown" - }, - "13": { - "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", - "name": "meta.link.reference.def.markdown" - }, - "list_paragraph": { - "begin": "(^|\\G)(?=\\S)(?![*+->]\\s|[0-9]+\\.\\s)", - "name": "meta.paragraph.markdown", - "patterns": [ - { - "include": "#inline" - }, - { - "include": "text.html.basic" - }, - { - "include": "#heading-setext" + "include": "#list_paragraph" } ], - "while": "(^|\\G)(?!\\s*$|#|[ ]{0,3}([-*_>][ ]{2,}){3,}[ \\t]*$\\n?|[ ]{0,3}[*+->]|[ ]{0,3}[0-9]+\\.)" - }, - "lists": { - "patterns": [ - { - "begin": "(^|\\G)([ ]{0,3})([*+-])([ \\t])", - "beginCaptures": { - "3": { - "name": "punctuation.definition.list.begin.markdown" - } - }, - "comment": "Currently does not support un-indented second lines.", - "name": "markup.list.unnumbered.markdown", - "patterns": [ - { - "include": "#block" - }, - { - "include": "#list_paragraph" - } - ], - "while": "((^|\\G)([ ]{2,4}|\\t))|(^[ \\t]*$)" - }, - { - "begin": "(^|\\G)([ ]{0,3})([0-9]+\\.)([ \\t])", - "beginCaptures": { - "3": { - "name": "punctuation.definition.list.begin.markdown" - } - }, - "name": "markup.list.numbered.markdown", - "patterns": [ - { - "include": "#block" - }, - { - "include": "#list_paragraph" - } - ], - "while": "((^|\\G)([ ]{2,4}|\\t))|(^[ \\t]*$)" - } - ] - }, - "paragraph": { - "begin": "(^|\\G)[ ]{0,3}(?=\\S)", - "name": "meta.paragraph.markdown", - "patterns": [ - { - "include": "#inline" - }, - { - "include": "text.html.basic" - }, - { - "include": "#heading-setext" - } - ], - "while": "(^|\\G)((?=\\s*[-=]{3,}\\s*$)|[ ]{4,}(?=\\S))" - }, - "raw_block": { - "begin": "(^|\\G)([ ]{4}|\\t)", - "name": "markup.raw.block.markdown", - "while": "(^|\\G)([ ]{4}|\\t)" - }, - "separator": { - "match": "(^|\\G)[ ]{0,3}([\\*\\-\\_])([ ]{0,2}\\2){2,}[ \\t]*$\\n?", - "name": "meta.separator.markdown" + "while": "((^|\\G)([ ]{2,4}|\\t))|(^[ \\t]*$)" } - } + ] + }, + "paragraph": { + "begin": "(^|\\G)[ ]{0,3}(?=\\S)", + "name": "meta.paragraph.markdown", + "patterns": [ + { + "include": "#inline" + }, + { + "include": "text.html.basic" + }, + { + "include": "#heading-setext" + } + ], + "while": "(^|\\G)((?=\\s*[-=]{3,}\\s*$)|[ ]{4,}(?=\\S))" + }, + "raw_block": { + "begin": "(^|\\G)([ ]{4}|\\t)", + "name": "markup.raw.block.markdown", + "while": "(^|\\G)([ ]{4}|\\t)" + }, + "separator": { + "match": "(^|\\G)[ ]{0,3}([\\*\\-\\_])([ ]{0,2}\\2){2,}[ \\t]*$\\n?", + "name": "meta.separator.markdown" }, "frontMatter": { "begin": "\\A-{3}\\s*$", @@ -2161,370 +2202,368 @@ { "include": "#link-ref-shortcut" } - ], - "repository": { - "ampersand": { - "comment": "Markdown will convert this for us. We match it so that the HTML grammar will not mark it up as invalid.", - "match": "&(?!([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+);)", - "name": "meta.other.valid-ampersand.markdown" - }, - "bold": { - "begin": "(?x)\n (\\*\\*(?=\\w)|(?]*+> # 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", - "captures": { - "1": { - "name": "punctuation.definition.bold.markdown" - } - }, - "end": "(?<=\\S)(\\1)", - "name": "markup.bold.markdown", - "patterns": [ - { - "applyEndPatternLast": 1, - "begin": "(?=<[^>]*?>)", - "end": "(?<=>)", - "patterns": [ - { - "include": "text.html.basic" - } - ] - }, - { - "include": "#escape" - }, - { - "include": "#ampersand" - }, - { - "include": "#bracket" - }, - { - "include": "#raw" - }, - { - "include": "#bold" - }, - { - "include": "#italic" - }, - { - "include": "#image-inline" - }, - { - "include": "#link-inline" - }, - { - "include": "#link-inet" - }, - { - "include": "#link-email" - }, - { - "include": "#image-ref" - }, - { - "include": "#link-ref-literal" - }, - { - "include": "#link-ref" - }, - { - "include": "#link-ref-shortcut" - } - ] - }, - "bracket": { - "comment": "Markdown will convert this for us. We match it so that the HTML grammar will not mark it up as invalid.", - "match": "<(?![a-z/?\\$!])", - "name": "meta.other.valid-bracket.markdown" - }, - "escape": { - "match": "\\\\[-`*_#+.!(){}\\[\\]\\\\>]", - "name": "constant.character.escape.markdown" - }, - "image-inline": { - "captures": { - "1": { - "name": "punctuation.definition.string.begin.markdown" - }, - "2": { - "name": "string.other.link.description.markdown" - }, - "4": { - "name": "punctuation.definition.string.end.markdown" - }, - "5": { - "name": "punctuation.definition.metadata.markdown" - }, - "6": { - "name": "punctuation.definition.link.markdown" - }, - "7": { - "name": "markup.underline.link.image.markdown" - }, - "8": { - "name": "punctuation.definition.link.markdown" - }, - "9": { - "name": "string.other.link.description.title.markdown" - }, - "10": { - "name": "punctuation.definition.string.markdown" - }, - "11": { - "name": "punctuation.definition.string.markdown" - }, - "12": { - "name": "string.other.link.description.title.markdown" - }, - "13": { - "name": "punctuation.definition.string.markdown" - }, - "14": { - "name": "punctuation.definition.string.markdown" - }, - "15": { - "name": "punctuation.definition.metadata.markdown" - } - }, - "match": "(?x)\n (\\!\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (<?)(\\S+?)(>?) # 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.image.inline.markdown" - }, - "image-ref": { - "captures": { - "1": { - "name": "punctuation.definition.string.begin.markdown" - }, - "2": { - "name": "string.other.link.description.markdown" - }, - "4": { - "name": "punctuation.definition.string.begin.markdown" - }, - "5": { - "name": "punctuation.definition.constant.markdown" - }, - "6": { - "name": "constant.other.reference.link.markdown" - }, - "7": { - "name": "punctuation.definition.constant.markdown" - } - }, - "match": "(\\!\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])[ ]?(\\[)(.*?)(\\])", - "name": "meta.image.reference.markdown" - }, - "italic": { - "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" - } - }, - "end": "(?<=\\S)(\\1)((?!\\1)|(?=\\1\\1))", - "name": "markup.italic.markdown", - "patterns": [ - { - "applyEndPatternLast": 1, - "begin": "(?=<[^>]*?>)", - "end": "(?<=>)", - "patterns": [ - { - "include": "text.html.basic" - } - ] - }, - { - "include": "#escape" - }, - { - "include": "#ampersand" - }, - { - "include": "#bracket" - }, - { - "include": "#raw" - }, - { - "include": "#bold" - }, - { - "include": "#image-inline" - }, - { - "include": "#link-inline" - }, - { - "include": "#link-inet" - }, - { - "include": "#link-email" - }, - { - "include": "#image-ref" - }, - { - "include": "#link-ref-literal" - }, - { - "include": "#link-ref" - }, - { - "include": "#link-ref-shortcut" - } - ] - }, - "link-email": { - "captures": { - "1": { - "name": "punctuation.definition.link.markdown" - }, - "2": { - "name": "markup.underline.link.markdown" - }, - "4": { - "name": "punctuation.definition.link.markdown" - } - }, - "match": "(<)((?:mailto:)?[-.\\w]+@[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]+)(>)", - "name": "meta.link.email.lt-gt.markdown" - }, - "link-inet": { - "captures": { - "1": { - "name": "punctuation.definition.link.markdown" - }, - "2": { - "name": "markup.underline.link.markdown" - }, - "3": { - "name": "punctuation.definition.link.markdown" - } - }, - "match": "(<)((?:https?|ftp)://.*?)(>)", - "name": "meta.link.inet.markdown" - }, - "link-inline": { - "captures": { - "1": { - "name": "punctuation.definition.string.begin.markdown" - }, - "2": { - "name": "string.other.link.title.markdown" - }, - "4": { - "name": "punctuation.definition.string.end.markdown" - }, - "5": { - "name": "punctuation.definition.metadata.markdown" - }, - "6": { - "name": "punctuation.definition.link.markdown" - }, - "7": { - "name": "markup.underline.link.markdown" - }, - "9": { - "name": "punctuation.definition.link.markdown" - }, - "10": { - "name": "string.other.link.description.title.markdown" - }, - "11": { - "name": "punctuation.definition.string.begin.markdown" - }, - "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 (<?)((?<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": { - "captures": { - "1": { - "name": "punctuation.definition.string.begin.markdown" - }, - "2": { - "name": "string.other.link.title.markdown" - }, - "4": { - "name": "punctuation.definition.string.end.markdown" - }, - "5": { - "name": "punctuation.definition.constant.begin.markdown" - }, - "6": { - "name": "constant.other.reference.link.markdown" - }, - "7": { - "name": "punctuation.definition.constant.end.markdown" - } - }, - "match": "(\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])(\\[)([^\\]]*+)(\\])", - "name": "meta.link.reference.markdown" - }, - "link-ref-literal": { - "captures": { - "1": { - "name": "punctuation.definition.string.begin.markdown" - }, - "2": { - "name": "string.other.link.title.markdown" - }, - "4": { - "name": "punctuation.definition.string.end.markdown" - }, - "5": { - "name": "punctuation.definition.constant.begin.markdown" - }, - "6": { - "name": "punctuation.definition.constant.end.markdown" - } - }, - "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" - }, - "3": { - "name": "punctuation.definition.string.end.markdown" - } - }, - "match": "(\\[)(\\S+?)(\\])", - "name": "meta.link.reference.markdown" - }, - "raw": { - "captures": { - "1": { - "name": "punctuation.definition.raw.markdown" - }, - "3": { - "name": "punctuation.definition.raw.markdown" - } - }, - "match": "(`+)([^`]|(?!(?<!`)\\1(?!`))`)*+(\\1)", - "name": "markup.inline.raw.string.markdown" + ] + }, + "ampersand": { + "comment": "Markdown will convert this for us. We match it so that the HTML grammar will not mark it up as invalid.", + "match": "&(?!([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+);)", + "name": "meta.other.valid-ampersand.markdown" + }, + "bold": { + "begin": "(?x) (\\*\\*(?=\\w)|(?<!\\w)\\*\\*|(?<!\\w)\\b__)(?=\\S) (?=\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" } - } + }, + "end": "(?<=\\S)(\\1)", + "name": "markup.bold.markdown", + "patterns": [ + { + "applyEndPatternLast": 1, + "begin": "(?=<[^>]*?>)", + "end": "(?<=>)", + "patterns": [ + { + "include": "text.html.basic" + } + ] + }, + { + "include": "#escape" + }, + { + "include": "#ampersand" + }, + { + "include": "#bracket" + }, + { + "include": "#raw" + }, + { + "include": "#bold" + }, + { + "include": "#italic" + }, + { + "include": "#image-inline" + }, + { + "include": "#link-inline" + }, + { + "include": "#link-inet" + }, + { + "include": "#link-email" + }, + { + "include": "#image-ref" + }, + { + "include": "#link-ref-literal" + }, + { + "include": "#link-ref" + }, + { + "include": "#link-ref-shortcut" + } + ] + }, + "bracket": { + "comment": "Markdown will convert this for us. We match it so that the HTML grammar will not mark it up as invalid.", + "match": "<(?![a-z/?\\$!])", + "name": "meta.other.valid-bracket.markdown" + }, + "escape": { + "match": "\\\\[-`*_#+.!(){}\\[\\]\\\\>]", + "name": "constant.character.escape.markdown" + }, + "image-inline": { + "captures": { + "1": { + "name": "punctuation.definition.string.begin.markdown" + }, + "2": { + "name": "string.other.link.description.markdown" + }, + "4": { + "name": "punctuation.definition.string.end.markdown" + }, + "5": { + "name": "punctuation.definition.metadata.markdown" + }, + "6": { + "name": "punctuation.definition.link.markdown" + }, + "7": { + "name": "markup.underline.link.image.markdown" + }, + "8": { + "name": "punctuation.definition.link.markdown" + }, + "9": { + "name": "string.other.link.description.title.markdown" + }, + "10": { + "name": "punctuation.definition.string.markdown" + }, + "11": { + "name": "punctuation.definition.string.markdown" + }, + "12": { + "name": "string.other.link.description.title.markdown" + }, + "13": { + "name": "punctuation.definition.string.markdown" + }, + "14": { + "name": "punctuation.definition.string.markdown" + }, + "15": { + "name": "punctuation.definition.metadata.markdown" + } + }, + "match": "(?x)\n (\\!\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (<?)(\\S+?)(>?) # 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.image.inline.markdown" + }, + "image-ref": { + "captures": { + "1": { + "name": "punctuation.definition.string.begin.markdown" + }, + "2": { + "name": "string.other.link.description.markdown" + }, + "4": { + "name": "punctuation.definition.string.begin.markdown" + }, + "5": { + "name": "punctuation.definition.constant.markdown" + }, + "6": { + "name": "constant.other.reference.link.markdown" + }, + "7": { + "name": "punctuation.definition.constant.markdown" + } + }, + "match": "(\\!\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])[ ]?(\\[)(.*?)(\\])", + "name": "meta.image.reference.markdown" + }, + "italic": { + "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" + } + }, + "end": "(?<=\\S)(\\1)((?!\\1)|(?=\\1\\1))", + "name": "markup.italic.markdown", + "patterns": [ + { + "applyEndPatternLast": 1, + "begin": "(?=<[^>]*?>)", + "end": "(?<=>)", + "patterns": [ + { + "include": "text.html.basic" + } + ] + }, + { + "include": "#escape" + }, + { + "include": "#ampersand" + }, + { + "include": "#bracket" + }, + { + "include": "#raw" + }, + { + "include": "#bold" + }, + { + "include": "#image-inline" + }, + { + "include": "#link-inline" + }, + { + "include": "#link-inet" + }, + { + "include": "#link-email" + }, + { + "include": "#image-ref" + }, + { + "include": "#link-ref-literal" + }, + { + "include": "#link-ref" + }, + { + "include": "#link-ref-shortcut" + } + ] + }, + "link-email": { + "captures": { + "1": { + "name": "punctuation.definition.link.markdown" + }, + "2": { + "name": "markup.underline.link.markdown" + }, + "4": { + "name": "punctuation.definition.link.markdown" + } + }, + "match": "(<)((?:mailto:)?[-.\\w]+@[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]+)(>)", + "name": "meta.link.email.lt-gt.markdown" + }, + "link-inet": { + "captures": { + "1": { + "name": "punctuation.definition.link.markdown" + }, + "2": { + "name": "markup.underline.link.markdown" + }, + "3": { + "name": "punctuation.definition.link.markdown" + } + }, + "match": "(<)((?:https?|ftp)://.*?)(>)", + "name": "meta.link.inet.markdown" + }, + "link-inline": { + "captures": { + "1": { + "name": "punctuation.definition.string.begin.markdown" + }, + "2": { + "name": "string.other.link.title.markdown" + }, + "4": { + "name": "punctuation.definition.string.end.markdown" + }, + "5": { + "name": "punctuation.definition.metadata.markdown" + }, + "6": { + "name": "punctuation.definition.link.markdown" + }, + "7": { + "name": "markup.underline.link.markdown" + }, + "9": { + "name": "punctuation.definition.link.markdown" + }, + "10": { + "name": "string.other.link.description.title.markdown" + }, + "11": { + "name": "punctuation.definition.string.begin.markdown" + }, + "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 (<?)((?<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": { + "captures": { + "1": { + "name": "punctuation.definition.string.begin.markdown" + }, + "2": { + "name": "string.other.link.title.markdown" + }, + "4": { + "name": "punctuation.definition.string.end.markdown" + }, + "5": { + "name": "punctuation.definition.constant.begin.markdown" + }, + "6": { + "name": "constant.other.reference.link.markdown" + }, + "7": { + "name": "punctuation.definition.constant.end.markdown" + } + }, + "match": "(\\[)((?<square>[^\\[\\]\\\\]|\\\\.|\\[\\g<square>*+\\])*+)(\\])(\\[)([^\\]]*+)(\\])", + "name": "meta.link.reference.markdown" + }, + "link-ref-literal": { + "captures": { + "1": { + "name": "punctuation.definition.string.begin.markdown" + }, + "2": { + "name": "string.other.link.title.markdown" + }, + "4": { + "name": "punctuation.definition.string.end.markdown" + }, + "5": { + "name": "punctuation.definition.constant.begin.markdown" + }, + "6": { + "name": "punctuation.definition.constant.end.markdown" + } + }, + "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" + }, + "3": { + "name": "punctuation.definition.string.end.markdown" + } + }, + "match": "(\\[)(\\S+?)(\\])", + "name": "meta.link.reference.markdown" + }, + "raw": { + "captures": { + "1": { + "name": "punctuation.definition.raw.markdown" + }, + "3": { + "name": "punctuation.definition.raw.markdown" + } + }, + "match": "(`+)([^`]|(?!(?<!`)\\1(?!`))`)*+(\\1)", + "name": "markup.inline.raw.string.markdown" } } } \ No newline at end of file diff --git a/extensions/markdown-language-features/cgmanifest.json b/extensions/markdown-language-features/cgmanifest.json index 89c68532e25..71df78ef41f 100644 --- a/extensions/markdown-language-features/cgmanifest.json +++ b/extensions/markdown-language-features/cgmanifest.json @@ -1,28 +1,5 @@ { "registrations": [ - { - "component": { - "type": "git", - "git": { - "name": "chriskempson/tomorrow-theme", - "repositoryUrl": "https://github.com/chriskempson/tomorrow-theme", - "commitHash": "0e0d35ac303f99b8aa182091ebeaee81cf2183a0" - } - }, - "licenseDetail": [ - "Copyright (C) 2013 Chris Kempson", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,", - "and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ], - "license": "MIT", - "version": "0.0.0" - }, { "component": { "type": "git", @@ -52,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 25df5db9f4e..8a4eea9541d 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -643,12 +643,12 @@ const messaging_1 = __webpack_require__(/*! ./messaging */ "./preview-src/messag const scroll_sync_1 = __webpack_require__(/*! ./scroll-sync */ "./preview-src/scroll-sync.ts"); const settings_1 = __webpack_require__(/*! ./settings */ "./preview-src/settings.ts"); const throttle = __webpack_require__(/*! lodash.throttle */ "./node_modules/lodash.throttle/index.js"); -var scrollDisabled = true; +let scrollDisabled = true; const marker = new activeLineMarker_1.ActiveLineMarker(); const settings = settings_1.getSettings(); const vscode = acquireVsCodeApi(); // Set VS Code state -const state = settings_1.getData('data-state'); +let state = settings_1.getData('data-state'); vscode.setState(state); const messaging = messaging_1.createPosterForVsCode(vscode); window.cspAlerter.setPoster(messaging); @@ -762,6 +762,8 @@ if (settings.scrollEditorWithPreview) { const line = scroll_sync_1.getEditorLineNumberForPageOffset(window.scrollY); if (typeof line === 'number' && !isNaN(line)) { messaging.postMessage('revealLine', { line }); + state.line = line; + vscode.setState(state); } } }, 50)); @@ -825,11 +827,13 @@ const getCodeLineElements = (() => { let elements; return () => { if (!elements) { - elements = ([{ element: document.body, line: 0 }]).concat(Array.prototype.map.call(document.getElementsByClassName('code-line'), (element) => { + elements = [{ element: document.body, line: 0 }]; + for (const element of document.getElementsByClassName('code-line')) { const line = +element.getAttribute('data-line'); - return { element, line }; - }) - .filter((x) => !isNaN(x.line))); + if (!isNaN(line)) { + elements.push({ element: element, line }); + } + } } return elements; }; @@ -908,7 +912,8 @@ function scrollToRevealSourceLine(line) { scrollTo = previousTop + betweenProgress * elementOffset; } else { - scrollTo = previousTop; + const progressInElement = line - Math.floor(line); + scrollTo = previousTop + (rect.height * progressInElement); } window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } @@ -978,4 +983,4 @@ exports.getSettings = getSettings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RDtJQUdDLDhCQUE4QixDQUFDLElBQVk7UUFDMUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLHNDQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsT0FBTyxDQUFDLE1BQStCO1FBQ3RDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLENBQUM7SUFFRCxvQkFBb0IsQ0FBQyxPQUFnQztRQUNwRCxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDZCxNQUFNLENBQUM7UUFDUixDQUFDO1FBQ0QsT0FBTyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBRUQsa0JBQWtCLENBQUMsT0FBZ0M7UUFDbEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ2QsTUFBTSxDQUFDO1FBQ1IsQ0FBQztRQUNELE9BQU8sQ0FBQyxTQUFTLElBQUksbUJBQW1CLENBQUM7SUFDMUMsQ0FBQztDQUNEO0FBM0JELDRDQTJCQzs7Ozs7Ozs7Ozs7Ozs7QUNqQ0Q7OztnR0FHZ0c7O0FBRWhHLDRCQUFtQyxDQUFhO0lBQy9DLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssZUFBZSxDQUFDLENBQUMsQ0FBQztRQUNsRixRQUFRLENBQUMsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxFQUFFLENBQUM7SUFDTCxDQUFDO0FBQ0YsQ0FBQztBQU5ELGdEQU1DOzs7Ozs7Ozs7Ozs7OztBQ1hEOzs7Z0dBR2dHOztBQUVoRyw4R0FBc0Q7QUFDdEQsZ0ZBQThDO0FBQzlDLHlGQUFvRDtBQUNwRCwrRkFBMkY7QUFDM0Ysc0ZBQWtEO0FBQ2xELHVHQUE2QztBQUk3QyxJQUFJLGNBQWMsR0FBRyxJQUFJLENBQUM7QUFDMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxtQ0FBZ0IsRUFBRSxDQUFDO0FBQ3RDLE1BQU0sUUFBUSxHQUFHLHNCQUFXLEVBQUUsQ0FBQztBQUUvQixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRWxDLG9CQUFvQjtBQUNwQixNQUFNLEtBQUssR0FBRyxrQkFBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkIsTUFBTSxTQUFTLEdBQUcsaUNBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFFaEQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtJQUNwQixnQkFBZ0IsRUFBRSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQUVGLDJCQUFrQixDQUFDLEdBQUcsRUFBRTtJQUN2QixFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbkMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixzQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxDQUFDO1FBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDMUIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7UUFDMUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUN0QixzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFUCxNQUFNLENBQUMsQ0FBQyxJQUFZLEVBQUUsUUFBYSxFQUFFLEVBQUU7UUFDdEMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQixDQUFDO0lBQ0YsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtJQUNwQyxNQUFNLFNBQVMsR0FBb0QsRUFBRSxDQUFDO0lBQ3RFLElBQUksTUFBTSxHQUFHLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ1osSUFBSSxDQUFDLENBQUM7UUFDTixHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDcEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXRCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdkMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDakMsQ0FBQztZQUVELFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO2FBQ2hCLENBQUMsQ0FBQztRQUNKLENBQUM7UUFFRCxTQUFTLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7QUFDRixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFUCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUN0QyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBQ3RCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMzQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLEtBQUssZ0NBQWdDO1lBQ3BDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELEtBQUssQ0FBQztRQUVQLEtBQUssWUFBWTtZQUNoQixZQUFZLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDeEMsS0FBSyxDQUFDO0lBQ1IsQ0FBQztBQUNGLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVWLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDN0MsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsMkJBQTJCLENBQUMsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCx5QkFBeUI7SUFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQXFCLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUMsVUFBeUIsRUFBRSxDQUFDO1FBQzFGLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxQixNQUFNLENBQUM7UUFDUixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsOENBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMvRCxDQUFDO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNaLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxJQUFJLElBQUksR0FBUSxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQzdCLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDYixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3ZELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0MsS0FBSyxDQUFDO1lBQ1AsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqRixNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDNUYsU0FBUyxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUN2QixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3hCLEtBQUssQ0FBQztZQUNQLENBQUM7WUFDRCxLQUFLLENBQUM7UUFDUCxDQUFDO1FBQ0QsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDeEIsQ0FBQztBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7SUFDdEMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9DLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7WUFDcEIsY0FBYyxHQUFHLEtBQUssQ0FBQztRQUN4QixDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDUCxNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDOUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLENBQUM7UUFDRixDQUFDO0lBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDVCxDQUFDOzs7Ozs7Ozs7Ozs7OztBQzdKRDs7O2dHQUdnRzs7QUFFaEcsc0ZBQXlDO0FBUzVCLDZCQUFxQixHQUFHLENBQUMsTUFBVyxFQUFFLEVBQUU7SUFDcEQsTUFBTSxDQUFDLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxlQUFlLEdBQVcsRUFBRSxHQUFXLEVBQUUsS0FBYTtJQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsbUJBQW1CLElBQVk7SUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQVcsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQVFELE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDakMsSUFBSSxRQUEyQixDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxHQUFHLEVBQUU7UUFDWCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDZixRQUFRLEdBQUcsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUNqRixRQUFRLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEVBQzVDLENBQUMsT0FBWSxFQUFFLEVBQUU7Z0JBQ2hCLE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDaEQsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1lBQzFCLENBQUMsQ0FBQztpQkFDRCxNQUFNLENBQUMsQ0FBQyxDQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkMsQ0FBQztRQUNELE1BQU0sQ0FBQyxRQUFRLENBQUM7SUFDakIsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMOzs7OztHQUtHO0FBQ0gsa0NBQXlDLFVBQWtCO0lBQzFELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDMUMsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sS0FBSyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDM0IsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQy9CLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQzdDLENBQUM7UUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQ3BDLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDbEMsQ0FBQztRQUNELFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDbEIsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQ3JCLENBQUM7QUFiRCw0REFhQztBQUVEOztHQUVHO0FBQ0gscUNBQTRDLE1BQWM7SUFDekQsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUN6QyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNaLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQzFCLE9BQU8sRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUNwQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMxRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksUUFBUSxDQUFDLENBQUMsQ0FBQztZQUM1QyxFQUFFLEdBQUcsR0FBRyxDQUFDO1FBQ1YsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUNWLENBQUM7SUFDRixDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUMzRCxFQUFFLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLFFBQVEsQ0FBQyxHQUFHLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUN4QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsTUFBTSxDQUFDLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDakQsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBdEJELGtFQXNCQztBQUVEOztHQUVHO0FBQ0gsa0NBQXlDLElBQVk7SUFDcEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxzQkFBVyxFQUFFLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxFQUFFLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNmLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMxRCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDZixNQUFNLENBQUM7SUFDUixDQUFDO0lBQ0QsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUN0RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO0lBQzdCLEVBQUUsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pDLDhEQUE4RDtRQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztRQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7SUFDMUQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsUUFBUSxHQUFHLFdBQVcsQ0FBQztJQUN4QixDQUFDO0lBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxPQUFPLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUN2RSxDQUFDO0FBMUJELDREQTBCQztBQUVELDBDQUFpRCxNQUFjO0lBQzlELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0QsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNkLE1BQU0sY0FBYyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUNoRSxNQUFNLGtCQUFrQixHQUFHLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLHVCQUF1QixHQUFHLGtCQUFrQixHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDckgsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyx1QkFBdUIsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25GLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsTUFBTSxxQkFBcUIsR0FBRyxrQkFBa0IsR0FBRyxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUMzRSxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO1lBQ25ELE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztJQUNGLENBQUM7SUFDRCxNQUFNLENBQUMsSUFBSSxDQUFDO0FBQ2IsQ0FBQztBQWpCRCw0RUFpQkM7Ozs7Ozs7Ozs7Ozs7O0FDdElEOzs7Z0dBR2dHOztBQVloRyxJQUFJLGNBQWMsR0FBZ0MsU0FBUyxDQUFDO0FBRTVELGlCQUF3QixHQUFXO0lBQ2xDLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN4RSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ2IsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ1YsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsQ0FBQztJQUNGLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBQ25ELENBQUM7QUFWRCwwQkFVQztBQUVEO0lBQ0MsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxjQUFjLEdBQUcsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7UUFDcEIsTUFBTSxDQUFDLGNBQWMsQ0FBQztJQUN2QixDQUFDO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFYRCxrQ0FXQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3ByZXZpZXctc3JjL2luZGV4LnRzXCIpO1xuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwidmFyIGc7XHJcblxyXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxyXG5nID0gKGZ1bmN0aW9uKCkge1xyXG5cdHJldHVybiB0aGlzO1xyXG59KSgpO1xyXG5cclxudHJ5IHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcclxuXHRnID0gZyB8fCBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCkgfHwgKDEsIGV2YWwpKFwidGhpc1wiKTtcclxufSBjYXRjaCAoZSkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXHJcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XHJcbn1cclxuXHJcbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cclxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3NcclxuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBnO1xyXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IGdldEVsZW1lbnRzRm9yU291cmNlTGluZSB9IGZyb20gJy4vc2Nyb2xsLXN5bmMnO1xuXG5leHBvcnQgY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG5cdHByaXZhdGUgX2N1cnJlbnQ6IGFueTtcblxuXHRvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24obGluZTogbnVtYmVyKSB7XG5cdFx0Y29uc3QgeyBwcmV2aW91cyB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRcdHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcblx0fVxuXG5cdF91cGRhdGUoYmVmb3JlOiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdHRoaXMuX3VubWFya0FjdGl2ZUVsZW1lbnQodGhpcy5fY3VycmVudCk7XG5cdFx0dGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcblx0XHR0aGlzLl9jdXJyZW50ID0gYmVmb3JlO1xuXHR9XG5cblx0X3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKC9cXGJjb2RlLWFjdGl2ZS1saW5lXFxiL2csICcnKTtcblx0fVxuXG5cdF9tYXJrQWN0aXZlRWxlbWVudChlbGVtZW50OiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdGlmICghZWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuXHR9XG59IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBmdW5jdGlvbiBvbmNlRG9jdW1lbnRMb2FkZWQoZjogKCkgPT4gdm9pZCkge1xuXHRpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmKTtcblx0fSBlbHNlIHtcblx0XHRmKCk7XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQWN0aXZlTGluZU1hcmtlciB9IGZyb20gJy4vYWN0aXZlTGluZU1hcmtlcic7XG5pbXBvcnQgeyBvbmNlRG9jdW1lbnRMb2FkZWQgfSBmcm9tICcuL2V2ZW50cyc7XG5pbXBvcnQgeyBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCwgc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lIH0gZnJvbSAnLi9zY3JvbGwtc3luYyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncywgZ2V0RGF0YSB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHRocm90dGxlID0gcmVxdWlyZSgnbG9kYXNoLnRocm90dGxlJyk7XG5cbmRlY2xhcmUgdmFyIGFjcXVpcmVWc0NvZGVBcGk6IGFueTtcblxudmFyIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbmNvbnN0IG1hcmtlciA9IG5ldyBBY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IGdldFNldHRpbmdzKCk7XG5cbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcblxuLy8gU2V0IFZTIENvZGUgc3RhdGVcbmNvbnN0IHN0YXRlID0gZ2V0RGF0YSgnZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LCA1MCkpO1xufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE1lc3NhZ2VQb3N0ZXIge1xuXHQvKipcblx0ICogUG9zdCBhIG1lc3NhZ2UgdG8gdGhlIG1hcmtkb3duIGV4dGVuc2lvblxuXHQgKi9cblx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkO1xufVxuXG5leHBvcnQgY29uc3QgY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZTogYW55KSA9PiB7XG5cdHJldHVybiBuZXcgY2xhc3MgaW1wbGVtZW50cyBNZXNzYWdlUG9zdGVyIHtcblx0XHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQge1xuXHRcdFx0dnNjb2RlLnBvc3RNZXNzYWdlKHtcblx0XHRcdFx0dHlwZSxcblx0XHRcdFx0c291cmNlOiBnZXRTZXR0aW5ncygpLnNvdXJjZSxcblx0XHRcdFx0Ym9keVxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9O1xufTtcblxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cblxuZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIG1heDogbnVtYmVyLCB2YWx1ZTogbnVtYmVyKSB7XG5cdHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cblxuZnVuY3Rpb24gY2xhbXBMaW5lKGxpbmU6IG51bWJlcikge1xuXHRyZXR1cm4gY2xhbXAoMCwgZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cblxuXG5leHBvcnQgaW50ZXJmYWNlIENvZGVMaW5lRWxlbWVudCB7XG5cdGVsZW1lbnQ6IEhUTUxFbGVtZW50O1xuXHRsaW5lOiBudW1iZXI7XG59XG5cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuXHRsZXQgZWxlbWVudHM6IENvZGVMaW5lRWxlbWVudFtdO1xuXHRyZXR1cm4gKCkgPT4ge1xuXHRcdGlmICghZWxlbWVudHMpIHtcblx0XHRcdGVsZW1lbnRzID0gKFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV0pLmNvbmNhdChBcnJheS5wcm90b3R5cGUubWFwLmNhbGwoXG5cdFx0XHRcdGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpLFxuXHRcdFx0XHQoZWxlbWVudDogYW55KSA9PiB7XG5cdFx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJyk7XG5cdFx0XHRcdFx0cmV0dXJuIHsgZWxlbWVudCwgbGluZSB9O1xuXHRcdFx0XHR9KVxuXHRcdFx0XHQuZmlsdGVyKCh4OiBhbnkpID0+ICFpc05hTih4LmxpbmUpKSk7XG5cdFx0fVxuXHRcdHJldHVybiBlbGVtZW50cztcblx0fTtcbn0pKCk7XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmU6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuXHRjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcblx0bGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuXHRcdGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG5cdFx0XHRyZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuXHRcdH0gZWxzZSBpZiAoZW50cnkubGluZSA+IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzLCBuZXh0OiBlbnRyeSB9O1xuXHRcdH1cblx0XHRwcmV2aW91cyA9IGVudHJ5O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzIH07XG59XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IGFyZSBhdCBhIHNwZWNpZmljIHBpeGVsIG9mZnNldCBvbiB0aGUgcGFnZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuXHRsZXQgbG8gPSAtMTtcblx0bGV0IGhpID0gbGluZXMubGVuZ3RoIC0gMTtcblx0d2hpbGUgKGxvICsgMSA8IGhpKSB7XG5cdFx0Y29uc3QgbWlkID0gTWF0aC5mbG9vcigobG8gKyBoaSkgLyAyKTtcblx0XHRjb25zdCBib3VuZHMgPSBsaW5lc1ttaWRdLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0aWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG5cdFx0XHRoaSA9IG1pZDtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRsbyA9IG1pZDtcblx0XHR9XG5cdH1cblx0Y29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuXHRjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRpZiAoaGkgPj0gMSAmJiBoaUJvdW5kcy50b3AgPiBwb3NpdGlvbikge1xuXHRcdGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcblx0XHRyZXR1cm4geyBwcmV2aW91czogbG9FbGVtZW50LCBuZXh0OiBoaUVsZW1lbnQgfTtcblx0fVxuXHRyZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lOiBudW1iZXIpIHtcblx0aWYgKCFnZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0aWYgKGxpbmUgPD0gMCkge1xuXHRcdHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIDApO1xuXHRcdHJldHVybjtcblx0fVxuXG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0aWYgKCFwcmV2aW91cykge1xuXHRcdHJldHVybjtcblx0fVxuXHRsZXQgc2Nyb2xsVG8gPSAwO1xuXHRjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0Y29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcblx0aWYgKG5leHQgJiYgbmV4dC5saW5lICE9PSBwcmV2aW91cy5saW5lKSB7XG5cdFx0Ly8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cblx0XHRjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdGNvbnN0IGVsZW1lbnRPZmZzZXQgPSBuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNUb3A7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG5cdH0gZWxzZSB7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcDtcblx0fVxuXHR3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcikge1xuXHRjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KTtcblx0aWYgKHByZXZpb3VzKSB7XG5cdFx0Y29uc3QgcHJldmlvdXNCb3VuZHMgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRcdGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG5cdFx0aWYgKG5leHQpIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyAqIChuZXh0LmxpbmUgLSBwcmV2aW91cy5saW5lKTtcblx0XHRcdHJldHVybiBjbGFtcExpbmUobGluZSk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0Y29uc3QgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50ID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKHByZXZpb3VzQm91bmRzLmhlaWdodCk7XG5cdFx0XHRjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzV2l0aGluRWxlbWVudDtcblx0XHRcdHJldHVybiBjbGFtcExpbmUobGluZSk7XG5cdFx0fVxuXHR9XG5cdHJldHVybiBudWxsO1xufVxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJldmlld1NldHRpbmdzIHtcblx0c291cmNlOiBzdHJpbmc7XG5cdGxpbmU6IG51bWJlcjtcblx0bGluZUNvdW50OiBudW1iZXI7XG5cdHNjcm9sbFByZXZpZXdXaXRoRWRpdG9yPzogYm9vbGVhbjtcblx0c2Nyb2xsRWRpdG9yV2l0aFByZXZpZXc6IGJvb2xlYW47XG5cdGRpc2FibGVTZWN1cml0eVdhcm5pbmdzOiBib29sZWFuO1xuXHRkb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3I6IGJvb2xlYW47XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YShrZXk6IHN0cmluZyk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RCxNQUFhLGdCQUFnQjtJQUc1Qiw4QkFBOEIsQ0FBQyxJQUFZO1FBQzFDLE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRCxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsSUFBSSxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVELE9BQU8sQ0FBQyxNQUErQjtRQUN0QyxJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3pDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNoQyxJQUFJLENBQUMsUUFBUSxHQUFHLE1BQU0sQ0FBQztJQUN4QixDQUFDO0lBRUQsb0JBQW9CLENBQUMsT0FBZ0M7UUFDcEQsSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNiLE9BQU87U0FDUDtRQUNELE9BQU8sQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsdUJBQXVCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUVELGtCQUFrQixDQUFDLE9BQWdDO1FBQ2xELElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDYixPQUFPO1NBQ1A7UUFDRCxPQUFPLENBQUMsU0FBUyxJQUFJLG1CQUFtQixDQUFDO0lBQzFDLENBQUM7Q0FDRDtBQTNCRCw0Q0EyQkM7Ozs7Ozs7Ozs7Ozs7O0FDakNEOzs7Z0dBR2dHOztBQUVoRyxTQUFnQixrQkFBa0IsQ0FBQyxDQUFhO0lBQy9DLElBQUksUUFBUSxDQUFDLFVBQVUsS0FBSyxTQUFTLElBQUksUUFBUSxDQUFDLFVBQW9CLEtBQUssZUFBZSxFQUFFO1FBQzNGLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLENBQUMsQ0FBQztLQUNqRDtTQUFNO1FBQ04sQ0FBQyxFQUFFLENBQUM7S0FDSjtBQUNGLENBQUM7QUFORCxnREFNQzs7Ozs7Ozs7Ozs7Ozs7QUNYRDs7O2dHQUdnRzs7QUFFaEcsOEdBQXNEO0FBQ3RELGdGQUE4QztBQUM5Qyx5RkFBb0Q7QUFDcEQsK0ZBQTJGO0FBQzNGLHNGQUFrRDtBQUNsRCx1R0FBNkM7QUFJN0MsSUFBSSxjQUFjLEdBQUcsSUFBSSxDQUFDO0FBQzFCLE1BQU0sTUFBTSxHQUFHLElBQUksbUNBQWdCLEVBQUUsQ0FBQztBQUN0QyxNQUFNLFFBQVEsR0FBRyxzQkFBVyxFQUFFLENBQUM7QUFFL0IsTUFBTSxNQUFNLEdBQUcsZ0JBQWdCLEVBQUUsQ0FBQztBQUVsQyxvQkFBb0I7QUFDcEIsSUFBSSxLQUFLLEdBQUcsa0JBQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUNsQyxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBRXZCLE1BQU0sU0FBUyxHQUFHLGlDQUFxQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBRWhELE1BQU0sQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBQ3ZDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7QUFFaEQsTUFBTSxDQUFDLE1BQU0sR0FBRyxHQUFHLEVBQUU7SUFDcEIsZ0JBQWdCLEVBQUUsQ0FBQztBQUNwQixDQUFDLENBQUM7QUFFRiwyQkFBa0IsQ0FBQyxHQUFHLEVBQUU7SUFDdkIsSUFBSSxRQUFRLENBQUMsdUJBQXVCLEVBQUU7UUFDckMsVUFBVSxDQUFDLEdBQUcsRUFBRTtZQUNmLE1BQU0sV0FBVyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQztZQUNuQyxJQUFJLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxFQUFFO2dCQUN4QixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixzQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQzthQUN0QztRQUNGLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztLQUNOO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxNQUFNLFlBQVksR0FBRyxDQUFDLEdBQUcsRUFBRTtJQUMxQixNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsQ0FBQyxJQUFZLEVBQUUsRUFBRTtRQUMxQyxjQUFjLEdBQUcsSUFBSSxDQUFDO1FBQ3RCLHNDQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUVQLE9BQU8sQ0FBQyxJQUFZLEVBQUUsUUFBYSxFQUFFLEVBQUU7UUFDdEMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRTtZQUNqQixRQUFRLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztZQUNyQixRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDZjtJQUNGLENBQUMsQ0FBQztBQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7QUFFTCxJQUFJLGdCQUFnQixHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUU7SUFDcEMsTUFBTSxTQUFTLEdBQW9ELEVBQUUsQ0FBQztJQUN0RSxJQUFJLE1BQU0sR0FBRyxRQUFRLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDbEQsSUFBSSxNQUFNLEVBQUU7UUFDWCxJQUFJLENBQUMsQ0FBQztRQUNOLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNuQyxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFFdEIsSUFBSSxHQUFHLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsRUFBRTtnQkFDdEMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7YUFDaEM7WUFFRCxTQUFTLENBQUMsSUFBSSxDQUFDO2dCQUNkLEVBQUUsRUFBRSxHQUFHLENBQUMsRUFBRTtnQkFDVixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU07Z0JBQ2xCLEtBQUssRUFBRSxHQUFHLENBQUMsS0FBSzthQUNoQixDQUFDLENBQUM7U0FDSDtRQUVELFNBQVMsQ0FBQyxXQUFXLENBQUMsaUJBQWlCLEVBQUUsU0FBUyxDQUFDLENBQUM7S0FDcEQ7QUFDRixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFUCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUN0QyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBQ3RCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLFFBQVEsQ0FBQyxNQUFNLEVBQUU7UUFDMUMsT0FBTztLQUNQO0lBRUQsUUFBUSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRTtRQUN4QixLQUFLLGdDQUFnQztZQUNwQyxNQUFNLENBQUMsOEJBQThCLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUN2RCxNQUFNO1FBRVAsS0FBSyxZQUFZO1lBQ2hCLFlBQVksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQztZQUN4QyxNQUFNO0tBQ1A7QUFDRixDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7QUFFVixRQUFRLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzdDLElBQUksQ0FBQyxRQUFRLENBQUMsMkJBQTJCLEVBQUU7UUFDMUMsT0FBTztLQUNQO0lBRUQseUJBQXlCO0lBQ3pCLEtBQUssSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQXFCLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUMsVUFBeUIsRUFBRTtRQUN6RixJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxFQUFFO1lBQ3pCLE9BQU87U0FDUDtLQUNEO0lBRUQsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQztJQUMzQixNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN0RCxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRTtRQUM3QyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztLQUM5RDtBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsUUFBUSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxJQUFJLENBQUMsS0FBSyxFQUFFO1FBQ1gsT0FBTztLQUNQO0lBRUQsSUFBSSxJQUFJLEdBQVEsS0FBSyxDQUFDLE1BQU0sQ0FBQztJQUM3QixPQUFPLElBQUksRUFBRTtRQUNaLElBQUksSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsT0FBTyxLQUFLLEdBQUcsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFO1lBQ3RELElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUU7Z0JBQzlDLE1BQU07YUFDTjtZQUNELElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsa0JBQWtCLENBQUMsRUFBRTtnQkFDaEYsTUFBTSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQ0FBZ0MsRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQzVGLFNBQVMsQ0FBQyxXQUFXLENBQUMsV0FBVyxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7Z0JBQ3ZELEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDdkIsS0FBSyxDQUFDLGVBQWUsRUFBRSxDQUFDO2dCQUN4QixNQUFNO2FBQ047WUFDRCxNQUFNO1NBQ047UUFDRCxJQUFJLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztLQUN2QjtBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULElBQUksUUFBUSxDQUFDLHVCQUF1QixFQUFFO0lBQ3JDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDLEdBQUcsRUFBRTtRQUMvQyxJQUFJLGNBQWMsRUFBRTtZQUNuQixjQUFjLEdBQUcsS0FBSyxDQUFDO1NBQ3ZCO2FBQU07WUFDTixNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQzdDLFNBQVMsQ0FBQyxXQUFXLENBQUMsWUFBWSxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDOUMsS0FBSyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7Z0JBQ2xCLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7YUFDdkI7U0FDRDtJQUNGLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0NBQ1I7Ozs7Ozs7Ozs7Ozs7O0FDL0pEOzs7Z0dBR2dHOztBQUVoRyxzRkFBeUM7QUFTNUIsNkJBQXFCLEdBQUcsQ0FBQyxNQUFXLEVBQUUsRUFBRTtJQUNwRCxPQUFPLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxTQUFTLEtBQUssQ0FBQyxHQUFXLEVBQUUsR0FBVyxFQUFFLEtBQWE7SUFDckQsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxJQUFZO0lBQzlCLE9BQU8sS0FBSyxDQUFDLENBQUMsRUFBRSxzQkFBVyxFQUFFLENBQUMsU0FBUyxHQUFHLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUNwRCxDQUFDO0FBUUQsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLEdBQUcsRUFBRTtJQUNqQyxJQUFJLFFBQTJCLENBQUM7SUFDaEMsT0FBTyxHQUFHLEVBQUU7UUFDWCxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQ2QsUUFBUSxHQUFHLENBQUMsRUFBRSxPQUFPLEVBQUUsUUFBUSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUNqRCxLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsQ0FBQyxzQkFBc0IsQ0FBQyxXQUFXLENBQUMsRUFBRTtnQkFDbkUsTUFBTSxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLFdBQVcsQ0FBRSxDQUFDO2dCQUNqRCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFO29CQUNqQixRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLE9BQXNCLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztpQkFDekQ7YUFDRDtTQUNEO1FBQ0QsT0FBTyxRQUFRLENBQUM7SUFDakIsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMOzs7OztHQUtHO0FBQ0gsU0FBZ0Isd0JBQXdCLENBQUMsVUFBa0I7SUFDMUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUMxQyxNQUFNLEtBQUssR0FBRyxtQkFBbUIsRUFBRSxDQUFDO0lBQ3BDLElBQUksUUFBUSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUM7SUFDaEMsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLEVBQUU7UUFDMUIsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLFVBQVUsRUFBRTtZQUM5QixPQUFPLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7U0FDNUM7YUFBTSxJQUFJLEtBQUssQ0FBQyxJQUFJLEdBQUcsVUFBVSxFQUFFO1lBQ25DLE9BQU8sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDO1NBQ2pDO1FBQ0QsUUFBUSxHQUFHLEtBQUssQ0FBQztLQUNqQjtJQUNELE9BQU8sRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUNyQixDQUFDO0FBYkQsNERBYUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLDJCQUEyQixDQUFDLE1BQWM7SUFDekQsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUN6QyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNaLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQzFCLE9BQU8sRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUU7UUFDbkIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUN0QyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDMUQsSUFBSSxNQUFNLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksUUFBUSxFQUFFO1lBQzNDLEVBQUUsR0FBRyxHQUFHLENBQUM7U0FDVDthQUNJO1lBQ0osRUFBRSxHQUFHLEdBQUcsQ0FBQztTQUNUO0tBQ0Q7SUFDRCxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDNUIsTUFBTSxRQUFRLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO0lBQzNELElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxRQUFRLENBQUMsR0FBRyxHQUFHLFFBQVEsRUFBRTtRQUN2QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO0tBQ2hEO0lBQ0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBdEJELGtFQXNCQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0Isd0JBQXdCLENBQUMsSUFBWTtJQUNwRCxJQUFJLENBQUMsc0JBQVcsRUFBRSxDQUFDLHVCQUF1QixFQUFFO1FBQzNDLE9BQU87S0FDUDtJQUVELElBQUksSUFBSSxJQUFJLENBQUMsRUFBRTtRQUNkLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqQyxPQUFPO0tBQ1A7SUFFRCxNQUFNLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxHQUFHLHdCQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUU7UUFDZCxPQUFPO0tBQ1A7SUFDRCxJQUFJLFFBQVEsR0FBRyxDQUFDLENBQUM7SUFDakIsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO0lBQ3RELE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7SUFDN0IsSUFBSSxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRLENBQUMsSUFBSSxFQUFFO1FBQ3hDLDhEQUE4RDtRQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztRQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7S0FDekQ7U0FBTTtRQUNOLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEQsUUFBUSxHQUFHLFdBQVcsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsaUJBQWlCLENBQUMsQ0FBQztLQUMzRDtJQUNELE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsT0FBTyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUM7QUFDdkUsQ0FBQztBQTNCRCw0REEyQkM7QUFFRCxTQUFnQixnQ0FBZ0MsQ0FBQyxNQUFjO0lBQzlELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0QsSUFBSSxRQUFRLEVBQUU7UUFDYixNQUFNLGNBQWMsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDaEUsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsT0FBTyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMxRSxJQUFJLElBQUksRUFBRTtZQUNULE1BQU0sdUJBQXVCLEdBQUcsa0JBQWtCLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNySCxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHVCQUF1QixHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDbkYsT0FBTyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDdkI7YUFDSTtZQUNKLE1BQU0scUJBQXFCLEdBQUcsa0JBQWtCLEdBQUcsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDM0UsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyxxQkFBcUIsQ0FBQztZQUNuRCxPQUFPLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN2QjtLQUNEO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDYixDQUFDO0FBakJELDRFQWlCQzs7Ozs7Ozs7Ozs7Ozs7QUN2SUQ7OztnR0FHZ0c7O0FBWWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsU0FBZ0IsT0FBTyxDQUFDLEdBQVc7SUFDbEMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLElBQUksT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxJQUFJLElBQUksRUFBRTtZQUNULE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0MiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9pbmRleC50c1wiKTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pbXBvcnQgeyBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcblxuZXhwb3J0IGNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuXHRwcml2YXRlIF9jdXJyZW50OiBhbnk7XG5cblx0b25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmU6IG51bWJlcikge1xuXHRcdGNvbnN0IHsgcHJldmlvdXMgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0XHR0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG5cdH1cblxuXHRfdXBkYXRlKGJlZm9yZTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHR0aGlzLl91bm1hcmtBY3RpdmVFbGVtZW50KHRoaXMuX2N1cnJlbnQpO1xuXHRcdHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG5cdFx0dGhpcy5fY3VycmVudCA9IGJlZm9yZTtcblx0fVxuXG5cdF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lID0gZWxlbWVudC5jbGFzc05hbWUucmVwbGFjZSgvXFxiY29kZS1hY3RpdmUtbGluZVxcYi9nLCAnJyk7XG5cdH1cblxuXHRfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGY6ICgpID0+IHZvaWQpIHtcblx0aWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlIGFzIHN0cmluZyA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxubGV0IHN0YXRlID0gZ2V0RGF0YSgnZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHRcdHN0YXRlLmxpbmUgPSBsaW5lO1xuXHRcdFx0XHR2c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSwgNTApKTtcbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuZXhwb3J0IGludGVyZmFjZSBNZXNzYWdlUG9zdGVyIHtcblx0LyoqXG5cdCAqIFBvc3QgYSBtZXNzYWdlIHRvIHRoZSBtYXJrZG93biBleHRlbnNpb25cblx0ICovXG5cdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZDtcbn1cblxuZXhwb3J0IGNvbnN0IGNyZWF0ZVBvc3RlckZvclZzQ29kZSA9ICh2c2NvZGU6IGFueSkgPT4ge1xuXHRyZXR1cm4gbmV3IGNsYXNzIGltcGxlbWVudHMgTWVzc2FnZVBvc3RlciB7XG5cdFx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkIHtcblx0XHRcdHZzY29kZS5wb3N0TWVzc2FnZSh7XG5cdFx0XHRcdHR5cGUsXG5cdFx0XHRcdHNvdXJjZTogZ2V0U2V0dGluZ3MoKS5zb3VyY2UsXG5cdFx0XHRcdGJvZHlcblx0XHRcdH0pO1xuXHRcdH1cblx0fTtcbn07XG5cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5cbmZ1bmN0aW9uIGNsYW1wKG1pbjogbnVtYmVyLCBtYXg6IG51bWJlciwgdmFsdWU6IG51bWJlcikge1xuXHRyZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lOiBudW1iZXIpIHtcblx0cmV0dXJuIGNsYW1wKDAsIGdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5cblxuZXhwb3J0IGludGVyZmFjZSBDb2RlTGluZUVsZW1lbnQge1xuXHRlbGVtZW50OiBIVE1MRWxlbWVudDtcblx0bGluZTogbnVtYmVyO1xufVxuXG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcblx0bGV0IGVsZW1lbnRzOiBDb2RlTGluZUVsZW1lbnRbXTtcblx0cmV0dXJuICgpID0+IHtcblx0XHRpZiAoIWVsZW1lbnRzKSB7XG5cdFx0XHRlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG5cdFx0XHRmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS1saW5lJykpIHtcblx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJykhO1xuXHRcdFx0XHRpZiAoIWlzTmFOKGxpbmUpKSB7XG5cdFx0XHRcdFx0ZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQgYXMgSFRNTEVsZW1lbnQsIGxpbmUgfSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGVsZW1lbnRzO1xuXHR9O1xufSkoKTtcblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZTogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRsZXQgcHJldmlvdXMgPSBsaW5lc1swXSB8fCBudWxsO1xuXHRmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG5cdFx0aWYgKGVudHJ5LmxpbmUgPT09IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzOiBlbnRyeSwgbmV4dDogdW5kZWZpbmVkIH07XG5cdFx0fSBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRpZiAoIWdldFNldHRpbmdzKCkuc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRpZiAobGluZSA8PSAwKSB7XG5cdFx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRpZiAoIXByZXZpb3VzKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGxldCBzY3JvbGxUbyA9IDA7XG5cdGNvbnN0IHJlY3QgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcblx0XHQvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuXHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0Y29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcblx0fSBlbHNlIHtcblx0XHRjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuXHRcdHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG5cdH1cblx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgTWF0aC5tYXgoMSwgd2luZG93LnNjcm9sbFkgKyBzY3JvbGxUbykpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0OiBudW1iZXIpIHtcblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmIChwcmV2aW91cykge1xuXHRcdGNvbnN0IHByZXZpb3VzQm91bmRzID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBvZmZzZXRGcm9tUHJldmlvdXMgPSAob2Zmc2V0IC0gd2luZG93LnNjcm9sbFkgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gbnVsbDtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgaW50ZXJmYWNlIFByZXZpZXdTZXR0aW5ncyB7XG5cdHNvdXJjZTogc3RyaW5nO1xuXHRsaW5lOiBudW1iZXI7XG5cdGxpbmVDb3VudDogbnVtYmVyO1xuXHRzY3JvbGxQcmV2aWV3V2l0aEVkaXRvcj86IGJvb2xlYW47XG5cdHNjcm9sbEVkaXRvcldpdGhQcmV2aWV3OiBib29sZWFuO1xuXHRkaXNhYmxlU2VjdXJpdHlXYXJuaW5nczogYm9vbGVhbjtcblx0ZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yOiBib29sZWFuO1xufVxuXG5sZXQgY2FjaGVkU2V0dGluZ3M6IFByZXZpZXdTZXR0aW5ncyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldERhdGEoa2V5OiBzdHJpbmcpOiBQcmV2aWV3U2V0dGluZ3Mge1xuXHRjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcblx0aWYgKGVsZW1lbnQpIHtcblx0XHRjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcblx0XHRpZiAoZGF0YSkge1xuXHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG5cdFx0fVxuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgbG9hZCBkYXRhIGZvciAke2tleX1gKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNldHRpbmdzKCk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBsb2FkIHNldHRpbmdzJyk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index e78f73d9666..12145e43a66 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ body { - font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; font-size: 14px; padding: 0 26px; line-height: 22px; diff --git a/extensions/markdown-language-features/media/pre.js b/extensions/markdown-language-features/media/pre.js index bb0bd24f548..f0d83635dd0 100644 --- a/extensions/markdown-language-features/media/pre.js +++ b/extensions/markdown-language-features/media/pre.js @@ -277,4 +277,4 @@ exports.getStrings = getStrings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvY3NwLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3NldHRpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3N0cmluZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0EseURBQWlELGNBQWM7QUFDL0Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsbUNBQTJCLDBCQUEwQixFQUFFO0FBQ3ZELHlDQUFpQyxlQUFlO0FBQ2hEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDhEQUFzRCwrREFBK0Q7O0FBRXJIO0FBQ0E7OztBQUdBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FDbkVBOzs7Z0dBR2dHOztBQUdoRyxzRkFBeUM7QUFDekMsbUZBQXVDO0FBRXZDOztHQUVHO0FBQ0g7SUFNQztRQUxRLFlBQU8sR0FBRyxLQUFLLENBQUM7UUFDaEIsc0JBQWlCLEdBQUcsS0FBSyxDQUFDO1FBS2pDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyx5QkFBeUIsRUFBRSxHQUFHLEVBQUU7WUFDekQsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ3JCLENBQUMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFO1lBQzVDLEVBQUUsQ0FBQyxDQUFDLEtBQUssSUFBSSxLQUFLLENBQUMsSUFBSSxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxLQUFLLHNCQUFzQixDQUFDLENBQUMsQ0FBQztnQkFDdkUsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3JCLENBQUM7UUFDRixDQUFDLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFTSxTQUFTLENBQUMsTUFBcUI7UUFDckMsSUFBSSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUM7UUFDeEIsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQztZQUM1QixJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDdkIsQ0FBQztJQUNGLENBQUM7SUFFTyxZQUFZO1FBQ25CLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLENBQUM7UUFDOUIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO0lBQ3ZCLENBQUM7SUFFTyxjQUFjO1FBQ3JCLE1BQU0sT0FBTyxHQUFHLG9CQUFVLEVBQUUsQ0FBQztRQUM3QixNQUFNLFFBQVEsR0FBRyxzQkFBVyxFQUFFLENBQUM7UUFFL0IsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxRQUFRLENBQUMsdUJBQXVCLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztZQUN6RSxNQUFNLENBQUM7UUFDUixDQUFDO1FBQ0QsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFFcEIsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNqRCxZQUFZLENBQUMsU0FBUyxHQUFHLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQztRQUNyRCxZQUFZLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3BELFlBQVksQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBRWpFLFlBQVksQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzVDLFlBQVksQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQ3RFLFlBQVksQ0FBQyxPQUFPLEdBQUcsR0FBRyxFQUFFO1lBQzNCLElBQUksQ0FBQyxTQUFVLENBQUMsV0FBVyxDQUFDLDZCQUE2QixFQUFFLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3pGLENBQUMsQ0FBQztRQUNGLFFBQVEsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ3pDLENBQUM7Q0FDRDtBQW5ERCxnQ0FtREM7Ozs7Ozs7Ozs7Ozs7OztBQ3pERDtJQU1DO1FBTFEsbUJBQWMsR0FBYSxFQUFFLENBQUM7UUFDOUIsb0JBQWUsR0FBWSxLQUFLLENBQUM7UUFLeEMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLEtBQVUsRUFBRSxFQUFFO1lBQ3ZDLE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztZQUMzQyxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNsQyxDQUFDLENBQUM7UUFFRixNQUFNLENBQUMsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUUsR0FBRyxFQUFFO1lBQ2hELEdBQUcsQ0FBQyxDQUFDLE1BQU0sSUFBSSxJQUFJLFFBQVEsQ0FBQyxzQkFBc0IsQ0FBQyxpQkFBaUIsQ0FBa0MsQ0FBQyxDQUFDLENBQUM7Z0JBQ3hHLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztvQkFDekIsSUFBSSxDQUFDLE9BQU8sR0FBRyxnQkFBZ0IsQ0FBQztnQkFDakMsQ0FBQztZQUNGLENBQUM7UUFDRixDQUFDLENBQUMsQ0FBQztRQUVILE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFO1lBQ3BDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUNqQyxNQUFNLENBQUM7WUFDUixDQUFDO1lBQ0QsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUM7WUFDNUIsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQ2pCLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLHVCQUF1QixFQUFFLEVBQUUsY0FBYyxFQUFFLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1lBQzNGLENBQUM7UUFDRixDQUFDLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFTSxTQUFTLENBQUMsTUFBcUI7UUFDckMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUM7WUFDMUIsTUFBTSxDQUFDLFdBQVcsQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLGNBQWMsRUFBRSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUMsQ0FBQztRQUN0RixDQUFDO0lBQ0YsQ0FBQztDQUNEO0FBckNELGtEQXFDQzs7Ozs7Ozs7Ozs7Ozs7QUMzQ0Q7OztnR0FHZ0c7O0FBRWhHLHVFQUFtQztBQUNuQyxtRkFBZ0Q7QUFTaEQsTUFBTSxDQUFDLFVBQVUsR0FBRyxJQUFJLGdCQUFVLEVBQUUsQ0FBQztBQUNyQyxNQUFNLENBQUMsbUJBQW1CLEdBQUcsSUFBSSw2QkFBbUIsRUFBRSxDQUFDOzs7Ozs7Ozs7Ozs7OztBQ2hCdkQ7OztnR0FHZ0c7O0FBWWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsaUJBQXdCLEdBQVc7SUFDbEMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDYixNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3ZDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMsMkJBQTJCLEdBQUcsRUFBRSxDQUFDLENBQUM7QUFDbkQsQ0FBQztBQVZELDBCQVVDO0FBRUQ7SUFDQyxFQUFFLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxjQUFjLENBQUM7SUFDdkIsQ0FBQztJQUVELGNBQWMsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDMUMsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLHlCQUF5QixDQUFDLENBQUM7QUFDNUMsQ0FBQztBQVhELGtDQVdDOzs7Ozs7Ozs7Ozs7OztBQ3hDRDs7O2dHQUdnRzs7QUFFaEc7SUFDQyxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsY0FBYyxDQUFDLDhCQUE4QixDQUFDLENBQUM7SUFDdEUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNYLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDaEQsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUNWLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3pCLENBQUM7SUFDRixDQUFDO0lBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO0FBQzNDLENBQUM7QUFURCxnQ0FTQyIsImZpbGUiOiJwcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9wcmUudHNcIik7XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgTWVzc2FnZVBvc3RlciB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgeyBnZXRTdHJpbmdzIH0gZnJvbSAnLi9zdHJpbmdzJztcblxuLyoqXG4gKiBTaG93cyBhbiBhbGVydCB3aGVuIHRoZXJlIGlzIGEgY29udGVudCBzZWN1cml0eSBwb2xpY3kgdmlvbGF0aW9uLlxuICovXG5leHBvcnQgY2xhc3MgQ3NwQWxlcnRlciB7XG5cdHByaXZhdGUgZGlkU2hvdyA9IGZhbHNlO1xuXHRwcml2YXRlIGRpZEhhdmVDc3BXYXJuaW5nID0gZmFsc2U7XG5cblx0cHJpdmF0ZSBtZXNzYWdpbmc/OiBNZXNzYWdlUG9zdGVyO1xuXG5cdGNvbnN0cnVjdG9yKCkge1xuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3NlY3VyaXR5cG9saWN5dmlvbGF0aW9uJywgKCkgPT4ge1xuXHRcdFx0dGhpcy5vbkNzcFdhcm5pbmcoKTtcblx0XHR9KTtcblxuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgKGV2ZW50KSA9PiB7XG5cdFx0XHRpZiAoZXZlbnQgJiYgZXZlbnQuZGF0YSAmJiBldmVudC5kYXRhLm5hbWUgPT09ICd2c2NvZGUtZGlkLWJsb2NrLXN2ZycpIHtcblx0XHRcdFx0dGhpcy5vbkNzcFdhcm5pbmcoKTtcblx0XHRcdH1cblx0XHR9KTtcblx0fVxuXG5cdHB1YmxpYyBzZXRQb3N0ZXIocG9zdGVyOiBNZXNzYWdlUG9zdGVyKSB7XG5cdFx0dGhpcy5tZXNzYWdpbmcgPSBwb3N0ZXI7XG5cdFx0aWYgKHRoaXMuZGlkSGF2ZUNzcFdhcm5pbmcpIHtcblx0XHRcdHRoaXMuc2hvd0NzcFdhcm5pbmcoKTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIG9uQ3NwV2FybmluZygpIHtcblx0XHR0aGlzLmRpZEhhdmVDc3BXYXJuaW5nID0gdHJ1ZTtcblx0XHR0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG5cdH1cblxuXHRwcml2YXRlIHNob3dDc3BXYXJuaW5nKCkge1xuXHRcdGNvbnN0IHN0cmluZ3MgPSBnZXRTdHJpbmdzKCk7XG5cdFx0Y29uc3Qgc2V0dGluZ3MgPSBnZXRTZXR0aW5ncygpO1xuXG5cdFx0aWYgKHRoaXMuZGlkU2hvdyB8fCBzZXR0aW5ncy5kaXNhYmxlU2VjdXJpdHlXYXJuaW5ncyB8fCAhdGhpcy5tZXNzYWdpbmcpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0dGhpcy5kaWRTaG93ID0gdHJ1ZTtcblxuXHRcdGNvbnN0IG5vdGlmaWNhdGlvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcblx0XHRub3RpZmljYXRpb24uaW5uZXJUZXh0ID0gc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VUZXh0O1xuXHRcdG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ2lkJywgJ2NvZGUtY3NwLXdhcm5pbmcnKTtcblx0XHRub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCd0aXRsZScsIHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGl0bGUpO1xuXG5cdFx0bm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgncm9sZScsICdidXR0b24nKTtcblx0XHRub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdhcmlhLWxhYmVsJywgc3RyaW5ncy5jc3BBbGVydE1lc3NhZ2VMYWJlbCk7XG5cdFx0bm90aWZpY2F0aW9uLm9uY2xpY2sgPSAoKSA9PiB7XG5cdFx0XHR0aGlzLm1lc3NhZ2luZyEucG9zdE1lc3NhZ2UoJ3Nob3dQcmV2aWV3U2VjdXJpdHlTZWxlY3RvcicsIHsgc291cmNlOiBzZXR0aW5ncy5zb3VyY2UgfSk7XG5cdFx0fTtcblx0XHRkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKG5vdGlmaWNhdGlvbik7XG5cdH1cbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuaW1wb3J0IHsgTWVzc2FnZVBvc3RlciB9IGZyb20gJy4vbWVzc2FnaW5nJztcblxuZXhwb3J0IGNsYXNzIFN0eWxlTG9hZGluZ01vbml0b3Ige1xuXHRwcml2YXRlIHVubG9hZGVkU3R5bGVzOiBzdHJpbmdbXSA9IFtdO1xuXHRwcml2YXRlIGZpbmlzaGVkTG9hZGluZzogYm9vbGVhbiA9IGZhbHNlO1xuXG5cdHByaXZhdGUgcG9zdGVyPzogTWVzc2FnZVBvc3RlcjtcblxuXHRjb25zdHJ1Y3RvcigpIHtcblx0XHRjb25zdCBvblN0eWxlTG9hZEVycm9yID0gKGV2ZW50OiBhbnkpID0+IHtcblx0XHRcdGNvbnN0IHNvdXJjZSA9IGV2ZW50LnRhcmdldC5kYXRhc2V0LnNvdXJjZTtcblx0XHRcdHRoaXMudW5sb2FkZWRTdHlsZXMucHVzaChzb3VyY2UpO1xuXHRcdH07XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsICgpID0+IHtcblx0XHRcdGZvciAoY29uc3QgbGluayBvZiBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdjb2RlLXVzZXItc3R5bGUnKSBhcyBIVE1MQ29sbGVjdGlvbk9mPEhUTUxFbGVtZW50Pikge1xuXHRcdFx0XHRpZiAobGluay5kYXRhc2V0LnNvdXJjZSkge1xuXHRcdFx0XHRcdGxpbmsub25lcnJvciA9IG9uU3R5bGVMb2FkRXJyb3I7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9KTtcblxuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdsb2FkJywgKCkgPT4ge1xuXHRcdFx0aWYgKCF0aGlzLnVubG9hZGVkU3R5bGVzLmxlbmd0aCkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cdFx0XHR0aGlzLmZpbmlzaGVkTG9hZGluZyA9IHRydWU7XG5cdFx0XHRpZiAodGhpcy5wb3N0ZXIpIHtcblx0XHRcdFx0dGhpcy5wb3N0ZXIucG9zdE1lc3NhZ2UoJ3ByZXZpZXdTdHlsZUxvYWRFcnJvcicsIHsgdW5sb2FkZWRTdHlsZXM6IHRoaXMudW5sb2FkZWRTdHlsZXMgfSk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cblxuXHRwdWJsaWMgc2V0UG9zdGVyKHBvc3RlcjogTWVzc2FnZVBvc3Rlcik6IHZvaWQge1xuXHRcdHRoaXMucG9zdGVyID0gcG9zdGVyO1xuXHRcdGlmICh0aGlzLmZpbmlzaGVkTG9hZGluZykge1xuXHRcdFx0cG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuXHRcdH1cblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBDc3BBbGVydGVyIH0gZnJvbSAnLi9jc3AnO1xuaW1wb3J0IHsgU3R5bGVMb2FkaW5nTW9uaXRvciB9IGZyb20gJy4vbG9hZGluZyc7XG5cbmRlY2xhcmUgZ2xvYmFsIHtcblx0aW50ZXJmYWNlIFdpbmRvdyB7XG5cdFx0Y3NwQWxlcnRlcjogQ3NwQWxlcnRlcjtcblx0XHRzdHlsZUxvYWRpbmdNb25pdG9yOiBTdHlsZUxvYWRpbmdNb25pdG9yO1xuXHR9XG59XG5cbndpbmRvdy5jc3BBbGVydGVyID0gbmV3IENzcEFsZXJ0ZXIoKTtcbndpbmRvdy5zdHlsZUxvYWRpbmdNb25pdG9yID0gbmV3IFN0eWxlTG9hZGluZ01vbml0b3IoKTsiLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuZXhwb3J0IGludGVyZmFjZSBQcmV2aWV3U2V0dGluZ3Mge1xuXHRzb3VyY2U6IHN0cmluZztcblx0bGluZTogbnVtYmVyO1xuXHRsaW5lQ291bnQ6IG51bWJlcjtcblx0c2Nyb2xsUHJldmlld1dpdGhFZGl0b3I/OiBib29sZWFuO1xuXHRzY3JvbGxFZGl0b3JXaXRoUHJldmlldzogYm9vbGVhbjtcblx0ZGlzYWJsZVNlY3VyaXR5V2FybmluZ3M6IGJvb2xlYW47XG5cdGRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcjogYm9vbGVhbjtcbn1cblxubGV0IGNhY2hlZFNldHRpbmdzOiBQcmV2aWV3U2V0dGluZ3MgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREYXRhKGtleTogc3RyaW5nKTogUHJldmlld1NldHRpbmdzIHtcblx0Y29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG5cdGlmIChlbGVtZW50KSB7XG5cdFx0Y29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG5cdFx0aWYgKGRhdGEpIHtcblx0XHRcdHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuXHRcdH1cblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTZXR0aW5ncygpOiBQcmV2aWV3U2V0dGluZ3Mge1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHRjYWNoZWRTZXR0aW5ncyA9IGdldERhdGEoJ2RhdGEtc2V0dGluZ3MnKTtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTdHJpbmdzKCk6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH0ge1xuXHRjb25zdCBzdG9yZSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG5cdGlmIChzdG9yZSkge1xuXHRcdGNvbnN0IGRhdGEgPSBzdG9yZS5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RyaW5ncycpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblx0dGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzdHJpbmdzJyk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvY3NwLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2xvYWRpbmcudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvcHJlLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3NldHRpbmdzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL3N0cmluZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0EseURBQWlELGNBQWM7QUFDL0Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsbUNBQTJCLDBCQUEwQixFQUFFO0FBQ3ZELHlDQUFpQyxlQUFlO0FBQ2hEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDhEQUFzRCwrREFBK0Q7O0FBRXJIO0FBQ0E7OztBQUdBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7O0FDbkVBOzs7Z0dBR2dHOztBQUdoRyxzRkFBeUM7QUFDekMsbUZBQXVDO0FBRXZDOztHQUVHO0FBQ0gsTUFBYSxVQUFVO0lBTXRCO1FBTFEsWUFBTyxHQUFHLEtBQUssQ0FBQztRQUNoQixzQkFBaUIsR0FBRyxLQUFLLENBQUM7UUFLakMsUUFBUSxDQUFDLGdCQUFnQixDQUFDLHlCQUF5QixFQUFFLEdBQUcsRUFBRTtZQUN6RCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDckIsQ0FBQyxDQUFDLENBQUM7UUFFSCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDNUMsSUFBSSxLQUFLLElBQUksS0FBSyxDQUFDLElBQUksSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksS0FBSyxzQkFBc0IsRUFBRTtnQkFDdEUsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO2FBQ3BCO1FBQ0YsQ0FBQyxDQUFDLENBQUM7SUFDSixDQUFDO0lBRU0sU0FBUyxDQUFDLE1BQXFCO1FBQ3JDLElBQUksQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDO1FBQ3hCLElBQUksSUFBSSxDQUFDLGlCQUFpQixFQUFFO1lBQzNCLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztTQUN0QjtJQUNGLENBQUM7SUFFTyxZQUFZO1FBQ25CLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLENBQUM7UUFDOUIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO0lBQ3ZCLENBQUM7SUFFTyxjQUFjO1FBQ3JCLE1BQU0sT0FBTyxHQUFHLG9CQUFVLEVBQUUsQ0FBQztRQUM3QixNQUFNLFFBQVEsR0FBRyxzQkFBVyxFQUFFLENBQUM7UUFFL0IsSUFBSSxJQUFJLENBQUMsT0FBTyxJQUFJLFFBQVEsQ0FBQyx1QkFBdUIsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUU7WUFDeEUsT0FBTztTQUNQO1FBQ0QsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFFcEIsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNqRCxZQUFZLENBQUMsU0FBUyxHQUFHLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQztRQUNyRCxZQUFZLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ3BELFlBQVksQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBRWpFLFlBQVksQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1FBQzVDLFlBQVksQ0FBQyxZQUFZLENBQUMsWUFBWSxFQUFFLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQ3RFLFlBQVksQ0FBQyxPQUFPLEdBQUcsR0FBRyxFQUFFO1lBQzNCLElBQUksQ0FBQyxTQUFVLENBQUMsV0FBVyxDQUFDLDZCQUE2QixFQUFFLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQ3pGLENBQUMsQ0FBQztRQUNGLFFBQVEsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ3pDLENBQUM7Q0FDRDtBQW5ERCxnQ0FtREM7Ozs7Ozs7Ozs7Ozs7OztBQ3pERCxNQUFhLG1CQUFtQjtJQU0vQjtRQUxRLG1CQUFjLEdBQWEsRUFBRSxDQUFDO1FBQzlCLG9CQUFlLEdBQVksS0FBSyxDQUFDO1FBS3hDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxLQUFVLEVBQUUsRUFBRTtZQUN2QyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7WUFDM0MsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDbEMsQ0FBQyxDQUFDO1FBRUYsTUFBTSxDQUFDLGdCQUFnQixDQUFDLGtCQUFrQixFQUFFLEdBQUcsRUFBRTtZQUNoRCxLQUFLLE1BQU0sSUFBSSxJQUFJLFFBQVEsQ0FBQyxzQkFBc0IsQ0FBQyxpQkFBaUIsQ0FBa0MsRUFBRTtnQkFDdkcsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRTtvQkFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxnQkFBZ0IsQ0FBQztpQkFDaEM7YUFDRDtRQUNGLENBQUMsQ0FBQyxDQUFDO1FBRUgsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUU7WUFDcEMsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFO2dCQUNoQyxPQUFPO2FBQ1A7WUFDRCxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztZQUM1QixJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUU7Z0JBQ2hCLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLHVCQUF1QixFQUFFLEVBQUUsY0FBYyxFQUFFLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO2FBQzFGO1FBQ0YsQ0FBQyxDQUFDLENBQUM7SUFDSixDQUFDO0lBRU0sU0FBUyxDQUFDLE1BQXFCO1FBQ3JDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRTtZQUN6QixNQUFNLENBQUMsV0FBVyxDQUFDLHVCQUF1QixFQUFFLEVBQUUsY0FBYyxFQUFFLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1NBQ3JGO0lBQ0YsQ0FBQztDQUNEO0FBckNELGtEQXFDQzs7Ozs7Ozs7Ozs7Ozs7QUMzQ0Q7OztnR0FHZ0c7O0FBRWhHLHVFQUFtQztBQUNuQyxtRkFBZ0Q7QUFTaEQsTUFBTSxDQUFDLFVBQVUsR0FBRyxJQUFJLGdCQUFVLEVBQUUsQ0FBQztBQUNyQyxNQUFNLENBQUMsbUJBQW1CLEdBQUcsSUFBSSw2QkFBbUIsRUFBRSxDQUFDOzs7Ozs7Ozs7Ozs7OztBQ2hCdkQ7OztnR0FHZ0c7O0FBWWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsU0FBZ0IsT0FBTyxDQUFDLEdBQVc7SUFDbEMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLElBQUksT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxJQUFJLElBQUksRUFBRTtZQUNULE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0M7Ozs7Ozs7Ozs7Ozs7O0FDeENEOzs7Z0dBR2dHOztBQUVoRyxTQUFnQixVQUFVO0lBQ3pCLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN0RSxJQUFJLEtBQUssRUFBRTtRQUNWLE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDaEQsSUFBSSxJQUFJLEVBQUU7WUFDVCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDeEI7S0FDRDtJQUNELE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztBQUMzQyxDQUFDO0FBVEQsZ0NBU0MiLCJmaWxlIjoicHJlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7XG4gXHRcdFx0XHRjb25maWd1cmFibGU6IGZhbHNlLFxuIFx0XHRcdFx0ZW51bWVyYWJsZTogdHJ1ZSxcbiBcdFx0XHRcdGdldDogZ2V0dGVyXG4gXHRcdFx0fSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbiBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0RGVmYXVsdCgpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcbiBcdFx0XHRmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4gXHRcdHJldHVybiBnZXR0ZXI7XG4gXHR9O1xuXG4gXHQvLyBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGxcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubyA9IGZ1bmN0aW9uKG9iamVjdCwgcHJvcGVydHkpIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIHByb3BlcnR5KTsgfTtcblxuIFx0Ly8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbiBcdF9fd2VicGFja19yZXF1aXJlX18ucCA9IFwiXCI7XG5cblxuIFx0Ly8gTG9hZCBlbnRyeSBtb2R1bGUgYW5kIHJldHVybiBleHBvcnRzXG4gXHRyZXR1cm4gX193ZWJwYWNrX3JlcXVpcmVfXyhfX3dlYnBhY2tfcmVxdWlyZV9fLnMgPSBcIi4vcHJldmlldy1zcmMvcHJlLnRzXCIpO1xuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IE1lc3NhZ2VQb3N0ZXIgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHsgZ2V0U3RyaW5ncyB9IGZyb20gJy4vc3RyaW5ncyc7XG5cbi8qKlxuICogU2hvd3MgYW4gYWxlcnQgd2hlbiB0aGVyZSBpcyBhIGNvbnRlbnQgc2VjdXJpdHkgcG9saWN5IHZpb2xhdGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIENzcEFsZXJ0ZXIge1xuXHRwcml2YXRlIGRpZFNob3cgPSBmYWxzZTtcblx0cHJpdmF0ZSBkaWRIYXZlQ3NwV2FybmluZyA9IGZhbHNlO1xuXG5cdHByaXZhdGUgbWVzc2FnaW5nPzogTWVzc2FnZVBvc3RlcjtcblxuXHRjb25zdHJ1Y3RvcigpIHtcblx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdzZWN1cml0eXBvbGljeXZpb2xhdGlvbicsICgpID0+IHtcblx0XHRcdHRoaXMub25Dc3BXYXJuaW5nKCk7XG5cdFx0fSk7XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIChldmVudCkgPT4ge1xuXHRcdFx0aWYgKGV2ZW50ICYmIGV2ZW50LmRhdGEgJiYgZXZlbnQuZGF0YS5uYW1lID09PSAndnNjb2RlLWRpZC1ibG9jay1zdmcnKSB7XG5cdFx0XHRcdHRoaXMub25Dc3BXYXJuaW5nKCk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cblxuXHRwdWJsaWMgc2V0UG9zdGVyKHBvc3RlcjogTWVzc2FnZVBvc3Rlcikge1xuXHRcdHRoaXMubWVzc2FnaW5nID0gcG9zdGVyO1xuXHRcdGlmICh0aGlzLmRpZEhhdmVDc3BXYXJuaW5nKSB7XG5cdFx0XHR0aGlzLnNob3dDc3BXYXJuaW5nKCk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBvbkNzcFdhcm5pbmcoKSB7XG5cdFx0dGhpcy5kaWRIYXZlQ3NwV2FybmluZyA9IHRydWU7XG5cdFx0dGhpcy5zaG93Q3NwV2FybmluZygpO1xuXHR9XG5cblx0cHJpdmF0ZSBzaG93Q3NwV2FybmluZygpIHtcblx0XHRjb25zdCBzdHJpbmdzID0gZ2V0U3RyaW5ncygpO1xuXHRcdGNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuXHRcdGlmICh0aGlzLmRpZFNob3cgfHwgc2V0dGluZ3MuZGlzYWJsZVNlY3VyaXR5V2FybmluZ3MgfHwgIXRoaXMubWVzc2FnaW5nKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMuZGlkU2hvdyA9IHRydWU7XG5cblx0XHRjb25zdCBub3RpZmljYXRpb24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG5cdFx0bm90aWZpY2F0aW9uLmlubmVyVGV4dCA9IHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlVGV4dDtcblx0XHRub3RpZmljYXRpb24uc2V0QXR0cmlidXRlKCdpZCcsICdjb2RlLWNzcC13YXJuaW5nJyk7XG5cdFx0bm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgndGl0bGUnLCBzdHJpbmdzLmNzcEFsZXJ0TWVzc2FnZVRpdGxlKTtcblxuXHRcdG5vdGlmaWNhdGlvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG5cdFx0bm90aWZpY2F0aW9uLnNldEF0dHJpYnV0ZSgnYXJpYS1sYWJlbCcsIHN0cmluZ3MuY3NwQWxlcnRNZXNzYWdlTGFiZWwpO1xuXHRcdG5vdGlmaWNhdGlvbi5vbmNsaWNrID0gKCkgPT4ge1xuXHRcdFx0dGhpcy5tZXNzYWdpbmchLnBvc3RNZXNzYWdlKCdzaG93UHJldmlld1NlY3VyaXR5U2VsZWN0b3InLCB7IHNvdXJjZTogc2V0dGluZ3Muc291cmNlIH0pO1xuXHRcdH07XG5cdFx0ZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChub3RpZmljYXRpb24pO1xuXHR9XG59XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IE1lc3NhZ2VQb3N0ZXIgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5cbmV4cG9ydCBjbGFzcyBTdHlsZUxvYWRpbmdNb25pdG9yIHtcblx0cHJpdmF0ZSB1bmxvYWRlZFN0eWxlczogc3RyaW5nW10gPSBbXTtcblx0cHJpdmF0ZSBmaW5pc2hlZExvYWRpbmc6IGJvb2xlYW4gPSBmYWxzZTtcblxuXHRwcml2YXRlIHBvc3Rlcj86IE1lc3NhZ2VQb3N0ZXI7XG5cblx0Y29uc3RydWN0b3IoKSB7XG5cdFx0Y29uc3Qgb25TdHlsZUxvYWRFcnJvciA9IChldmVudDogYW55KSA9PiB7XG5cdFx0XHRjb25zdCBzb3VyY2UgPSBldmVudC50YXJnZXQuZGF0YXNldC5zb3VyY2U7XG5cdFx0XHR0aGlzLnVubG9hZGVkU3R5bGVzLnB1c2goc291cmNlKTtcblx0XHR9O1xuXG5cdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7XG5cdFx0XHRmb3IgKGNvbnN0IGxpbmsgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS11c2VyLXN0eWxlJykgYXMgSFRNTENvbGxlY3Rpb25PZjxIVE1MRWxlbWVudD4pIHtcblx0XHRcdFx0aWYgKGxpbmsuZGF0YXNldC5zb3VyY2UpIHtcblx0XHRcdFx0XHRsaW5rLm9uZXJyb3IgPSBvblN0eWxlTG9hZEVycm9yO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSk7XG5cblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbG9hZCcsICgpID0+IHtcblx0XHRcdGlmICghdGhpcy51bmxvYWRlZFN0eWxlcy5sZW5ndGgpIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5maW5pc2hlZExvYWRpbmcgPSB0cnVlO1xuXHRcdFx0aWYgKHRoaXMucG9zdGVyKSB7XG5cdFx0XHRcdHRoaXMucG9zdGVyLnBvc3RNZXNzYWdlKCdwcmV2aWV3U3R5bGVMb2FkRXJyb3InLCB7IHVubG9hZGVkU3R5bGVzOiB0aGlzLnVubG9hZGVkU3R5bGVzIH0pO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9XG5cblx0cHVibGljIHNldFBvc3Rlcihwb3N0ZXI6IE1lc3NhZ2VQb3N0ZXIpOiB2b2lkIHtcblx0XHR0aGlzLnBvc3RlciA9IHBvc3Rlcjtcblx0XHRpZiAodGhpcy5maW5pc2hlZExvYWRpbmcpIHtcblx0XHRcdHBvc3Rlci5wb3N0TWVzc2FnZSgncHJldmlld1N0eWxlTG9hZEVycm9yJywgeyB1bmxvYWRlZFN0eWxlczogdGhpcy51bmxvYWRlZFN0eWxlcyB9KTtcblx0XHR9XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQ3NwQWxlcnRlciB9IGZyb20gJy4vY3NwJztcbmltcG9ydCB7IFN0eWxlTG9hZGluZ01vbml0b3IgfSBmcm9tICcuL2xvYWRpbmcnO1xuXG5kZWNsYXJlIGdsb2JhbCB7XG5cdGludGVyZmFjZSBXaW5kb3cge1xuXHRcdGNzcEFsZXJ0ZXI6IENzcEFsZXJ0ZXI7XG5cdFx0c3R5bGVMb2FkaW5nTW9uaXRvcjogU3R5bGVMb2FkaW5nTW9uaXRvcjtcblx0fVxufVxuXG53aW5kb3cuY3NwQWxlcnRlciA9IG5ldyBDc3BBbGVydGVyKCk7XG53aW5kb3cuc3R5bGVMb2FkaW5nTW9uaXRvciA9IG5ldyBTdHlsZUxvYWRpbmdNb25pdG9yKCk7IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJldmlld1NldHRpbmdzIHtcblx0c291cmNlOiBzdHJpbmc7XG5cdGxpbmU6IG51bWJlcjtcblx0bGluZUNvdW50OiBudW1iZXI7XG5cdHNjcm9sbFByZXZpZXdXaXRoRWRpdG9yPzogYm9vbGVhbjtcblx0c2Nyb2xsRWRpdG9yV2l0aFByZXZpZXc6IGJvb2xlYW47XG5cdGRpc2FibGVTZWN1cml0eVdhcm5pbmdzOiBib29sZWFuO1xuXHRkb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3I6IGJvb2xlYW47XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YShrZXk6IHN0cmluZyk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U3RyaW5ncygpOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9IHtcblx0Y29uc3Qgc3RvcmUgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoc3RvcmUpIHtcblx0XHRjb25zdCBkYXRhID0gc3RvcmUuZ2V0QXR0cmlidXRlKCdkYXRhLXN0cmluZ3MnKTtcblx0XHRpZiAoZGF0YSkge1xuXHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG5cdFx0fVxuXHR9XG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc3RyaW5ncycpO1xufVxuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 54b289a7d23..10431185034 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -153,6 +153,14 @@ { "command": "markdown.preview.toggleLock", "when": "markdownPreviewFocus" + }, + { + "command": "markdown.preview.refresh", + "when": "editorLangId == markdown" + }, + { + "command": "markdown.preview.refresh", + "when": "markdownPreviewFocus" } ] }, @@ -181,16 +189,6 @@ "description": "%markdown.styles.dec%", "scope": "resource" }, - "markdown.previewFrontMatter": { - "type": "string", - "enum": [ - "hide", - "show" - ], - "default": "hide", - "description": "%markdown.previewFrontMatter.dec%", - "scope": "resource" - }, "markdown.preview.breaks": { "type": "boolean", "default": false, @@ -205,7 +203,7 @@ }, "markdown.preview.fontFamily": { "type": "string", - "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'HelveticaNeue-Light', 'Ubuntu', 'Droid Sans', sans-serif", + "default": "-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', 'Ubuntu', 'Droid Sans', sans-serif", "description": "%markdown.preview.fontFamily.desc%", "scope": "resource" }, @@ -305,23 +303,24 @@ "build-preview": "webpack --mode development" }, "dependencies": { - "highlight.js": "9.12.0", - "markdown-it": "^8.4.1", - "vscode-extension-telemetry": "0.1.0", + "highlight.js": "9.13.1", + "markdown-it": "^8.4.2", + "markdown-it-front-matter": "^0.1.2", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/highlight.js": "9.1.10", + "@types/highlight.js": "9.12.3", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "0.0.2", - "@types/node": "^8.10.25", + "@types/node": "^10.12.21", "lodash.throttle": "^4.1.1", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "ts-loader": "^4.0.1", - "typescript": "^2.7.2", + "typescript": "^3.3.1", "vscode": "^1.1.10", "webpack": "^4.1.0", "webpack-cli": "^2.0.10" } -} +} \ No newline at end of file diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index ca3d7226f58..7841caff810 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -13,7 +13,6 @@ "markdown.preview.scrollPreviewWithEditorSelection.desc": "[Deprecated] Scrolls the markdown preview to reveal the currently selected line from the editor.", "markdown.preview.scrollPreviewWithEditorSelection.deprecationMessage": "This setting has been replaced by 'markdown.preview.scrollPreviewWithEditor' and no longer has any effect.", "markdown.preview.title": "Open Preview", - "markdown.previewFrontMatter.dec": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content.", "markdown.previewSide.title": "Open Preview to the Side", "markdown.showLockedPreviewToSide.title": "Open Locked Preview to the Side", "markdown.showSource.title": "Show Source", diff --git a/extensions/markdown-language-features/preview-src/events.ts b/extensions/markdown-language-features/preview-src/events.ts index 40833aca711..81ce5c61640 100644 --- a/extensions/markdown-language-features/preview-src/events.ts +++ b/extensions/markdown-language-features/preview-src/events.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ export function onceDocumentLoaded(f: () => void) { - if (document.readyState === 'loading' || document.readyState === 'uninitialized') { + if (document.readyState === 'loading' || document.readyState as string === 'uninitialized') { document.addEventListener('DOMContentLoaded', f); } else { f(); diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 676d53be757..567b915bc53 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -12,14 +12,14 @@ import throttle = require('lodash.throttle'); declare var acquireVsCodeApi: any; -var scrollDisabled = true; +let scrollDisabled = true; const marker = new ActiveLineMarker(); const settings = getSettings(); const vscode = acquireVsCodeApi(); // Set VS Code state -const state = getData('data-state'); +let state = getData('data-state'); vscode.setState(state); const messaging = createPosterForVsCode(vscode); @@ -152,6 +152,8 @@ if (settings.scrollEditorWithPreview) { const line = getEditorLineNumberForPageOffset(window.scrollY); if (typeof line === 'number' && !isNaN(line)) { messaging.postMessage('revealLine', { line }); + state.line = line; + vscode.setState(state); } } }, 50)); diff --git a/extensions/markdown-language-features/preview-src/scroll-sync.ts b/extensions/markdown-language-features/preview-src/scroll-sync.ts index 3bd2939e029..4fe8987f6cd 100644 --- a/extensions/markdown-language-features/preview-src/scroll-sync.ts +++ b/extensions/markdown-language-features/preview-src/scroll-sync.ts @@ -24,13 +24,13 @@ const getCodeLineElements = (() => { let elements: CodeLineElement[]; return () => { if (!elements) { - elements = ([{ element: document.body, line: 0 }]).concat(Array.prototype.map.call( - document.getElementsByClassName('code-line'), - (element: any) => { - const line = +element.getAttribute('data-line'); - return { element, line }; - }) - .filter((x: any) => !isNaN(x.line))); + elements = [{ element: document.body, line: 0 }]; + for (const element of document.getElementsByClassName('code-line')) { + const line = +element.getAttribute('data-line')!; + if (!isNaN(line)) { + elements.push({ element: element as HTMLElement, line }); + } + } } return elements; }; @@ -110,7 +110,8 @@ export function scrollToRevealSourceLine(line: number) { const elementOffset = next.element.getBoundingClientRect().top - previousTop; scrollTo = previousTop + betweenProgress * elementOffset; } else { - scrollTo = previousTop; + const progressInElement = line - Math.floor(line); + scrollTo = previousTop + (rect.height * progressInElement); } window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } diff --git a/extensions/markdown-language-features/preview-src/tsconfig.json b/extensions/markdown-language-features/preview-src/tsconfig.json index 9684d1ec2d9..8a1e8a03fb8 100644 --- a/extensions/markdown-language-features/preview-src/tsconfig.json +++ b/extensions/markdown-language-features/preview-src/tsconfig.json @@ -6,6 +6,7 @@ "jsx": "react", "sourceMap": true, "strict": true, + "strictBindCallApply": true, "noImplicitAny": true, "noUnusedLocals": true } diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index eed32d6efb0..ea929f484f6 100644 --- a/extensions/markdown-language-features/src/commands/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/commands/openDocumentLink.ts @@ -25,7 +25,7 @@ export class OpenDocumentLinkCommand implements Command { path: string, fragment: string ): vscode.Uri { - return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path, fragment }))}`); + return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path: encodeURIComponent(path), fragment }))}`); } public constructor( @@ -39,9 +39,9 @@ export class OpenDocumentLinkCommand implements Command { return this.tryOpen(p + '.md', args); } const resource = vscode.Uri.file(p); - return Promise.resolve(void 0) + return Promise.resolve(undefined) .then(() => vscode.commands.executeCommand('vscode.open', resource)) - .then(() => void 0); + .then(() => undefined); }); } diff --git a/extensions/markdown-language-features/src/commands/refreshPreview.ts b/extensions/markdown-language-features/src/commands/refreshPreview.ts index fb1bb829960..6fbeed98191 100644 --- a/extensions/markdown-language-features/src/commands/refreshPreview.ts +++ b/extensions/markdown-language-features/src/commands/refreshPreview.ts @@ -5,15 +5,18 @@ import { Command } from '../commandManager'; import { MarkdownPreviewManager } from '../features/previewManager'; +import { MarkdownEngine } from '../markdownEngine'; export class RefreshPreviewCommand implements Command { public readonly id = 'markdown.preview.refresh'; public constructor( - private readonly webviewManager: MarkdownPreviewManager + private readonly webviewManager: MarkdownPreviewManager, + private readonly engine: MarkdownEngine ) { } public execute() { + this.engine.cleanCache(); this.webviewManager.refresh(); } } \ No newline at end of file diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 812d61af9d5..3fe2c9d4cd3 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -15,8 +15,8 @@ import MarkdownWorkspaceSymbolProvider from './features/workspaceSymbolProvider' import { Logger } from './logger'; import { MarkdownEngine } from './markdownEngine'; import { getMarkdownExtensionContributions } from './markdownExtensions'; -import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security'; -import { loadDefaultTelemetryReporter } from './telemetryReporter'; +import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector, ContentSecurityPolicyArbiter } from './security'; +import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; import { githubSlugifier } from './slugify'; @@ -25,42 +25,64 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(telemetryReporter); const contributions = getMarkdownExtensionContributions(context); + context.subscriptions.push(contributions); const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState, context.workspaceState); const engine = new MarkdownEngine(contributions, githubSlugifier); const logger = new Logger(); - const selector: vscode.DocumentSelector = [ - { language: 'markdown', scheme: 'file' }, - { language: 'markdown', scheme: 'untitled' } - ]; - const contentProvider = new MarkdownContentProvider(engine, context, cspArbiter, contributions, logger); const symbolProvider = new MDDocumentSymbolProvider(engine); const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions); context.subscriptions.push(previewManager); - context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider)); - context.subscriptions.push(vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider())); - context.subscriptions.push(vscode.languages.registerFoldingRangeProvider(selector, new MarkdownFoldingProvider(engine))); - context.subscriptions.push(vscode.languages.registerWorkspaceSymbolProvider(new MarkdownWorkspaceSymbolProvider(symbolProvider))); - - const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager); - - const commandManager = new CommandManager(); - context.subscriptions.push(commandManager); - commandManager.register(new commands.ShowPreviewCommand(previewManager, telemetryReporter)); - commandManager.register(new commands.ShowPreviewToSideCommand(previewManager, telemetryReporter)); - commandManager.register(new commands.ShowLockedPreviewToSideCommand(previewManager, telemetryReporter)); - commandManager.register(new commands.ShowSourceCommand(previewManager)); - commandManager.register(new commands.RefreshPreviewCommand(previewManager)); - commandManager.register(new commands.MoveCursorToPositionCommand()); - commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector, previewManager)); - commandManager.register(new commands.OpenDocumentLinkCommand(engine)); - commandManager.register(new commands.ToggleLockCommand(previewManager)); + context.subscriptions.push(registerMarkdownLanguageFeatures(symbolProvider, engine)); + context.subscriptions.push(registerMarkdownCommands(previewManager, telemetryReporter, cspArbiter, engine)); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { logger.updateConfiguration(); previewManager.updateConfiguration(); })); } + +function registerMarkdownLanguageFeatures( + symbolProvider: MDDocumentSymbolProvider, + engine: MarkdownEngine +): vscode.Disposable { + const selector: vscode.DocumentSelector = [ + { language: 'markdown', scheme: 'file' }, + { language: 'markdown', scheme: 'untitled' } + ]; + + return vscode.Disposable.from( + vscode.languages.setLanguageConfiguration('markdown', { + wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number})+', 'ug'), + }), + vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), + vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()), + vscode.languages.registerFoldingRangeProvider(selector, new MarkdownFoldingProvider(engine)), + vscode.languages.registerWorkspaceSymbolProvider(new MarkdownWorkspaceSymbolProvider(symbolProvider)) + ); +} + +function registerMarkdownCommands( + previewManager: MarkdownPreviewManager, + telemetryReporter: TelemetryReporter, + cspArbiter: ContentSecurityPolicyArbiter, + engine: MarkdownEngine +): vscode.Disposable { + const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager); + + const commandManager = new CommandManager(); + commandManager.register(new commands.ShowPreviewCommand(previewManager, telemetryReporter)); + commandManager.register(new commands.ShowPreviewToSideCommand(previewManager, telemetryReporter)); + commandManager.register(new commands.ShowLockedPreviewToSideCommand(previewManager, telemetryReporter)); + commandManager.register(new commands.ShowSourceCommand(previewManager)); + commandManager.register(new commands.RefreshPreviewCommand(previewManager, engine)); + commandManager.register(new commands.MoveCursorToPositionCommand()); + commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector, previewManager)); + commandManager.register(new commands.OpenDocumentLinkCommand(engine)); + commandManager.register(new commands.ToggleLockCommand(previewManager)); + return commandManager; +} + diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index f4e1bd0a9d2..39190f78a53 100644 --- a/extensions/markdown-language-features/src/features/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/features/documentLinkProvider.ts @@ -50,8 +50,27 @@ function matchAll( return out; } +function extractDocumentLink( + document: vscode.TextDocument, + base: string, + pre: number, + link: string, + matchIndex: number | undefined +): vscode.DocumentLink | undefined { + const offset = (matchIndex || 0) + pre; + const linkStart = document.positionAt(offset); + const linkEnd = document.positionAt(offset + link.length); + try { + return new vscode.DocumentLink( + new vscode.Range(linkStart, linkEnd), + normalizeLink(document, link, base)); + } catch (e) { + return undefined; + } +} + export default class LinkProvider implements vscode.DocumentLinkProvider { - private readonly linkPattern = /(\[[^\]]*\]\(\s*)((([^\s\(\)]|\(\S*?\))+))\s*(".*?")?\)/g; + private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g; private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g; private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm; @@ -62,8 +81,10 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { const base = document.uri.scheme === 'file' ? path.dirname(document.uri.fsPath) : ''; const text = document.getText(); - return this.providerInlineLinks(text, document, base) - .concat(this.provideReferenceLinks(text, document, base)); + return [ + ...this.providerInlineLinks(text, document, base), + ...this.provideReferenceLinks(text, document, base) + ]; } private providerInlineLinks( @@ -73,20 +94,15 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { ): vscode.DocumentLink[] { const results: vscode.DocumentLink[] = []; for (const match of matchAll(this.linkPattern, text)) { - const pre = match[1]; - const link = match[2]; - const offset = (match.index || 0) + pre.length; - const linkStart = document.positionAt(offset); - const linkEnd = document.positionAt(offset + link.length); - try { - results.push(new vscode.DocumentLink( - new vscode.Range(linkStart, linkEnd), - normalizeLink(document, link, base))); - } catch (e) { - // noop + const matchImage = match[4] && extractDocumentLink(document, base, match[3].length + 1, match[4], match.index); + if (matchImage) { + results.push(matchImage); + } + const matchLink = extractDocumentLink(document, base, match[1].length, match[5], match.index); + if (matchLink) { + results.push(matchLink); } } - return results; } @@ -159,4 +175,4 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { } return out; } -} +} \ No newline at end of file diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 594baaadb82..4850b3759de 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -7,25 +7,37 @@ import { Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; import { TableOfContentsProvider } from '../tableOfContentsProvider'; +import { flatten } from '../util/arrays'; const rangeLimit = 5000; +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)); + export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvider { constructor( private readonly engine: MarkdownEngine ) { } + public async provideFoldingRanges( + document: vscode.TextDocument, + _: vscode.FoldingContext, + _token: vscode.CancellationToken + ): Promise<vscode.FoldingRange[]> { + const foldables = await Promise.all([ + this.getRegions(document), + this.getHeaderFoldingRanges(document), + this.getBlockFoldingRanges(document) + ]); + return flatten(foldables).slice(0, rangeLimit); + } + 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 tokens = await this.engine.parse(document); const regionMarkers = tokens.filter(isRegionMarker) .map(token => ({ line: token.map[0], isStart: isStartRegion(token.content) })); @@ -44,18 +56,6 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi .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 foldables = await Promise.all([ - this.getRegions(document), - this.getHeaderFoldingRanges(document), - this.getBlockFoldingRanges(document)]); - return ([] as vscode.FoldingRange[]).concat.apply([], foldables).slice(0, rangeLimit); - } - private async getHeaderFoldingRanges(document: vscode.TextDocument) { const tocProvider = new TableOfContentsProvider(this.engine, document); const toc = await tocProvider.getToc(); @@ -70,7 +70,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi private async getBlockFoldingRanges(document: vscode.TextDocument): Promise<vscode.FoldingRange[]> { - const isFoldableToken = (token: Token) => { + const isFoldableToken = (token: Token): boolean => { switch (token.type) { case 'fence': case 'list_item_open': @@ -84,7 +84,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi } }; - const tokens = await this.engine.parse(document.uri, document.getText()); + const tokens = await this.engine.parse(document); const multiLineListItems = tokens.filter(isFoldableToken); return multiLineListItems.map(listItem => { const start = listItem.map[0]; diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index e38f9167ec9..0bb67dccd4c 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -8,17 +8,71 @@ import * as path from 'path'; import { Logger } from '../logger'; import { MarkdownContentProvider } from './previewContentProvider'; -import { disposeAll } from '../util/dispose'; +import { Disposable } from '../util/dispose'; import * as nls from 'vscode-nls'; import { getVisibleLine, MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { isMarkdownFile } from '../util/file'; import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; const localize = nls.loadMessageBundle(); -export class MarkdownPreview { +interface WebviewMessage { + readonly source: string; +} + +interface CacheImageSizesMessage extends WebviewMessage { + readonly type: 'cacheImageSizes'; + readonly body: { id: string, width: number, height: number }[]; +} + +interface RevealLineMessage extends WebviewMessage { + readonly type: 'revealLine'; + readonly body: { + readonly line: number; + }; +} + +interface DidClickMessage extends WebviewMessage { + readonly type: 'didClick'; + readonly body: { + readonly line: number; + }; +} + +interface ClickLinkMessage extends WebviewMessage { + readonly type: 'clickLink'; + readonly body: { + readonly path: string; + readonly fragment?: string; + }; +} + +interface ShowPreviewSecuritySelectorMessage extends WebviewMessage { + readonly type: 'showPreviewSecuritySelector'; +} + +interface PreviewStyleLoadErrorMessage extends WebviewMessage { + readonly type: 'previewStyleLoadError'; + readonly body: { + readonly unloadedStyles: string[]; + }; +} + +export class PreviewDocumentVersion { + public constructor( + public readonly resource: vscode.Uri, + public readonly version: number, + ) { } + + public equals(other: PreviewDocumentVersion): boolean { + return this.resource.fsPath === other.resource.fsPath + && this.version === other.version; + } +} + +export class MarkdownPreview extends Disposable { public static viewType = 'markdown.preview'; @@ -28,9 +82,8 @@ export class MarkdownPreview { private readonly editor: vscode.WebviewPanel; private throttleTimer: any; private line: number | undefined = undefined; - private readonly disposables: vscode.Disposable[] = []; private firstUpdate = true; - private currentVersion?: { resource: vscode.Uri, version: number }; + private currentVersion?: PreviewDocumentVersion; private forceUpdate = false; private isScrolling = false; private _disposed: boolean = false; @@ -43,7 +96,7 @@ export class MarkdownPreview { previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, topmostLineMonitor: MarkdownFileTopmostLineMonitor, - contributions: MarkdownContributions, + contributionProvider: MarkdownContributionProvider, ): Promise<MarkdownPreview> { const resource = vscode.Uri.parse(state.resource); const locked = state.locked; @@ -57,9 +110,9 @@ export class MarkdownPreview { previewConfigurations, logger, topmostLineMonitor, - contributions); + contributionProvider); - preview.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, contributions); + preview.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions); if (!isNaN(line)) { preview.line = line; @@ -76,14 +129,14 @@ export class MarkdownPreview { previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, topmostLineMonitor: MarkdownFileTopmostLineMonitor, - contributions: MarkdownContributions + contributionProvider: MarkdownContributionProvider ): MarkdownPreview { const webview = vscode.window.createWebviewPanel( MarkdownPreview.viewType, MarkdownPreview.getPreviewTitle(resource, locked), previewColumn, { enableFindWidget: true, - ...MarkdownPreview.getWebviewOptions(resource, contributions) + ...MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions) }); return new MarkdownPreview( @@ -94,7 +147,7 @@ export class MarkdownPreview { previewConfigurations, logger, topmostLineMonitor, - contributions); + contributionProvider); } private constructor( @@ -105,21 +158,26 @@ export class MarkdownPreview { private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _logger: Logger, topmostLineMonitor: MarkdownFileTopmostLineMonitor, - private readonly _contributions: MarkdownContributions, + private readonly _contributionProvider: MarkdownContributionProvider, ) { + super(); this._resource = resource; this._locked = locked; this.editor = webview; this.editor.onDidDispose(() => { this.dispose(); - }, null, this.disposables); + }, null, this._disposables); this.editor.onDidChangeViewState(e => { this._onDidChangeViewStateEmitter.fire(e); - }, null, this.disposables); + }, null, this._disposables); - this.editor.webview.onDidReceiveMessage(e => { + _contributionProvider.onContributionsChanged(() => { + setImmediate(() => this.refresh()); + }, null, this._disposables); + + this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { if (e.source !== this._resource.toString()) { return; } @@ -138,30 +196,30 @@ export class MarkdownPreview { break; case 'clickLink': - this.onDidClickPreviewLink(e.body.path, e.body.fragement); + this.onDidClickPreviewLink(e.body.path, e.body.fragment); break; case 'showPreviewSecuritySelector': - vscode.commands.executeCommand('markdown.showPreviewSecuritySelector', e.body.source); + vscode.commands.executeCommand('markdown.showPreviewSecuritySelector', e.source); break; case 'previewStyleLoadError': vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", e.body.unloadedStyles.join(', '))); break; } - }, null, this.disposables); + }, null, this._disposables); vscode.workspace.onDidChangeTextDocument(event => { if (this.isPreviewOf(event.document.uri)) { this.refresh(); } - }, null, this.disposables); + }, null, this._disposables); topmostLineMonitor.onDidChangeTopmostLine(event => { if (this.isPreviewOf(event.resource)) { this.updateForView(event.resource, event.line); } - }, null, this.disposables); + }, null, this._disposables); vscode.window.onDidChangeTextEditorSelection(event => { if (this.isPreviewOf(event.textEditor.document.uri)) { @@ -171,13 +229,13 @@ export class MarkdownPreview { source: this.resource.toString() }); } - }, null, this.disposables); + }, null, this._disposables); vscode.window.onDidChangeActiveTextEditor(editor => { if (editor && isMarkdownFile(editor.document) && !this._locked) { this.update(editor.document.uri); } - }, null, this.disposables); + }, null, this._disposables); } private readonly _onDisposeEmitter = new vscode.EventEmitter<void>(); @@ -200,18 +258,17 @@ export class MarkdownPreview { } public dispose() { + super.dispose(); if (this._disposed) { return; } this._disposed = true; this._onDisposeEmitter.fire(); - this._onDisposeEmitter.dispose(); + this._onDidChangeViewStateEmitter.dispose(); this.editor.dispose(); - - disposeAll(this.disposables); } public update(resource: vscode.Uri) { @@ -286,7 +343,7 @@ export class MarkdownPreview { } private get iconPath() { - const root = path.join(this._contributions.extensionPath, 'media'); + const root = path.join(this._contributionProvider.extensionPath, 'media'); return { light: vscode.Uri.file(path.join(root, 'Preview.svg')), dark: vscode.Uri.file(path.join(root, 'Preview_inverse.svg')) @@ -336,8 +393,16 @@ export class MarkdownPreview { clearTimeout(this.throttleTimer); this.throttleTimer = undefined; - const document = await vscode.workspace.openTextDocument(resource); - if (!this.forceUpdate && this.currentVersion && this.currentVersion.resource.fsPath === resource.fsPath && this.currentVersion.version === document.version) { + let document: vscode.TextDocument; + try { + document = await vscode.workspace.openTextDocument(resource); + } catch { + await this.showFileNotFoundError(); + return; + } + + const pendingVersion = new PreviewDocumentVersion(resource, document.version); + if (!this.forceUpdate && this.currentVersion && this.currentVersion.equals(pendingVersion)) { if (this.line) { this.updateForView(resource, this.line); } @@ -345,13 +410,14 @@ export class MarkdownPreview { } this.forceUpdate = false; - this.currentVersion = { resource, version: document.version }; - const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state); + this.currentVersion = pendingVersion; 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; + const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state); + // Another call to `doUpdate` may have happened. + // Make sure we are still updating for the correct document + if (this.currentVersion && this.currentVersion.equals(pendingVersion)) { + this.setContent(content); + } } } @@ -368,7 +434,7 @@ export class MarkdownPreview { private static getLocalResourceRoots( resource: vscode.Uri, contributions: MarkdownContributions - ): vscode.Uri[] { + ): ReadonlyArray<vscode.Uri> { const baseRoots = contributions.previewResourceRoots; const folder = vscode.workspace.getWorkspaceFolder(resource); @@ -411,7 +477,22 @@ export class MarkdownPreview { } } - vscode.workspace.openTextDocument(this._resource).then(vscode.window.showTextDocument); + vscode.workspace.openTextDocument(this._resource) + .then(vscode.window.showTextDocument) + .then(undefined, () => { + vscode.window.showErrorMessage(localize('preview.clickOpenFailed', 'Could not open {0}', this._resource.toString())); + }); + } + + private async showFileNotFoundError() { + this.setContent(this._contentProvider.provideFileNotFoundContent(this._resource)); + } + + private setContent(html: string): void { + this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked); + this.editor.iconPath = this.iconPath; + this.editor.webview.options = MarkdownPreview.getWebviewOptions(this._resource, this._contributionProvider.contributions); + this.editor.webview.html = html; } private async onDidClickPreviewLink(path: string, fragment: string | undefined) { diff --git a/extensions/markdown-language-features/src/features/previewConfig.ts b/extensions/markdown-language-features/src/features/previewConfig.ts index d660bf10793..ed09a9ce46e 100644 --- a/extensions/markdown-language-features/src/features/previewConfig.ts +++ b/extensions/markdown-language-features/src/features/previewConfig.ts @@ -12,7 +12,6 @@ export class MarkdownPreviewConfiguration { public readonly scrollBeyondLastLine: boolean; public readonly wordWrap: boolean; - public readonly previewFrontMatter: string; public readonly lineBreaks: boolean; public readonly doubleClickToSwitchToEditor: boolean; public readonly scrollEditorWithPreview: boolean; @@ -36,7 +35,6 @@ export class MarkdownPreviewConfiguration { this.wordWrap = markdownEditorConfig['editor.wordWrap'] !== 'off'; } - this.previewFrontMatter = markdownConfig.get<string>('previewFrontMatter', 'hide'); this.scrollPreviewWithEditor = !!markdownConfig.get<boolean>('preview.scrollPreviewWithEditor', true); this.scrollEditorWithPreview = !!markdownConfig.get<boolean>('preview.scrollEditorWithPreview', true); this.lineBreaks = !!markdownConfig.get<boolean>('preview.breaks', false); diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 30767b88c5d..98ffe0bfba0 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -13,7 +13,7 @@ const localize = nls.loadMessageBundle(); import { Logger } from '../logger'; import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security'; import { MarkdownPreviewConfigurationManager, MarkdownPreviewConfiguration } from './previewConfig'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider } from '../markdownExtensions'; /** * Strings used inside the markdown preview. @@ -40,7 +40,7 @@ export class MarkdownContentProvider { private readonly engine: MarkdownEngine, private readonly context: vscode.ExtensionContext, private readonly cspArbiter: ContentSecurityPolicyArbiter, - private readonly contributions: MarkdownContributions, + private readonly contributionProvider: MarkdownContributionProvider, private readonly logger: Logger ) { } @@ -68,7 +68,7 @@ export class MarkdownContentProvider { const nonce = new Date().getTime() + '' + new Date().getMilliseconds(); const csp = this.getCspForResource(sourceUri, nonce); - const body = await this.engine.render(sourceUri, config.previewFrontMatter === 'hide', markdownDocument.getText()); + const body = await this.engine.render(markdownDocument); return `<!DOCTYPE html> <html> <head> @@ -90,6 +90,19 @@ export class MarkdownContentProvider { </html>`; } + public provideFileNotFoundContent( + resource: vscode.Uri, + ): string { + const resourcePath = path.basename(resource.fsPath); + const body = localize('preview.notFound', '{0} cannot be found', resourcePath); + return `<!DOCTYPE html> + <html> + <body class="vscode-body"> + ${body} + </body> + </html>`; + } + private extensionResourcePath(mediaFile: string): string { return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))) .with({ scheme: 'vscode-resource' }) @@ -101,21 +114,19 @@ export class MarkdownContentProvider { return href; } - // Use href if it is already an URL - const hrefUri = vscode.Uri.parse(href); - if (['http', 'https'].indexOf(hrefUri.scheme) >= 0) { - return hrefUri.toString(true); + if (href.startsWith('http:') || href.startsWith('https:') || href.startsWith('file:')) { + return href; } - // Use href as file URI if it is absolute - if (path.isAbsolute(href) || hrefUri.scheme === 'file') { + // Assume it must be a local file + if (path.isAbsolute(href)) { return vscode.Uri.file(href) .with({ scheme: 'vscode-resource' }) .toString(); } // Use a workspace relative path if there is a workspace - let root = vscode.workspace.getWorkspaceFolder(resource); + const root = vscode.workspace.getWorkspaceFolder(resource); if (root) { return vscode.Uri.file(path.join(root.uri.fsPath, href)) .with({ scheme: 'vscode-resource' }) @@ -163,7 +174,7 @@ export class MarkdownContentProvider { } private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string { - const baseStyles = this.contributions.previewStyles + const baseStyles = this.contributionProvider.contributions.previewStyles .map(resource => `<link rel="stylesheet" type="text/css" href="${resource.toString()}">`) .join('\n'); @@ -174,7 +185,7 @@ export class MarkdownContentProvider { } private getScripts(nonce: string): string { - return this.contributions.previewScripts + return this.contributionProvider.contributions.previewScripts .map(resource => `<script async src="${resource.toString()}" nonce="${nonce}" charset="UTF-8"></script>`) .join('\n'); } diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index 15e414f762e..aed62942826 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { Logger } from '../logger'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider } from '../markdownExtensions'; import { disposeAll } from '../util/dispose'; import { MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreview, PreviewSettings } from './preview'; @@ -25,7 +25,7 @@ export class MarkdownPreviewManager implements vscode.WebviewPanelSerializer { public constructor( private readonly _contentProvider: MarkdownContentProvider, private readonly _logger: Logger, - private readonly _contributions: MarkdownContributions + private readonly _contributions: MarkdownContributionProvider ) { this._disposables.push(vscode.window.registerWebviewPanelSerializer(MarkdownPreview.viewType, this)); } diff --git a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts index 12e51c75d76..fe886423aa0 100644 --- a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { disposeAll } from '../util/dispose'; +import { Disposable } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; import { Lazy, lazy } from '../util/lazy'; import MDDocumentSymbolProvider from './documentSymbolProvider'; import { SkinnyTextDocument } from '../tableOfContentsProvider'; +import { flatten } from '../util/arrays'; export interface WorkspaceMarkdownDocumentProvider { getAllMarkdownDocuments(): Thenable<Iterable<SkinnyTextDocument>>; @@ -18,25 +19,13 @@ export interface WorkspaceMarkdownDocumentProvider { readonly onDidDeleteMarkdownDocument: vscode.Event<vscode.Uri>; } -class VSCodeWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocumentProvider { +class VSCodeWorkspaceMarkdownDocumentProvider extends Disposable implements WorkspaceMarkdownDocumentProvider { - private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter<SkinnyTextDocument>(); - private readonly _onDidCreateMarkdownDocumentEmitter = new vscode.EventEmitter<SkinnyTextDocument>(); - private readonly _onDidDeleteMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.Uri>(); + private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<SkinnyTextDocument>()); + private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<SkinnyTextDocument>()); + private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<vscode.Uri>()); private _watcher: vscode.FileSystemWatcher | undefined; - private _disposables: vscode.Disposable[] = []; - - public dispose() { - this._onDidChangeMarkdownDocumentEmitter.dispose(); - this._onDidDeleteMarkdownDocumentEmitter.dispose(); - - if (this._watcher) { - this._watcher.dispose(); - } - - disposeAll(this._disposables); - } async getAllMarkdownDocuments() { const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**'); @@ -64,7 +53,7 @@ class VSCodeWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocume return; } - this._watcher = vscode.workspace.createFileSystemWatcher('**/*.md'); + this._watcher = this._register(vscode.workspace.createFileSystemWatcher('**/*.md')); this._watcher.onDidChange(async resource => { const document = await this.getMarkdownDocument(resource); @@ -98,15 +87,16 @@ class VSCodeWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocume } -export default class MarkdownWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { +export default class MarkdownWorkspaceSymbolProvider extends Disposable implements vscode.WorkspaceSymbolProvider { private _symbolCache = new Map<string, Lazy<Thenable<vscode.SymbolInformation[]>>>(); private _symbolCachePopulated: boolean = false; - private _disposables: vscode.Disposable[] = []; public constructor( private _symbolProvider: MDDocumentSymbolProvider, private _workspaceMarkdownDocumentProvider: WorkspaceMarkdownDocumentProvider = new VSCodeWorkspaceMarkdownDocumentProvider() - ) { } + ) { + super(); + } public async provideWorkspaceSymbols(query: string): Promise<vscode.SymbolInformation[]> { if (!this._symbolCachePopulated) { @@ -119,7 +109,7 @@ export default class MarkdownWorkspaceSymbolProvider implements vscode.Workspace } const allSymbolsSets = await Promise.all(Array.from(this._symbolCache.values()).map(x => x.value)); - const allSymbols: vscode.SymbolInformation[] = Array.prototype.concat.apply([], allSymbolsSets); + const allSymbols = flatten(allSymbolsSets); return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1); } @@ -130,10 +120,6 @@ export default class MarkdownWorkspaceSymbolProvider implements vscode.Workspace } } - public dispose(): void { - disposeAll(this._disposables); - } - private getSymbols(document: SkinnyTextDocument): Lazy<Thenable<vscode.SymbolInformation[]>> { return lazy(async () => { return this._symbolProvider.provideDocumentSymbolInformation(document); diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 9bd90ead16a..0c10f5540fa 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -3,125 +3,165 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as crypto from 'crypto'; import { MarkdownIt, Token } from 'markdown-it'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as crypto from 'crypto'; -import { MarkdownContributions } from './markdownExtensions'; +import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions'; import { Slugifier } from './slugify'; +import { SkinnyTextDocument } from './tableOfContentsProvider'; import { getUriForLinkWithKnownExternalScheme } from './util/links'; -const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/; +const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; + +interface MarkdownItConfig { + readonly breaks: boolean; + readonly linkify: boolean; +} + +class TokenCache { + private cachedDocument?: { + readonly uri: vscode.Uri; + readonly version: number; + readonly config: MarkdownItConfig; + }; + private tokens?: Token[]; + + public tryGetCached(document: SkinnyTextDocument, config: MarkdownItConfig): Token[] | undefined { + if (this.cachedDocument + && this.cachedDocument.uri.toString() === document.uri.toString() + && this.cachedDocument.version === document.version + && this.cachedDocument.config.breaks === config.breaks + && this.cachedDocument.config.linkify === config.linkify + ) { + return this.tokens; + } + return undefined; + } + + public update(document: SkinnyTextDocument, config: MarkdownItConfig, tokens: Token[]) { + this.cachedDocument = { + uri: document.uri, + version: document.version, + config, + }; + this.tokens = tokens; + } + + public clean(): void { + this.cachedDocument = undefined; + this.tokens = undefined; + } +} export class MarkdownEngine { - private md?: MarkdownIt; + private md?: Promise<MarkdownIt>; - private firstLine?: number; private currentDocument?: vscode.Uri; private _slugCount = new Map<string, number>(); + private _tokenCache = new TokenCache(); public constructor( - private readonly extensionPreviewResourceProvider: MarkdownContributions, + private readonly contributionProvider: MarkdownContributionProvider, private readonly slugifier: Slugifier, - ) { } - - private usePlugin(factory: (md: any) => any): void { - try { - this.md = factory(this.md); - } catch (e) { - // noop - } + ) { + contributionProvider.onContributionsChanged(() => { + // Markdown plugin contributions may have changed + this.md = undefined; + }); } - private async getEngine(resource: vscode.Uri): Promise<MarkdownIt> { + private async getEngine(config: MarkdownItConfig): Promise<MarkdownIt> { if (!this.md) { - const hljs = await import('highlight.js'); - this.md = (await import('markdown-it'))({ - html: true, - highlight: (str: string, lang?: string) => { - // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 - if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { - lang = 'jsx'; + this.md = import('markdown-it').then(async markdownIt => { + let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md)); + + for (const plugin of this.contributionProvider.contributions.markdownItPlugins.values()) { + try { + md = (await plugin)(md); + } catch { + // noop } - if (lang && lang.toLocaleLowerCase() === 'json5') { - lang = 'json'; - } - if (lang && lang.toLocaleLowerCase() === 'c#') { - lang = 'cs'; - } - if (lang && hljs.getLanguage(lang)) { - try { - return `<div>${hljs.highlight(lang, str, true).value}</div>`; - } catch (error) { } - } - return `<code><div>${this.md!.utils.escapeHtml(str)}</div></code>`; } + + const frontMatterPlugin = require('markdown-it-front-matter'); + // Extract rules from front matter plugin and apply at a lower precedence + let fontMatterRule: any; + frontMatterPlugin({ + block: { + ruler: { + before: (_id: any, _id2: any, rule: any) => { fontMatterRule = rule; } + } + } + }, () => { /* noop */ }); + + md.block.ruler.before('fence', 'front_matter', fontMatterRule, { + alt: ['paragraph', 'reference', 'blockquote', 'list'] + }); + + for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { + this.addLineNumberRenderer(md, renderName); + } + + this.addImageStabilizer(md); + this.addFencedRenderer(md); + + this.addLinkNormalizer(md); + this.addLinkValidator(md); + this.addNamedHeaders(md); + return md; }); - - for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) { - this.usePlugin(await plugin); - } - - for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { - this.addLineNumberRenderer(this.md, renderName); - } - - this.addImageStabilizer(this.md); - this.addFencedRenderer(this.md); - - this.addLinkNormalizer(this.md); - this.addLinkValidator(this.md); - this.addNamedHeaders(this.md); } + const md = await this.md!; + md.set(config); + return md; + } + + private tokenize( + document: SkinnyTextDocument, + config: MarkdownItConfig, + engine: MarkdownIt + ): Token[] { + const cached = this._tokenCache.tryGetCached(document, config); + if (cached) { + return cached; + } + + this.currentDocument = document.uri; + this._slugCount = new Map<string, number>(); + + const text = document.getText(); + const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); + this._tokenCache.update(document, config, tokens); + return tokens; + } + + public async render(document: SkinnyTextDocument): Promise<string> { + const config = this.getConfig(document.uri); + const engine = await this.getEngine(config); + return engine.renderer.render(this.tokenize(document, config, engine), { + ...(engine as any).options, + ...config + }, {}); + } + + public async parse(document: SkinnyTextDocument): Promise<Token[]> { + const config = this.getConfig(document.uri); + const engine = await this.getEngine(config); + return this.tokenize(document, config, engine); + } + + public cleanCache(): void { + this._tokenCache.clean(); + } + + private getConfig(resource: vscode.Uri): MarkdownItConfig { const config = vscode.workspace.getConfiguration('markdown', resource); - this.md.set({ + return { breaks: config.get<boolean>('preview.breaks', false), linkify: config.get<boolean>('preview.linkify', true) - }); - return this.md; - } - - private stripFrontmatter(text: string): { text: string, offset: number } { - let offset = 0; - const frontMatterMatch = FrontMatterRegex.exec(text); - if (frontMatterMatch) { - const frontMatter = frontMatterMatch[0]; - offset = frontMatter.split(/\r\n|\n|\r/g).length - 1; - text = text.substr(frontMatter.length); - } - return { text, offset }; - } - - public async render(document: vscode.Uri, stripFrontmatter: boolean, text: string): Promise<string> { - let offset = 0; - if (stripFrontmatter) { - const markdownContent = this.stripFrontmatter(text); - offset = markdownContent.offset; - text = markdownContent.text; - } - this.currentDocument = document; - this.firstLine = offset; - this._slugCount = new Map<string, number>(); - - const engine = await this.getEngine(document); - return engine.render(text); - } - - public async parse(document: vscode.Uri, source: string): Promise<Token[]> { - const { text, offset } = this.stripFrontmatter(source); - this.currentDocument = document; - this._slugCount = new Map<string, number>(); - - const engine = await this.getEngine(document); - - return engine.parse(text, {}).map(token => { - if (token.map) { - token.map[0] += offset; - token.map[1] += offset; - } - return token; - }); + }; } private addLineNumberRenderer(md: any, ruleName: string): void { @@ -129,7 +169,7 @@ export class MarkdownEngine { md.renderer.rules[ruleName] = (tokens: any, idx: number, options: any, env: any, self: any) => { const token = tokens[idx]; if (token.map && token.map.length) { - token.attrSet('data-line', this.firstLine + token.map[0]); + token.attrSet('data-line', token.map[0]); token.attrJoin('class', 'code-line'); } @@ -250,4 +290,30 @@ export class MarkdownEngine { } }; } -} \ No newline at end of file +} + +async function getMarkdownOptions(md: () => MarkdownIt) { + const hljs = await import('highlight.js'); + return { + html: true, + highlight: (str: string, lang?: string) => { + // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 + if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { + lang = 'jsx'; + } + if (lang && lang.toLocaleLowerCase() === 'json5') { + lang = 'json'; + } + if (lang && lang.toLocaleLowerCase() === 'c#') { + lang = 'cs'; + } + if (lang && hljs.getLanguage(lang)) { + try { + return `<div>${hljs.highlight(lang, str, true).value}</div>`; + } + catch (error) { } + } + return `<code><div>${md().utils.escapeHtml(str)}</div></code>`; + } + }; +} diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index 55c8d76e7a4..65899874f5a 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -5,13 +5,15 @@ import * as vscode from 'vscode'; import * as path from 'path'; +import { Disposable } from './util/dispose'; +import * as arrays from './util/arrays'; const resolveExtensionResource = (extension: vscode.Extension<any>, resourcePath: string): vscode.Uri => { return vscode.Uri.file(path.join(extension.extensionPath, resourcePath)) .with({ scheme: 'vscode-resource' }); }; -const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePaths: any): vscode.Uri[] => { +const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePaths: unknown): vscode.Uri[] => { const result: vscode.Uri[] = []; if (Array.isArray(resourcePaths)) { for (const resource of resourcePaths) { @@ -26,96 +28,135 @@ 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>[]; - readonly previewResourceRoots: vscode.Uri[]; + readonly previewScripts: ReadonlyArray<vscode.Uri>; + readonly previewStyles: ReadonlyArray<vscode.Uri>; + readonly previewResourceRoots: ReadonlyArray<vscode.Uri>; + readonly markdownItPlugins: Map<string, Thenable<(md: any) => any>>; } -class MarkdownExtensionContributions implements MarkdownContributions { - private readonly _scripts: vscode.Uri[] = []; - private readonly _styles: vscode.Uri[] = []; - private readonly _previewResourceRoots: vscode.Uri[] = []; - private readonly _plugins: Thenable<(md: any) => any>[] = []; +export namespace MarkdownContributions { + export const Empty: MarkdownContributions = { + previewScripts: [], + previewStyles: [], + previewResourceRoots: [], + markdownItPlugins: new Map() + }; - private _loaded = false; - - public constructor( - public readonly extensionPath: string, - ) { } - - public get previewScripts(): vscode.Uri[] { - this.ensureLoaded(); - return this._scripts; + export function merge(a: MarkdownContributions, b: MarkdownContributions): MarkdownContributions { + return { + previewScripts: [...a.previewScripts, ...b.previewScripts], + previewStyles: [...a.previewStyles, ...b.previewStyles], + previewResourceRoots: [...a.previewResourceRoots, ...b.previewResourceRoots], + markdownItPlugins: new Map([...a.markdownItPlugins.entries(), ...b.markdownItPlugins.entries()]), + }; } - public get previewStyles(): vscode.Uri[] { - this.ensureLoaded(); - return this._styles; + function uriEqual(a: vscode.Uri, b: vscode.Uri): boolean { + return a.toString() === b.toString(); } - public get previewResourceRoots(): vscode.Uri[] { - this.ensureLoaded(); - return this._previewResourceRoots; + export function equal(a: MarkdownContributions, b: MarkdownContributions): boolean { + return arrays.equals(a.previewScripts, b.previewScripts, uriEqual) + && arrays.equals(a.previewStyles, b.previewStyles, uriEqual) + && arrays.equals(a.previewResourceRoots, b.previewResourceRoots, uriEqual) + && arrays.equals(Array.from(a.markdownItPlugins.keys()), Array.from(b.markdownItPlugins.keys())); } - public get markdownItPlugins(): Thenable<(md: any) => any>[] { - this.ensureLoaded(); - return this._plugins; - } - - private ensureLoaded() { - if (this._loaded) { - return; + export function fromExtension( + extension: vscode.Extension<any> + ): MarkdownContributions { + const contributions = extension.packageJSON && extension.packageJSON.contributes; + if (!contributions) { + return MarkdownContributions.Empty; } - this._loaded = true; - for (const extension of vscode.extensions.all) { - const contributes = extension.packageJSON && extension.packageJSON.contributes; - if (!contributes) { - continue; - } + const previewStyles = getContributedStyles(contributions, extension); + const previewScripts = getContributedScripts(contributions, extension); + const previewResourceRoots = previewStyles.length || previewScripts.length ? [vscode.Uri.file(extension.extensionPath)] : []; + const markdownItPlugins = getContributedMarkdownItPlugins(contributions, extension); - this.tryLoadPreviewStyles(contributes, extension); - this.tryLoadPreviewScripts(contributes, extension); - this.tryLoadMarkdownItPlugins(contributes, extension); - - if (contributes['markdown.previewScripts'] || contributes['markdown.previewStyles']) { - this._previewResourceRoots.push(vscode.Uri.file(extension.extensionPath)); - } - } + return { + previewScripts, + previewStyles, + previewResourceRoots, + markdownItPlugins + }; } - private tryLoadMarkdownItPlugins( + function getContributedMarkdownItPlugins( contributes: any, extension: vscode.Extension<any> - ) { + ): Map<string, Thenable<(md: any) => any>> { + const map = new Map<string, Thenable<(md: any) => any>>(); if (contributes['markdown.markdownItPlugins']) { - this._plugins.push(extension.activate().then(() => { + map.set(extension.id, extension.activate().then(() => { if (extension.exports && extension.exports.extendMarkdownIt) { return (md: any) => extension.exports.extendMarkdownIt(md); } return (md: any) => md; })); } + return map; } - private tryLoadPreviewScripts( + function getContributedScripts( contributes: any, extension: vscode.Extension<any> ) { - this._scripts.push(...resolveExtensionResources(extension, contributes['markdown.previewScripts'])); + return resolveExtensionResources(extension, contributes['markdown.previewScripts']); } - private tryLoadPreviewStyles( + function getContributedStyles( contributes: any, extension: vscode.Extension<any> ) { - this._styles.push(...resolveExtensionResources(extension, contributes['markdown.previewStyles'])); + return resolveExtensionResources(extension, contributes['markdown.previewStyles']); } } -export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributions { - return new MarkdownExtensionContributions(context.extensionPath); +export interface MarkdownContributionProvider { + readonly extensionPath: string; + readonly contributions: MarkdownContributions; + readonly onContributionsChanged: vscode.Event<this>; + + dispose(): void; +} + +class VSCodeExtensionMarkdownContributionProvider extends Disposable implements MarkdownContributionProvider { + private _contributions?: MarkdownContributions; + + public constructor( + public readonly extensionPath: string, + ) { + super(); + + vscode.extensions.onDidChange(() => { + const currentContributions = this.getCurrentContributions(); + const existingContributions = this._contributions || MarkdownContributions.Empty; + if (!MarkdownContributions.equal(existingContributions, currentContributions)) { + this._contributions = currentContributions; + this._onContributionsChanged.fire(this); + } + }, undefined, this._disposables); + } + + private readonly _onContributionsChanged = this._register(new vscode.EventEmitter<this>()); + public readonly onContributionsChanged = this._onContributionsChanged.event; + + public get contributions(): MarkdownContributions { + if (!this._contributions) { + this._contributions = this.getCurrentContributions(); + } + return this._contributions; + } + + private getCurrentContributions(): MarkdownContributions { + return vscode.extensions.all + .map(MarkdownContributions.fromExtension) + .reduce(MarkdownContributions.merge, MarkdownContributions.Empty); + } +} + +export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributionProvider { + return new VSCodeExtensionMarkdownContributionProvider(context.extensionPath); } \ No newline at end of file diff --git a/extensions/markdown-language-features/src/security.ts b/extensions/markdown-language-features/src/security.ts index 3a62eed2dbd..7f11d65a113 100644 --- a/extensions/markdown-language-features/src/security.ts +++ b/extensions/markdown-language-features/src/security.ts @@ -149,6 +149,7 @@ export class PreviewSecuritySelector { if (selection.type === 'toggle') { this.cspArbiter.setShouldDisableSecurityWarning(!this.cspArbiter.shouldDisableSecurityWarnings()); + this.webviewManager.refresh(); return; } else { await this.cspArbiter.setSecurityLevelForResource(resource, selection.type); diff --git a/extensions/markdown-language-features/src/slugify.ts b/extensions/markdown-language-features/src/slugify.ts index 5b9146b602f..a91c5126a22 100644 --- a/extensions/markdown-language-features/src/slugify.ts +++ b/extensions/markdown-language-features/src/slugify.ts @@ -23,7 +23,7 @@ export const githubSlugifier: Slugifier = new class implements Slugifier { heading.trim() .toLowerCase() .replace(/\s+/g, '-') // Replace whitespace with - - .replace(/[\]\[\!\'\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators + .replace(/[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators .replace(/^\-+/, '') // Remove leading - .replace(/\-+$/, '') // Remove trailing - ); diff --git a/extensions/markdown-language-features/src/tableOfContentsProvider.ts b/extensions/markdown-language-features/src/tableOfContentsProvider.ts index bbb30b7cea6..342d71f1cfe 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 version: number; readonly lineCount: number; getText(): string; lineAt(line: number): vscode.TextLine; @@ -49,7 +50,7 @@ export class TableOfContentsProvider { private async buildToc(document: SkinnyTextDocument): Promise<TocEntry[]> { const toc: TocEntry[] = []; - const tokens = await this.engine.parse(document.uri, document.getText()); + const tokens = await this.engine.parse(document); const slugCount = new Map<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 5a1d0c87880..f6309a027d3 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -103,6 +103,33 @@ suite('markdown.DocumentLinkProvider', () => { assertRangeEqual(link1.range, new vscode.Range(0, 10, 0, 14)); assertRangeEqual(link2.range, new vscode.Range(0, 23, 0, 28)); }); + + // #49238 + test('should handle hyperlinked images', () => { + { + const links = getLinksForFile('[![alt text](image.jpg)](https://example.com)'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,13,0,22)); + assertRangeEqual(link2.range, new vscode.Range(0,25,0,44)); + } + { + const links = getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); + assert.strictEqual(links.length, 2); + const [link1, link2] = links; + assertRangeEqual(link1.range, new vscode.Range(0,7,0,21)); + assertRangeEqual(link2.range, new vscode.Range(0,26,0,48)); + } + { + const links = getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); + assert.strictEqual(links.length, 4); + const [link1, link2, link3, link4] = links; + assertRangeEqual(link1.range, new vscode.Range(0,6,0,14)); + assertRangeEqual(link2.range, new vscode.Range(0,17,0,26)); + assertRangeEqual(link3.range, new vscode.Range(0,39,0,47)); + assertRangeEqual(link4.range, new vscode.Range(0,50,0,59)); + } + }); }); diff --git a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts index 9533f807cf9..483c8395db0 100644 --- a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts @@ -82,5 +82,16 @@ suite('markdown.DocumentSymbolProvider', () => { assert.strictEqual(symbols[0].children[0].name, '### h2'); assert.strictEqual(symbols[0].children[1].name, '## h3'); }); + + test('Should handle line separator in file. Issue #63749', async () => { + const symbols = await getSymbolsForFile(`# A +- foo
 + +# B +- bar`); + assert.strictEqual(symbols.length, 2); + assert.strictEqual(symbols[0].name, '# A'); + assert.strictEqual(symbols[1].name, '# B'); + }); }); diff --git a/extensions/markdown-language-features/src/test/engine.ts b/extensions/markdown-language-features/src/test/engine.ts index a1834e057a2..cb74a5b695f 100644 --- a/extensions/markdown-language-features/src/test/engine.ts +++ b/extensions/markdown-language-features/src/test/engine.ts @@ -5,15 +5,14 @@ import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { githubSlugifier } from '../slugify'; +import { Disposable } from '../util/dispose'; -const emptyContributions = new class implements MarkdownContributions { +const emptyContributions = new class extends Disposable implements MarkdownContributionProvider { readonly extensionPath = ''; - readonly previewScripts: vscode.Uri[] = []; - readonly previewStyles: vscode.Uri[] = []; - readonly previewResourceRoots: vscode.Uri[] = []; - readonly markdownItPlugins: Promise<(md: any) => any>[] = []; + readonly contributions = MarkdownContributions.Empty; + readonly onContributionsChanged = this._register(new vscode.EventEmitter<this>()).event; }; export function createNewMarkdownEngine(): MarkdownEngine { diff --git a/extensions/markdown-language-features/src/test/inMemoryDocument.ts b/extensions/markdown-language-features/src/test/inMemoryDocument.ts index bfcf3721b65..c2472e5a4ec 100644 --- a/extensions/markdown-language-features/src/test/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/test/inMemoryDocument.ts @@ -10,7 +10,8 @@ export class InMemoryDocument implements vscode.TextDocument { constructor( public readonly uri: vscode.Uri, - private readonly _contents: string + private readonly _contents: string, + public readonly version = 1, ) { this._lines = this._contents.split(/\n/g); } @@ -18,7 +19,6 @@ export class InMemoryDocument implements vscode.TextDocument { isUntitled: boolean = false; languageId: string = ''; - version: number = 1; isDirty: boolean = false; isClosed: boolean = false; eol: vscode.EndOfLine = vscode.EndOfLine.LF; diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts index f5b3eb0ea4a..e74d3b592bc 100644 --- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts @@ -38,7 +38,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { const fileNameCount = 10; const files: vscode.TextDocument[] = []; for (let i = 0; i < fileNameCount; ++i) { - const testFileName = vscode.Uri.parse(`test${i}.md`); + const testFileName = vscode.Uri.file(`test${i}.md`); files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`)); } @@ -52,7 +52,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { const testFileName = vscode.Uri.file('test.md'); const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([ - new InMemoryDocument(testFileName, `# header1`) + new InMemoryDocument(testFileName, `# header1`, 1 /* version */) ]); const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); @@ -60,7 +60,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); // Update file - workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`)); + workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); const newSymbols = await provider.provideWorkspaceSymbols(''); assert.strictEqual(newSymbols.length, 2); assert.strictEqual(newSymbols[0].name, '# new header'); diff --git a/extensions/markdown-language-features/src/typings/markdown-it-named-headers.d.ts b/extensions/markdown-language-features/src/typings/markdown-it-named-headers.d.ts deleted file mode 100644 index de9b23d0df9..00000000000 --- a/extensions/markdown-language-features/src/typings/markdown-it-named-headers.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -declare module 'markdown-it-named-headers' { } \ No newline at end of file diff --git a/extensions/markdown-language-features/src/typings/ref.d.ts b/extensions/markdown-language-features/src/typings/ref.d.ts index bc057c55878..954bab971e3 100644 --- a/extensions/markdown-language-features/src/typings/ref.d.ts +++ b/extensions/markdown-language-features/src/typings/ref.d.ts @@ -4,4 +4,5 @@ *--------------------------------------------------------------------------------------------*/ /// <reference path='../../../../src/vs/vscode.d.ts'/> +/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/> /// <reference types='@types/node'/> diff --git a/extensions/markdown-language-features/src/util/arrays.ts b/extensions/markdown-language-features/src/util/arrays.ts new file mode 100644 index 00000000000..ec0ed25c55d --- /dev/null +++ b/extensions/markdown-language-features/src/util/arrays.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function equals<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { + if (one.length !== other.length) { + return false; + } + + for (let i = 0, len = one.length; i < len; i++) { + if (!itemEquals(one[i], other[i])) { + return false; + } + } + + return true; +} + +export function flatten<T>(arr: ReadonlyArray<T>[]): T[] { + return ([] as T[]).concat.apply([], arr); +} \ No newline at end of file diff --git a/extensions/markdown-language-features/src/util/dispose.ts b/extensions/markdown-language-features/src/util/dispose.ts index 19b3b3f4f19..548094c28e5 100644 --- a/extensions/markdown-language-features/src/util/dispose.ts +++ b/extensions/markdown-language-features/src/util/dispose.ts @@ -14,3 +14,29 @@ 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; + } + + protected get isDisposed() { + return this._isDisposed; + } +} \ No newline at end of file diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index 1afcbff83bd..c1c2b1bdfb7 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; -const knownSchemes = ['http:', 'https:', 'file:', 'mailto:', 'data:', 'vscode-resource:']; +const knownSchemes = ['http:', 'https:', 'file:', 'mailto:', 'data:', `${vscode.env.uriScheme}:`, 'vscode:', 'vscode-insiders:', 'vscode-resource:']; export function getUriForLinkWithKnownExternalScheme( link: string, @@ -15,4 +15,4 @@ export function getUriForLinkWithKnownExternalScheme( } return undefined; -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 74030b70a59..e85ae950b3a 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@types/highlight.js@9.1.10": - version "9.1.10" - resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.1.10.tgz#b621f809cd9573b80992b90cffc5788208e3069c" - integrity sha512-3uQgLVw3ukDjrgi1h2qxSgsg2W7Sp/BN/P+IBgi8D019FdCcetJzJIxk0Wp1Qfcxzy3EreUnPI7/1HXhFNCRTg== +"@types/highlight.js@9.12.3": + version "9.12.3" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca" + integrity sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ== "@types/lodash.throttle@^4.1.3": version "4.1.3" @@ -29,10 +29,10 @@ resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.2.tgz#5d9ad19e6e6508cdd2f2596df86fd0aade598660" integrity sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA= -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== abbrev@1: version "1.1.1" @@ -159,10 +159,10 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -2933,10 +2933,10 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -highlight.js@9.12.0: - version "9.12.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" - integrity sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4= +highlight.js@9.13.1: + version "9.13.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" + integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== hmac-drbg@^1.0.0: version "1.0.1" @@ -3895,10 +3895,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.1.tgz#206fe59b0e4e1b78a7c73250af9b34a4ad0aaf44" - integrity sha512-CzzqSSNkFRUf9vlWvhK1awpJreMRqdCrBvZ8DIoDWTOkESMIF741UPAhuAmbyWmdiFPA6WARNhnu2M6Nrhwa+A== +markdown-it-front-matter@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20" + integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA= + +markdown-it@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" + integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== dependencies: argparse "^1.0.7" entities "~1.1.1" @@ -6005,10 +6010,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" - integrity sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw== +typescript@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== uc.micro@^1.0.1: version "1.0.3" @@ -6308,12 +6313,12 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts index a736080896c..3247b592f88 100644 --- a/extensions/merge-conflict/src/commandHandler.ts +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -158,6 +158,11 @@ export default class CommandHandler implements vscode.Disposable { let navigationResult = await this.findConflictForNavigation(editor, direction); if (!navigationResult) { + // Check for autoNavigateNextConflict, if it's enabled(which indicating no conflict remain), then do not show warning + const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict'); + if (mergeConflictConfig.get<boolean>('autoNavigateNextConflict.enabled')) { + return; + } vscode.window.showWarningMessage(localize('noConflicts', 'No merge conflicts found in this file')); return; } @@ -232,9 +237,9 @@ export default class CommandHandler implements vscode.Disposable { return null; } - for (let i = 0; i < conflicts.length; i++) { - if (conflicts[i].range.contains(editor.selection.active)) { - return conflicts[i]; + for (const conflict of conflicts) { + if (conflict.range.contains(editor.selection.active)) { + return conflict; } } @@ -277,11 +282,11 @@ export default class CommandHandler implements vscode.Disposable { throw new Error(`Unsupported direction ${direction}`); } - for (let i = 0; i < conflicts.length; i++) { - if (predicate(conflicts[i]) && !conflicts[i].range.contains(selection)) { + for (const conflict of conflicts) { + if (predicate(conflict) && !conflict.range.contains(selection)) { return { canNavigate: true, - conflict: conflicts[i] + conflict: conflict }; } } diff --git a/extensions/merge-conflict/src/delayer.ts b/extensions/merge-conflict/src/delayer.ts index 0e51e3ba6eb..3b8f8e01c51 100644 --- a/extensions/merge-conflict/src/delayer.ts +++ b/extensions/merge-conflict/src/delayer.ts @@ -35,7 +35,7 @@ export class Delayer<T> { }).then(() => { this.completionPromise = null; this.onSuccess = null; - var result = this.task!(); + let result = this.task!(); this.task = null; return result; }); diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index f70cd91f185..41be7a803e2 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -91,8 +91,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, return false; } - var task = this.cache.get(key); - + const task = this.cache.get(key); if (!task) { return false; } @@ -128,7 +127,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, } private getCacheKey(document: vscode.TextDocument): string | null { - if (document.uri && document.uri) { + if (document.uri) { return document.uri.toString(); } diff --git a/extensions/merge-conflict/src/mergeDecorator.ts b/extensions/merge-conflict/src/mergeDecorator.ts index 5d5c42b8a44..b3d24bed81d 100644 --- a/extensions/merge-conflict/src/mergeDecorator.ts +++ b/extensions/merge-conflict/src/mergeDecorator.ts @@ -153,10 +153,10 @@ export default class MergeDecorator implements vscode.Disposable { } private applyDecorationsFromEvent(eventDocument: vscode.TextDocument) { - for (var i = 0; i < vscode.window.visibleTextEditors.length; i++) { - if (vscode.window.visibleTextEditors[i].document === eventDocument) { + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document === eventDocument) { // Attempt to apply - this.applyDecorations(vscode.window.visibleTextEditors[i]); + this.applyDecorations(editor); } } } diff --git a/extensions/npm/package.json b/extensions/npm/package.json index b50486c6bb3..43dc6a21557 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -7,6 +7,7 @@ "engines": { "vscode": "0.10.x" }, + "enableProposedApi": true, "icon": "images/npm_icon.png", "categories": [ "Other" @@ -23,7 +24,7 @@ }, "devDependencies": { "@types/minimatch": "^3.0.3", - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index aec8fcbd44d..705e40d34e7 100644 --- a/extensions/npm/src/features/bowerJSONContribution.ts +++ b/extensions/npm/src/features/bowerJSONContribution.ts @@ -68,9 +68,9 @@ export class BowerJSONContribution implements IJSONContribution { const obj = JSON.parse(success.responseText); if (Array.isArray(obj)) { const results = <{ name: string; description: string; }[]>obj; - for (let i = 0; i < results.length; i++) { - const name = results[i].name; - const description = results[i].description || ''; + for (const result of results) { + const name = result.name; + const description = result.description || ''; const insertText = new SnippetString().appendText(JSON.stringify(name)); if (addValue) { insertText.appendText(': ').appendPlaceholder('latest'); @@ -167,7 +167,7 @@ export class BowerJSONContribution implements IJSONContribution { if (url.indexOf('git://') === 0) { url = url.substring(6); } - if (url.lastIndexOf('.git') === url.length - 4) { + if (url.length >= 4 && url.substr(url.length - 4) === '.git') { url = url.substring(0, url.length - 4); } return url; @@ -175,9 +175,9 @@ export class BowerJSONContribution implements IJSONContribution { } catch (e) { // ignore } - return void 0; + return undefined; }, () => { - return void 0; + return undefined; }); } diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 4ea9f87f2eb..88cf753425c 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -26,7 +26,7 @@ export class PackageJSONContribution implements IJSONContribution { 'shelljs', 'gulp', 'yargs', 'browserify', 'minimatch', 'react', 'less', 'prompt', 'inquirer', 'ws', 'event-stream', 'inherits', 'mysql', 'esprima', 'jsdom', 'stylus', 'when', 'readable-stream', 'aws-sdk', 'concat-stream', 'chai', 'Thenable', 'wrench']; - private knownScopes = ['@types', '@angular']; + private knownScopes = ['@types', '@angular', '@babel', '@nuxtjs', '@vue', '@bazel']; private xhr: XHRRequest; public getDocumentSelector(): DocumentSelector { @@ -87,8 +87,8 @@ export class PackageJSONContribution implements IJSONContribution { const obj = JSON.parse(success.responseText); if (obj && Array.isArray(obj.rows)) { const results = <{ key: string[]; }[]>obj.rows; - for (let i = 0; i < results.length; i++) { - const keys = results[i].key; + for (const result of results) { + const keys = result.key; if (Array.isArray(keys) && keys.length > 0) { const name = keys[0]; const insertText = new SnippetString().appendText(JSON.stringify(name)); @@ -163,7 +163,11 @@ export class PackageJSONContribution implements IJSONContribution { } } else if (segments.length === 2 && segments[0].length > 1) { let scope = segments[0].substr(1); - let queryUrl = `https://registry.npmjs.org/-/v1/search?text=scope:${scope}%20${segments[1]}&size=${SCOPED_LIMIT}&popularity=1.0`; + let name = segments[1]; + if (name.length < 4) { + name = ''; + } + let queryUrl = `https://api.npms.io/v2/search?q=scope:${scope}%20${name}&size=250`; return this.xhr({ url: queryUrl, agent: USER_AGENT @@ -171,8 +175,8 @@ export class PackageJSONContribution implements IJSONContribution { if (success.status === 200) { try { const obj = JSON.parse(success.responseText); - if (obj && Array.isArray(obj.objects)) { - const objects = <{ package: { name: string; version: string, description: string; } }[]>obj.objects; + if (obj && Array.isArray(obj.results)) { + const objects = <{ package: { name: string; version: string, description: string; } }[]>obj.results; for (let object of objects) { if (object.package && object.package.name) { const name = object.package.name; @@ -225,7 +229,7 @@ export class PackageJSONContribution implements IJSONContribution { 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') { - const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(currentKey).replace('%40', '@'); + const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(currentKey).replace(/%40/g, '@'); return this.xhr({ url: queryUrl, agent: USER_AGENT @@ -285,7 +289,7 @@ export class PackageJSONContribution implements IJSONContribution { private getInfo(pack: string): Thenable<string[]> { - const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace('%40', '@'); + const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace(/%40/g, '@'); return this.xhr({ url: queryUrl, agent: USER_AGENT diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index 5903d6ebb58..640c349334a 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -74,8 +74,8 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataProvider | undefined { if (vscode.workspace.workspaceFolders) { let treeDataProvider = new NpmScriptsTreeDataProvider(context); - let disposable = vscode.window.registerTreeDataProvider('npm', treeDataProvider); - context.subscriptions.push(disposable); + const view = vscode.window.createTreeView('npm', { treeDataProvider: treeDataProvider, showCollapseAll: true }); + context.subscriptions.push(view); return treeDataProvider; } return undefined; diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 15f26a13537..bfe9d8cd698 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -70,8 +70,7 @@ function getPrePostScripts(scripts: any): Set<string> { 'pretest', 'postest', 'prepublishOnly' ]); let keys = Object.keys(scripts); - for (let i = 0; i < keys.length; i++) { - const script = keys[i]; + for (const script of keys) { const prepost = ['pre' + script, 'post' + script]; prepost.forEach(each => { if (scripts[each] !== undefined) { @@ -96,8 +95,7 @@ export async function hasNpmScripts(): Promise<boolean> { return false; } try { - for (let i = 0; i < folders.length; i++) { - let folder = folders[i]; + for (const folder of folders) { if (isAutoDetectionEnabled(folder)) { let relativePattern = new RelativePattern(folder, '**/package.json'); let paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); @@ -123,13 +121,11 @@ async function detectNpmScripts(): Promise<Task[]> { return emptyTasks; } try { - for (let i = 0; i < folders.length; i++) { - let folder = folders[i]; + for (const folder of folders) { if (isAutoDetectionEnabled(folder)) { let relativePattern = new RelativePattern(folder, '**/package.json'); let paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); - for (let j = 0; j < paths.length; j++) { - let path = paths[j]; + for (const path of paths) { if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) { let tasks = await provideNpmScriptsForFolder(path); visitedPackageJsonFiles.add(path.fsPath); @@ -343,7 +339,7 @@ export function startDebugging(scriptName: string, protocol: string, port: numbe export type StringMap = { [s: string]: string; }; async function findAllScripts(buffer: string): Promise<StringMap> { - var scripts: StringMap = {}; + let scripts: StringMap = {}; let script: string | undefined = undefined; let inScripts = false; @@ -380,7 +376,7 @@ 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 scripts: Map<string, [number, number, string]> = new Map(); let script: string | undefined = undefined; let offset: number; let length: number; @@ -469,8 +465,8 @@ export async function getScripts(packageJsonUri: Uri): Promise<StringMap | undef } try { - var contents = await readFile(packageJson); - var json = findAllScripts(contents);//JSON.parse(contents); + let contents = await readFile(packageJson); + let json = findAllScripts(contents);//JSON.parse(contents); return json; } catch (e) { let localizedParseError = localize('npm.parseError', 'Npm task detection: failed to parse the file {0}', packageJsonUri.fsPath); diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index 3851d9a4b67..ca339ed8150 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== agent-base@4, agent-base@^4.1.0: version "4.2.0" diff --git a/extensions/objective-c/cgmanifest.json b/extensions/objective-c/cgmanifest.json index 58d211c8fa5..1b54a305d12 100644 --- a/extensions/objective-c/cgmanifest.json +++ b/extensions/objective-c/cgmanifest.json @@ -6,13 +6,13 @@ "git": { "name": "atom/language-objective-c", "repositoryUrl": "https://github.com/atom/language-objective-c", - "commitHash": "0727e04544f3414c1c339cf15a39a05ea3938cb4" + "commitHash": "7fdf0c40ec1d592a902ed6a7cf5565bdf12e2ae8" } }, "license": "MIT", "description": "The files syntaxes/objective-c.tmLanguage.json and syntaxes/objective-c++.tmLanguage.json were derived from the Atom package https://github.com/atom/language-objective-c which was originally converted from the TextMate bundle https://github.com/textmate/objective-c.tmbundle.", - "version": "0.0.0" + "version": "0.15.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/objective-c/test/colorize-results/test_m.json b/extensions/objective-c/test/colorize-results/test_m.json index 9a4926acad5..7245315d4db 100644 --- a/extensions/objective-c/test/colorize-results/test_m.json +++ b/extensions/objective-c/test/colorize-results/test_m.json @@ -1,7 +1,7 @@ [ { "c": "//", - "t": "source.objc comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.objc comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -12,7 +12,7 @@ }, { "c": "//", - "t": "source.objc comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.objc comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -23,7 +23,7 @@ }, { "c": " Copyright (c) Microsoft Corporation. All rights reserved.", - "t": "source.objc comment.line.double-slash.cpp", + "t": "source.objc comment.line.double-slash.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -34,7 +34,7 @@ }, { "c": "//", - "t": "source.objc comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.objc comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -298,7 +298,7 @@ }, { "c": "void", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc storage.type.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.function.objc meta.return-type.objc storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -639,7 +639,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -650,7 +650,7 @@ }, { "c": "NSDocumentDirectory", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.constant.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c support.constant.cocoa", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -661,7 +661,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -672,7 +672,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -683,7 +683,7 @@ }, { "c": "NSUserDomainMask", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.constant.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c support.constant.cocoa", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -694,7 +694,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -705,7 +705,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -716,7 +716,7 @@ }, { "c": "true", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.language.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c constant.language.c", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -727,7 +727,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -738,7 +738,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +749,7 @@ }, { "c": "0", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -760,7 +760,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +848,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -859,7 +859,7 @@ }, { "c": "NSOpenPanel", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.class.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c support.class.cocoa", "r": { "dark_plus": "support.class: #4EC9B0", "light_plus": "support.class: #267F99", @@ -870,7 +870,7 @@ }, { "c": " openPanel", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -881,7 +881,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -914,7 +914,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +925,7 @@ }, { "c": "panel setAllowedFileTypes:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -935,8 +935,19 @@ } }, { - "c": "[[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,7 +958,7 @@ }, { "c": "NSArray", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.class.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c support.class.cocoa", "r": { "dark_plus": "support.class: #4EC9B0", "light_plus": "support.class: #267F99", @@ -958,7 +969,7 @@ }, { "c": " alloc", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -969,7 +980,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -980,7 +991,7 @@ }, { "c": " initWithObjects:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -991,7 +1002,7 @@ }, { "c": "@\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.begin.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.begin.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1002,7 +1013,7 @@ }, { "c": "ipa", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1013,7 +1024,7 @@ }, { "c": "\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.end.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.end.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1024,7 +1035,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1035,7 +1046,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1046,7 +1057,7 @@ }, { "c": "@\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.begin.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.begin.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1057,7 +1068,7 @@ }, { "c": "xcarchive", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1068,7 +1079,7 @@ }, { "c": "\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.end.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.end.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1079,7 +1090,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1090,7 +1101,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1101,7 +1112,7 @@ }, { "c": "@\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.begin.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.begin.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1112,7 +1123,7 @@ }, { "c": "app", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1123,7 +1134,7 @@ }, { "c": "\"", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c string.quoted.double.objc punctuation.definition.string.end.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c string.quoted.double.objc punctuation.definition.string.end.objc", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1134,7 +1145,7 @@ }, { "c": ",", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.delimiter.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.delimiter.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1145,7 +1156,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1156,7 +1167,7 @@ }, { "c": "nil", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.language.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c constant.language.objc", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1166,8 +1177,19 @@ } }, { - "c": "]]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1200,7 +1222,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1211,7 +1233,7 @@ }, { "c": "panel beginWithCompletionHandler:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1222,7 +1244,7 @@ }, { "c": "^", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.operator.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c keyword.operator.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1233,7 +1255,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1244,7 +1266,7 @@ }, { "c": "NSInteger", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.type.cocoa.leopard", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c support.type.cocoa.leopard", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1255,7 +1277,7 @@ }, { "c": " result", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1266,7 +1288,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1277,7 +1299,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,7 +1310,7 @@ }, { "c": "{", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.block.begin.bracket.curly.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.block.begin.bracket.curly.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1299,7 +1321,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1310,7 +1332,7 @@ }, { "c": "if", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.control.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c keyword.control.c", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1321,7 +1343,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1332,7 +1354,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.begin.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.parens.block.c punctuation.section.parens.begin.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1343,7 +1365,7 @@ }, { "c": "result ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1354,7 +1376,7 @@ }, { "c": "==", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.operator.comparison.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.parens.block.c keyword.operator.comparison.c", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1365,7 +1387,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.parens.block.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1376,7 +1398,7 @@ }, { "c": "NSFileHandlingPanelOKButton", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.constant.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.parens.block.c support.constant.cocoa", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1387,7 +1409,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.parens.end.bracket.round.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.parens.block.c punctuation.section.parens.end.bracket.round.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1398,7 +1420,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1409,7 +1431,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1420,18 +1442,18 @@ }, { "c": "self", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c variable.other.object.access.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": ".", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.dot-access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1442,7 +1464,7 @@ }, { "c": "inputTextField", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.member.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c variable.other.member.c", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1453,7 +1475,7 @@ }, { "c": " setStringValue:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1464,7 +1486,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1475,18 +1497,18 @@ }, { "c": "panel", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c variable.other.object.access.c", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { "c": ".", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.separator.dot-access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1497,7 +1519,7 @@ }, { "c": "URL", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.member.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c variable.other.member.c", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1508,51 +1530,7 @@ }, { "c": " path", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "]]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ";", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.terminator.statement.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "}", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.section.block.end.bracket.curly.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1563,7 +1541,62 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.terminator.statement.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "}", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.section.block.end.bracket.curly.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "]", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1651,7 +1684,7 @@ }, { "c": "int", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1694,8 +1727,19 @@ } }, { - "c": "0xFEF1F0F", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "0x", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.other.unit.hexadecimal.c", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "FEF1F0F", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.hexadecimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1728,7 +1772,7 @@ }, { "c": "float", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c storage.type.built-in.primitive.c", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1771,8 +1815,30 @@ } }, { - "c": "3.14", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "3", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.point.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "14", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1826,8 +1892,52 @@ } }, { - "c": "3.14e0", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "3", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.point.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "14", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "e", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.other.unit.exponent.decimal.c", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "0", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.exponent.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1881,8 +1991,63 @@ } }, { - "c": "31.4e-2", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.c", + "c": "31", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": ".", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.point.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "4", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.decimal.c", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": "e", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.other.unit.exponent.decimal.c", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #09885A", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #09885A", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": "-", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c keyword.operator.minus.exponent.decimal.c", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "2", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c constant.numeric.exponent.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2212,7 +2377,7 @@ }, { "c": " ", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.whitespace.comment.leading.cpp", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.whitespace.comment.leading.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2223,7 +2388,7 @@ }, { "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", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.c punctuation.definition.comment.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -2234,7 +2399,7 @@ }, { "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", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.c", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -2299,8 +2464,19 @@ } }, { - "c": "[[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "[", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2311,7 +2487,7 @@ }, { "c": "UITapGestureRecognizer alloc", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2322,7 +2498,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2333,7 +2509,7 @@ }, { "c": " initWithTarget:self action:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2344,7 +2520,7 @@ }, { "c": "@", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc storage.type.objc punctuation.definition.storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc storage.type.objc punctuation.definition.storage.type.objc", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2355,7 +2531,7 @@ }, { "c": "selector", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc storage.type.objc", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2366,7 +2542,7 @@ }, { "c": "(", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc punctuation.definition.storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc punctuation.definition.storage.type.objc", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2377,7 +2553,7 @@ }, { "c": "handleTap:", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc meta.selector.method-name.objc support.function.any-method.name-of-parameter.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc meta.selector.method-name.objc support.function.any-method.name-of-parameter.objc", "r": { "dark_plus": "support.function: #DCDCAA", "light_plus": "support.function: #795E26", @@ -2388,7 +2564,7 @@ }, { "c": ")", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.selector.objc punctuation.definition.storage.type.objc", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c meta.selector.objc punctuation.definition.storage.type.objc", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2399,7 +2575,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2498,7 +2674,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2509,7 +2685,7 @@ }, { "c": "NSMutableArray", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c support.class.cocoa", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c support.class.cocoa", "r": { "dark_plus": "support.class: #4EC9B0", "light_plus": "support.class: #267F99", @@ -2520,7 +2696,7 @@ }, { "c": " array", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2531,7 +2707,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2564,7 +2740,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2575,7 +2751,7 @@ }, { "c": "gestureRecognizers addObject:tapGesture", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2586,7 +2762,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2619,7 +2795,7 @@ }, { "c": "[", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.begin.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.begin.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2629,8 +2805,8 @@ } }, { - "c": "gestureRecognizers addObjectsFromArray:scnView", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", + "c": "gestureRecognizers addObjectsFromArray:", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2639,9 +2815,20 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "scnView", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c variable.other.object.access.c", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ".", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.separator.dot-access.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2652,7 +2839,7 @@ }, { "c": "gestureRecognizers", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.member.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c variable.other.member.c", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2663,7 +2850,7 @@ }, { "c": "]", - "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.definition.end.bracket.square.c", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c meta.bracket.square.access.c punctuation.definition.end.bracket.square.c", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2684,7 +2871,7 @@ } }, { - "c": " scnView", + "c": " ", "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c", "r": { "dark_plus": "default: #D4D4D4", @@ -2694,6 +2881,17 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "scnView", + "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c variable.other.object.access.c", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, { "c": ".", "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c punctuation.separator.dot-access.c", diff --git a/extensions/package.json b/extensions/package.json index af0de2f6181..da7f9f2b326 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.2.0-rc" + "typescript": "3.5.0-dev.20190517" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/perl/cgmanifest.json b/extensions/perl/cgmanifest.json index 6338921e75f..83d91107671 100644 --- a/extensions/perl/cgmanifest.json +++ b/extensions/perl/cgmanifest.json @@ -30,4 +30,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index fb6beb6deb5..28ccad47def 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -80,6 +80,6 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^8.10.25" + "@types/node": "^10.12.21" } } diff --git a/extensions/php-language-features/src/features/completionItemProvider.ts b/extensions/php-language-features/src/features/completionItemProvider.ts index a6b813cf085..c57085c3fc8 100644 --- a/extensions/php-language-features/src/features/completionItemProvider.ts +++ b/extensions/php-language-features/src/features/completionItemProvider.ts @@ -17,8 +17,8 @@ export default class PHPCompletionItemProvider implements CompletionItemProvider return Promise.resolve(result); } - var range = document.getWordRangeAtPosition(position); - var prefix = range ? document.getText(range) : ''; + let range = document.getWordRangeAtPosition(position); + let prefix = range ? document.getText(range) : ''; if (!range) { range = new Range(position, position); } @@ -31,9 +31,9 @@ export default class PHPCompletionItemProvider implements CompletionItemProvider } } - var added: any = {}; - var createNewProposal = function (kind: CompletionItemKind, name: string, entry: phpGlobals.IEntry | null): CompletionItem { - var proposal: CompletionItem = new CompletionItem(name); + let added: any = {}; + let createNewProposal = function (kind: CompletionItemKind, name: string, entry: phpGlobals.IEntry | null): CompletionItem { + let proposal: CompletionItem = new CompletionItem(name); proposal.kind = kind; if (entry) { if (entry.description) { @@ -46,7 +46,7 @@ export default class PHPCompletionItemProvider implements CompletionItemProvider return proposal; }; - var matches = (name: string) => { + let matches = (name: string) => { return prefix.length === 0 || name.length >= prefix.length && name.substr(0, prefix.length) === prefix; }; @@ -62,47 +62,47 @@ export default class PHPCompletionItemProvider implements CompletionItemProvider } } - for (var globalvariables in phpGlobals.globalvariables) { + for (let globalvariables in phpGlobals.globalvariables) { if (phpGlobals.globalvariables.hasOwnProperty(globalvariables) && matches(globalvariables)) { added[globalvariables] = true; result.push(createNewProposal(CompletionItemKind.Variable, globalvariables, phpGlobals.globalvariables[globalvariables])); } } - for (var globalfunctions in phpGlobalFunctions.globalfunctions) { + for (let globalfunctions in phpGlobalFunctions.globalfunctions) { if (phpGlobalFunctions.globalfunctions.hasOwnProperty(globalfunctions) && matches(globalfunctions)) { added[globalfunctions] = true; result.push(createNewProposal(CompletionItemKind.Function, globalfunctions, phpGlobalFunctions.globalfunctions[globalfunctions])); } } - for (var compiletimeconstants in phpGlobals.compiletimeconstants) { + for (let compiletimeconstants in phpGlobals.compiletimeconstants) { if (phpGlobals.compiletimeconstants.hasOwnProperty(compiletimeconstants) && matches(compiletimeconstants)) { added[compiletimeconstants] = true; result.push(createNewProposal(CompletionItemKind.Field, compiletimeconstants, phpGlobals.compiletimeconstants[compiletimeconstants])); } } - for (var keywords in phpGlobals.keywords) { + for (let keywords in phpGlobals.keywords) { if (phpGlobals.keywords.hasOwnProperty(keywords) && matches(keywords)) { added[keywords] = true; result.push(createNewProposal(CompletionItemKind.Keyword, keywords, phpGlobals.keywords[keywords])); } } - var text = document.getText(); + let text = document.getText(); if (prefix[0] === '$') { - var variableMatch = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/g; - var match: RegExpExecArray | null = null; + let variableMatch = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/g; + let match: RegExpExecArray | null = null; while (match = variableMatch.exec(text)) { - var word = match[0]; + let word = match[0]; if (!added[word]) { added[word] = true; result.push(createNewProposal(CompletionItemKind.Variable, word, null)); } } } - var functionMatch = /function\s+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\(/g; - var match2: RegExpExecArray | null = null; + let functionMatch = /function\s+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\(/g; + let match2: RegExpExecArray | null = null; while (match2 = functionMatch.exec(text)) { - var word2 = match2[1]; + let word2 = match2[1]; if (!added[word2]) { added[word2] = true; result.push(createNewProposal(CompletionItemKind.Function, word2, null)); diff --git a/extensions/php-language-features/src/features/hoverProvider.ts b/extensions/php-language-features/src/features/hoverProvider.ts index a0cda8c198d..a79da4073a9 100644 --- a/extensions/php-language-features/src/features/hoverProvider.ts +++ b/extensions/php-language-features/src/features/hoverProvider.ts @@ -23,7 +23,7 @@ export default class PHPHoverProvider implements HoverProvider { let name = document.getText(wordRange); - var entry = phpGlobalFunctions.globalfunctions[name] || phpGlobals.compiletimeconstants[name] || phpGlobals.globalvariables[name] || phpGlobals.keywords[name]; + let entry = phpGlobalFunctions.globalfunctions[name] || phpGlobals.compiletimeconstants[name] || phpGlobals.globalvariables[name] || phpGlobals.keywords[name]; if (entry && entry.description) { let signature = name + (entry.signature || ''); let contents: MarkedString[] = [textToMarkedString(entry.description), { language: 'php', value: signature }]; diff --git a/extensions/php-language-features/src/features/phpGlobalFunctions.ts b/extensions/php-language-features/src/features/phpGlobalFunctions.ts index a85e2c6f254..ce2acbc4258 100644 --- a/extensions/php-language-features/src/features/phpGlobalFunctions.ts +++ b/extensions/php-language-features/src/features/phpGlobalFunctions.ts @@ -7,7 +7,7 @@ import { IEntries } from './phpGlobals'; -export var globalfunctions: IEntries = { +export const globalfunctions: IEntries = { debug_backtrace: { description: 'Generates a backtrace', signature: '([ int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT [, int $limit = 0 ]]): array' @@ -191,6 +191,10 @@ export var globalfunctions: IEntries = { description: 'Reclaims memory used by the Zend Engine memory manager', signature: '(void): int' }, + gc_status: { + description: 'Gets information about the garbage collector', + signature: '(void): array' + }, get_cfg_var: { description: 'Gets the value of a PHP configuration option', signature: '( string $option ): mixed' @@ -1036,7 +1040,7 @@ export var globalfunctions: IEntries = { signature: '( string $client_keypair , string $server_key ): array' }, sodium_crypto_kx_keypair: { - description: 'Description', + description: 'Creates a new sodium keypair', signature: '(void): string' }, sodium_crypto_kx_publickey: { @@ -1072,11 +1076,11 @@ export var globalfunctions: IEntries = { signature: '( string $password , int $opslimit , int $memlimit ): bool' }, sodium_crypto_pwhash_str_verify: { - description: 'Verify that hash is a valid password verification string', + description: 'Verifies that a password matches a hash', signature: '( string $hash , string $password ): bool' }, sodium_crypto_pwhash_str: { - description: 'Get an ASCII encoded hash', + description: 'Get an ASCII-encoded hash', signature: '( string $password , int $opslimit , int $memlimit ): string' }, sodium_crypto_pwhash: { @@ -1136,7 +1140,7 @@ export var globalfunctions: IEntries = { }, sodium_crypto_sign_detached: { description: 'Sign the message', - signature: '( string $msg , string $keypair ): string' + signature: '( string $msg , string $secretkey ): string' }, sodium_crypto_sign_ed25519_pk_to_curve25519: { description: 'Convert an Ed25519 public key to a Curve25519 public key', @@ -1156,7 +1160,7 @@ export var globalfunctions: IEntries = { }, sodium_crypto_sign_open: { description: 'Check that the signed message has a valid signature', - signature: '( string $string , string $keypair ): string' + signature: '( string $string , string $public_key ): string' }, sodium_crypto_sign_publickey_from_secretkey: { description: 'Extract the public key from the secret key', @@ -1176,7 +1180,7 @@ export var globalfunctions: IEntries = { }, sodium_crypto_sign_verify_detached: { description: 'Verify signature for the message', - signature: '( string $signature , string $msg , string $key ): bool' + signature: '( string $signature , string $msg , string $public_key ): bool' }, sodium_crypto_sign: { description: 'Sign a message', @@ -1195,7 +1199,7 @@ export var globalfunctions: IEntries = { signature: '( int $length , string $nonce , string $key ): string' }, sodium_hex2bin: { - description: 'Decode from hexadecimal', + description: 'Decodes a hexadecimally encoded binary string', signature: '( string $hex [, string $ignore ]): string' }, sodium_increment: { @@ -1440,7 +1444,7 @@ export var globalfunctions: IEntries = { }, timezone_offset_get: { description: 'Returns the timezone offset from GMT', - signature: '( DateTime $datetime , DateTimeZone $object ): int' + signature: '( DateTimeInterface $datetime , DateTimeZone $object ): int' }, timezone_transitions_get: { description: 'Returns all transitions for the timezone', @@ -1695,7 +1699,7 @@ export var globalfunctions: IEntries = { signature: '( string $filename [, bool $use_include_path [, resource $context [, int $offset = 0 [, int $maxlen ]]]]): string' }, file_put_contents: { - description: 'Write a string to a file', + description: 'Write data to a file', signature: '( string $filename , mixed $data [, int $flags = 0 [, resource $context ]]): int' }, file: { @@ -2317,7 +2321,7 @@ export var globalfunctions: IEntries = { }, datefmt_set_timezone: { description: 'Sets formatterʼs timezone', - signature: '( mixed $zone ): bool' + signature: '( mixed $zone , IntlDateFormatter $fmt ): bool' }, resourcebundle_count: { description: 'Get number of elements in the bundle', @@ -2371,6 +2375,14 @@ export var globalfunctions: IEntries = { description: 'Transliterate a string', signature: '( string $subject [, int $start [, int $end , mixed $transliterator ]]): string' }, + intl_get_error_code: { + description: 'Get the last error code', + signature: '(void): int' + }, + intl_get_error_message: { + description: 'Get description of the last error', + signature: '(void): string' + }, grapheme_extract: { description: 'Function to extract a sequence of default grapheme clusters from a text buffer, which must be encoded in UTF-8', signature: '( string $haystack , int $size [, int $extract_type [, int $start = 0 [, int $next ]]]): string' @@ -2409,24 +2421,16 @@ export var globalfunctions: IEntries = { }, idn_to_ascii: { description: 'Convert domain name to IDNA ASCII form', - signature: '( string $domain [, int $options = IDNA_DEFAULT [, int $variant = INTL_IDNA_VARIANT_2003 [, array $idna_info ]]]): string' + signature: '( string $domain [, int $options = IDNA_DEFAULT [, int $variant = INTL_IDNA_VARIANT_UTS46 [, array $idna_info ]]]): string' }, idn_to_utf8: { description: 'Convert domain name from IDNA ASCII to Unicode', - signature: '( string $domain [, int $options = IDNA_DEFAULT [, int $variant = INTL_IDNA_VARIANT_2003 [, array $idna_info ]]]): string' + signature: '( string $domain [, int $options = IDNA_DEFAULT [, int $variant = INTL_IDNA_VARIANT_UTS46 [, array $idna_info ]]]): string' }, intl_error_name: { description: 'Get symbolic name for a given error code', signature: '( int $error_code ): string' }, - intl_get_error_code: { - description: 'Get the last error code', - signature: '(void): int' - }, - intl_get_error_message: { - description: 'Get description of the last error', - signature: '(void): string' - }, intl_is_failure: { description: 'Check whether the given error code indicates failure', signature: '( int $error_code ): bool' @@ -2488,7 +2492,7 @@ export var globalfunctions: IEntries = { signature: '( string $pattern , string $string [, string $option = "msr" ]): bool' }, mb_ereg_replace_callback: { - description: 'Perform a regular expresssion seach and replace with multibyte support using a callback', + description: 'Perform a regular expression search and replace with multibyte support using a callback', signature: '( string $pattern , callable $callback , string $string [, string $option = "msr" ]): string' }, mb_ereg_replace: { @@ -2704,7 +2708,7 @@ export var globalfunctions: IEntries = { }, bcmod: { description: 'Get modulus of an arbitrary precision number', - signature: '( string $dividend , string $modulus [, int $scale ]): string' + signature: '( string $dividend , string $divisor [, int $scale = 0 ]): string' }, bcmul: { description: 'Multiply two arbitrary precision numbers', @@ -2719,8 +2723,8 @@ export var globalfunctions: IEntries = { signature: '( string $base , string $exponent , string $modulus [, int $scale = 0 ]): string' }, bcscale: { - description: 'Set default scale parameter for all bc math functions', - signature: '([ int $scale ]): int' + description: 'Set or get default scale parameter for all bc math functions', + signature: '( int $scale ): int' }, bcsqrt: { description: 'Get the square root of an arbitrary precision number', @@ -2860,11 +2864,11 @@ export var globalfunctions: IEntries = { }, max: { description: 'Find highest value', - signature: '( array $values , mixed $value1 , mixed $value2 [, mixed $... ]): string' + signature: '( array $values , mixed $value1 [, mixed $... ]): string' }, min: { description: 'Find lowest value', - signature: '( array $values , mixed $value1 , mixed $value2 [, mixed $... ]): string' + signature: '( array $values , mixed $value1 [, mixed $... ]): string' }, mt_getrandmax: { description: 'Show largest possible random value', @@ -3371,13 +3375,17 @@ export var globalfunctions: IEntries = { description: 'Syntax highlighting of a string', signature: '( string $str [, bool $return ]): mixed' }, + hrtime: { + description: 'Get the system\'s high resolution time', + signature: '([ bool $get_as_number ]): mixed' + }, ignore_user_abort: { description: 'Set whether a client disconnect should abort script execution', signature: '([ bool $value ]): int' }, pack: { description: 'Pack data into binary string', - signature: '( string $format [, mixed $args [, mixed $... ]]): string' + signature: '( string $format [, mixed $... ]): string' }, php_check_syntax: { description: 'Check the PHP syntax of (and execute) the specified file', @@ -3403,6 +3411,10 @@ export var globalfunctions: IEntries = { description: 'Set process codepage', signature: '( int $cp ): bool' }, + sapi_windows_vt100_support: { + description: 'Get or set VT100 support for the specified stream associated to an output buffer of a Windows console.', + signature: '( resource $stream [, bool $enable ]): bool' + }, show_source: { description: 'Alias of highlight_file', }, @@ -3545,10 +3557,6 @@ export var globalfunctions: IEntries = { description: 'Copies data from one stream to another', signature: '( resource $source , resource $dest [, int $maxlength = -1 [, int $offset = 0 ]]): int' }, - stream_encoding: { - description: 'Set character set for stream encoding', - signature: '( resource $stream [, string $encoding ]): bool' - }, stream_filter_append: { description: 'Attach a filter to a stream', signature: '( resource $stream , string $filtername [, int $read_write [, mixed $params ]]): resource' @@ -3701,7 +3709,7 @@ export var globalfunctions: IEntries = { signature: '( string $data ): string' }, get_headers: { - description: 'Fetches all the headers sent by the server in response to a HTTP request', + description: 'Fetches all the headers sent by the server in response to an HTTP request', signature: '( string $url [, int $format = 0 [, resource $context ]]): array' }, get_meta_tags: { @@ -3756,6 +3764,10 @@ export var globalfunctions: IEntries = { description: 'Perform a cURL session', signature: '( resource $ch ): mixed' }, + curl_file_create: { + description: 'Create a CURLFile object', + signature: '( string $filename [, string $mimetype [, string $postname ]]): CURLFile' + }, curl_getinfo: { description: 'Get information regarding a specific transfer', signature: '( resource $ch [, int $opt ]): mixed' @@ -3856,16 +3868,12 @@ export var globalfunctions: IEntries = { description: 'Gets cURL version information', signature: '([ int $age = CURLVERSION_NOW ]): array' }, - curl_file_create: { - description: 'Create a CURLFile object', - signature: '( string $filename [, string $mimetype [, string $postname ]]): CURLFile' - }, ftp_alloc: { description: 'Allocates space for a file to be uploaded', signature: '( resource $ftp_stream , int $filesize [, string $result ]): bool' }, ftp_append: { - description: 'Append content of a file a another file on the FTP server', + description: 'Append the contents of a file to another file on the FTP server', signature: '( resource $ftp , string $remote_file , string $local_file [, int $mode ]): bool' }, ftp_cdup: { @@ -4115,11 +4123,11 @@ export var globalfunctions: IEntries = { }, setcookie: { description: 'Send a cookie', - signature: '( string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure [, bool $httponly ]]]]]]): bool' + signature: '( string $name [, string $value = "" [, int $expires = 0 [, string $path = "" [, string $domain = "" [, bool $secure [, bool $httponly [, array $options = [] ]]]]]]]): bool' }, setrawcookie: { description: 'Send a cookie without urlencoding the cookie value', - signature: '( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure [, bool $httponly ]]]]]]): bool' + signature: '( string $name [, string $value [, int $expires = 0 [, string $path [, string $domain [, bool $secure [, bool $httponly [, array $options = [] ]]]]]]]): bool' }, socket_get_status: { description: 'Alias of stream_get_meta_data', @@ -4231,7 +4239,7 @@ export var globalfunctions: IEntries = { }, socket_recvmsg: { description: 'Read a message', - signature: '( resource $socket , string $message [, int $flags = 0 ]): int' + signature: '( resource $socket , array $message [, int $flags = 0 ]): int' }, socket_select: { description: 'Runs the select() system call on the given arrays of sockets with a specified timeout', @@ -4413,7 +4421,7 @@ export var globalfunctions: IEntries = { }, session_set_cookie_params: { description: 'Set the session cookie parameters', - signature: '( int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly ]]]]): bool' + signature: '( int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly , array $options ]]]]): bool' }, session_set_save_handler: { description: 'Sets user-level session storage functions', @@ -4495,8 +4503,8 @@ export var globalfunctions: IEntries = { description: 'Alias of rtrim', }, chr: { - description: 'Return a specific character', - signature: '( int $ascii ): string' + description: 'Generate a single-byte string from a number', + signature: '( int $bytevalue ): string' }, chunk_split: { description: 'Split a string into smaller chunks', @@ -4536,7 +4544,7 @@ export var globalfunctions: IEntries = { }, fprintf: { description: 'Write a formatted string to a stream', - signature: '( resource $handle , string $format [, mixed $args [, mixed $... ]]): int' + signature: '( resource $handle , string $format [, mixed $... ]): int' }, get_html_translation_table: { description: 'Returns the translation table used by htmlspecialchars and htmlentities', @@ -4555,7 +4563,7 @@ export var globalfunctions: IEntries = { signature: '( string $data ): string' }, html_entity_decode: { - description: 'Convert all HTML entities to their applicable characters', + description: 'Convert HTML entities to their corresponding characters', signature: '( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") ]]): string' }, htmlentities: { @@ -4622,7 +4630,7 @@ export var globalfunctions: IEntries = { signature: '( float $number , int $decimals = 0 , string $dec_point = "." , string $thousands_sep = "," ): string' }, ord: { - description: 'Return ASCII value of character', + description: 'Convert the first byte of a string to a value between 0 and 255', signature: '( string $string ): int' }, parse_str: { @@ -4635,7 +4643,7 @@ export var globalfunctions: IEntries = { }, printf: { description: 'Output a formatted string', - signature: '( string $format [, mixed $args [, mixed $... ]]): int' + signature: '( string $format [, mixed $... ]): int' }, quoted_printable_decode: { description: 'Convert a quoted-printable string to an 8 bit string', @@ -4675,7 +4683,7 @@ export var globalfunctions: IEntries = { }, sprintf: { description: 'Return a formatted string', - signature: '( string $format [, mixed $args [, mixed $... ]]): string' + signature: '( string $format [, mixed $... ]): string' }, sscanf: { description: 'Parses input from a string according to a format', @@ -4746,7 +4754,7 @@ export var globalfunctions: IEntries = { }, stripos: { description: 'Find the position of the first occurrence of a case-insensitive substring in a string', - signature: '( string $haystack , string $needle [, int $offset = 0 ]): int' + signature: '( string $haystack , mixed $needle [, int $offset = 0 ]): int' }, stripslashes: { description: 'Un-quotes a quoted string', @@ -4794,11 +4802,11 @@ export var globalfunctions: IEntries = { }, strripos: { description: 'Find the position of the last occurrence of a case-insensitive substring in a string', - signature: '( string $haystack , string $needle [, int $offset = 0 ]): int' + signature: '( string $haystack , mixed $needle [, int $offset = 0 ]): int' }, strrpos: { description: 'Find the position of the last occurrence of a substring in a string', - signature: '( string $haystack , string $needle [, int $offset = 0 ]): int' + signature: '( string $haystack , mixed $needle [, int $offset = 0 ]): int' }, strspn: { description: 'Finds the length of the initial segment of a string consisting entirely of characters contained within a given mask', @@ -4948,16 +4956,24 @@ export var globalfunctions: IEntries = { description: 'Checks if the given key or index exists in the array', signature: '( mixed $key , array $array ): bool' }, + array_key_first: { + description: 'Gets the first key of an array', + signature: '( array $array ): mixed' + }, + array_key_last: { + description: 'Gets the last key of an array', + signature: '( array $array ): mixed' + }, array_keys: { description: 'Return all the keys or a subset of the keys of an array', - signature: '( array $array [, mixed $search_value [, bool $strict ]]): array' + signature: '( array $array , mixed $search_value [, bool $strict ]): array' }, array_map: { description: 'Applies the callback to the elements of the given arrays', signature: '( callable $callback , array $array1 [, array $... ]): array' }, array_merge_recursive: { - description: 'Merge two or more arrays recursively', + description: 'Merge one or more arrays recursively', signature: '( array $array1 [, array $... ]): array' }, array_merge: { @@ -4985,7 +5001,7 @@ export var globalfunctions: IEntries = { signature: '( array $array [, mixed $... ]): int' }, array_rand: { - description: 'Pick one or more random entries out of an array', + description: 'Pick one or more random keys out of an array', signature: '( array $array [, int $num = 1 ]): mixed' }, array_reduce: { @@ -4994,11 +5010,11 @@ export var globalfunctions: IEntries = { }, array_replace_recursive: { description: 'Replaces elements from passed arrays into the first array recursively', - signature: '( array $array1 , array $array2 [, array $... ]): array' + signature: '( array $array1 [, array $... ]): array' }, array_replace: { description: 'Replaces elements from passed arrays into the first array', - signature: '( array $array1 , array $array2 [, array $... ]): array' + signature: '( array $array1 [, array $... ]): array' }, array_reverse: { description: 'Return an array with elements in reverse order', @@ -5191,7 +5207,7 @@ export var globalfunctions: IEntries = { }, call_user_method: { description: 'Call a user method on an specific object', - signature: '( string $method_name , object $obj [, mixed $parameter [, mixed $... ]]): mixed' + signature: '( string $method_name , object $obj [, mixed $... ]): mixed' }, class_alias: { description: 'Creates an alias for a class', @@ -5243,7 +5259,7 @@ export var globalfunctions: IEntries = { }, is_a: { description: 'Checks if the object is of this class or has this class as one of its parents', - signature: '( object $object , string $class_name [, bool $allow_string ]): bool' + signature: '( mixed $object , string $class_name [, bool $allow_string ]): bool' }, is_subclass_of: { description: 'Checks if the object has this class as one of its parents or implements it', @@ -5339,7 +5355,7 @@ export var globalfunctions: IEntries = { }, call_user_func: { description: 'Call the callback given by the first parameter', - signature: '( callable $callback [, mixed $parameter [, mixed $... ]]): mixed' + signature: '( callable $callback [, mixed $... ]): mixed' }, create_function: { description: 'Create an anonymous (lambda-style) function', @@ -5351,7 +5367,7 @@ export var globalfunctions: IEntries = { }, forward_static_call: { description: 'Call a static method', - signature: '( callable $function [, mixed $parameter [, mixed $... ]]): mixed' + signature: '( callable $function [, mixed $... ]): mixed' }, func_get_arg: { description: 'Return an item from the argument list', @@ -5375,15 +5391,15 @@ export var globalfunctions: IEntries = { }, register_shutdown_function: { description: 'Register a function for execution on shutdown', - signature: '( callable $callback [, mixed $parameter [, mixed $... ]]): void' + signature: '( callable $callback [, mixed $... ]): void' }, register_tick_function: { description: 'Register a function for execution on each tick', - signature: '( callable $function [, mixed $arg [, mixed $... ]]): bool' + signature: '( callable $function [, mixed $... ]): bool' }, unregister_tick_function: { description: 'De-register a function for execution on each tick', - signature: '( string $function_name ): void' + signature: '( callable $function ): void' }, boolval: { description: 'Get the boolean value of a variable', @@ -5436,6 +5452,10 @@ export var globalfunctions: IEntries = { description: 'Verify that the contents of a variable can be called as a function', signature: '( mixed $var [, bool $syntax_only [, string $callable_name ]]): bool' }, + is_countable: { + description: 'Verify that the contents of a variable is a countable value', + signature: '( mixed $var ): array' + }, is_double: { description: 'Alias of is_float', }, @@ -5485,7 +5505,7 @@ export var globalfunctions: IEntries = { signature: '( mixed $var ): bool' }, isset: { - description: 'Determine if a variable is set and is not NULL', + description: 'Determine if a variable is declared and is different than NULL', signature: '( mixed $var [, mixed $... ]): bool' }, print_r: { @@ -5590,7 +5610,7 @@ export var globalfunctions: IEntries = { }, com_load_typelib: { description: 'Loads a Typelib', - signature: '( string $typelib_name [, bool $case_insensitive ]): bool' + signature: '( string $typelib_name [, bool $case_sensitive ]): bool' }, com_message_pump: { description: 'Process COM messages, sleeping for up to timeoutms milliseconds', diff --git a/extensions/php-language-features/src/features/phpGlobals.ts b/extensions/php-language-features/src/features/phpGlobals.ts index 47dac98d8d3..cdd14068fb5 100644 --- a/extensions/php-language-features/src/features/phpGlobals.ts +++ b/extensions/php-language-features/src/features/phpGlobals.ts @@ -8,7 +8,7 @@ export interface IEntry { description?: string; signature?: string; } export interface IEntries { [name: string]: IEntry; } -export var globalvariables: IEntries = { +export const globalvariables: IEntries = { $GLOBALS: { description: 'An associative array containing references to all variables which are currently defined in the global scope of the script. The variable names are the keys of the array.', }, @@ -55,7 +55,7 @@ export var globalvariables: IEntries = { description: 'Refers to the current object', }, }; -export var compiletimeconstants: IEntries = { +export const compiletimeconstants: IEntries = { __CLASS__: { description: 'The class name. (Added in PHP 4.3.0) As of PHP 5 this constant returns the class name as it was declared (case-sensitive). In PHP 4 its value is always lowercased.', }, @@ -159,7 +159,7 @@ export var compiletimeconstants: IEntries = { description: 'The default algorithm to use for hashing if no algorithm is provided. This may change in newer PHP releases when newer, stronger hashing algorithms are supported.', }, }; -export var keywords: IEntries = { +export const keywords: IEntries = { define: { description: 'Defines a named constant at runtime.', signature: '( string $name , mixed $value [, bool $case_insensitive = false ] ): bool' diff --git a/extensions/php-language-features/src/features/signatureHelpProvider.ts b/extensions/php-language-features/src/features/signatureHelpProvider.ts index e4b9b28b847..09db65929da 100644 --- a/extensions/php-language-features/src/features/signatureHelpProvider.ts +++ b/extensions/php-language-features/src/features/signatureHelpProvider.ts @@ -7,27 +7,27 @@ import { SignatureHelpProvider, SignatureHelp, SignatureInformation, Cancellatio import phpGlobals = require('./phpGlobals'); import phpGlobalFunctions = require('./phpGlobalFunctions'); -var _NL = '\n'.charCodeAt(0); -var _TAB = '\t'.charCodeAt(0); -var _WSB = ' '.charCodeAt(0); -var _LBracket = '['.charCodeAt(0); -var _RBracket = ']'.charCodeAt(0); -var _LCurly = '{'.charCodeAt(0); -var _RCurly = '}'.charCodeAt(0); -var _LParent = '('.charCodeAt(0); -var _RParent = ')'.charCodeAt(0); -var _Comma = ','.charCodeAt(0); -var _Quote = '\''.charCodeAt(0); -var _DQuote = '"'.charCodeAt(0); -var _USC = '_'.charCodeAt(0); -var _a = 'a'.charCodeAt(0); -var _z = 'z'.charCodeAt(0); -var _A = 'A'.charCodeAt(0); -var _Z = 'Z'.charCodeAt(0); -var _0 = '0'.charCodeAt(0); -var _9 = '9'.charCodeAt(0); +const _NL = '\n'.charCodeAt(0); +const _TAB = '\t'.charCodeAt(0); +const _WSB = ' '.charCodeAt(0); +const _LBracket = '['.charCodeAt(0); +const _RBracket = ']'.charCodeAt(0); +const _LCurly = '{'.charCodeAt(0); +const _RCurly = '}'.charCodeAt(0); +const _LParent = '('.charCodeAt(0); +const _RParent = ')'.charCodeAt(0); +const _Comma = ','.charCodeAt(0); +const _Quote = '\''.charCodeAt(0); +const _DQuote = '"'.charCodeAt(0); +const _USC = '_'.charCodeAt(0); +const _a = 'a'.charCodeAt(0); +const _z = 'z'.charCodeAt(0); +const _A = 'A'.charCodeAt(0); +const _Z = 'Z'.charCodeAt(0); +const _0 = '0'.charCodeAt(0); +const _9 = '9'.charCodeAt(0); -var BOF = 0; +const BOF = 0; class BackwardIterator { @@ -58,7 +58,7 @@ class BackwardIterator { this.lineNumber = -1; return BOF; } - var ch = this.line.charCodeAt(this.offset); + let ch = this.line.charCodeAt(this.offset); this.offset--; return ch; } @@ -74,27 +74,27 @@ export default class PHPSignatureHelpProvider implements SignatureHelpProvider { return null; } - var iterator = new BackwardIterator(document, position.character - 1, position.line); + let iterator = new BackwardIterator(document, position.character - 1, position.line); - var paramCount = this.readArguments(iterator); + let paramCount = this.readArguments(iterator); if (paramCount < 0) { return null; } - var ident = this.readIdent(iterator); + let ident = this.readIdent(iterator); if (!ident) { return null; } - var entry = phpGlobalFunctions.globalfunctions[ident] || phpGlobals.keywords[ident]; + let entry = phpGlobalFunctions.globalfunctions[ident] || phpGlobals.keywords[ident]; if (!entry || !entry.signature) { return null; } - var paramsString = entry.signature.substring(0, entry.signature.lastIndexOf(')') + 1); + let paramsString = entry.signature.substring(0, entry.signature.lastIndexOf(')') + 1); let signatureInfo = new SignatureInformation(ident + paramsString, entry.description); - var re = /\w*\s+\&?\$[\w_\.]+|void/g; - var match: RegExpExecArray | null = null; + let re = /\w*\s+\&?\$[\w_\.]+|void/g; + let match: RegExpExecArray | null = null; while ((match = re.exec(paramsString)) !== null) { signatureInfo.parameters.push({ label: match[0], documentation: '' }); } @@ -106,12 +106,12 @@ export default class PHPSignatureHelpProvider implements SignatureHelpProvider { } private readArguments(iterator: BackwardIterator): number { - var parentNesting = 0; - var bracketNesting = 0; - var curlyNesting = 0; - var paramCount = 0; + let parentNesting = 0; + let bracketNesting = 0; + let curlyNesting = 0; + let paramCount = 0; while (iterator.hasNext()) { - var ch = iterator.next(); + let ch = iterator.next(); switch (ch) { case _LParent: parentNesting--; @@ -153,10 +153,10 @@ export default class PHPSignatureHelpProvider implements SignatureHelpProvider { } private readIdent(iterator: BackwardIterator): string { - var identStarted = false; - var ident = ''; + let identStarted = false; + let ident = ''; while (iterator.hasNext()) { - var ch = iterator.next(); + let ch = iterator.next(); if (!identStarted && (ch === _WSB || ch === _TAB || ch === _NL)) { continue; } diff --git a/extensions/php-language-features/src/features/utils/async.ts b/extensions/php-language-features/src/features/utils/async.ts index 78ba0aa582a..f590e7d0014 100644 --- a/extensions/php-language-features/src/features/utils/async.ts +++ b/extensions/php-language-features/src/features/utils/async.ts @@ -44,10 +44,10 @@ export class Throttler<T> { this.queuedPromiseFactory = promiseFactory; if (!this.queuedPromise) { - var onComplete = () => { + let onComplete = () => { this.queuedPromise = null; - var result = this.queue(this.queuedPromiseFactory!); + let result = this.queue(this.queuedPromiseFactory!); this.queuedPromiseFactory = null; return result; @@ -127,7 +127,7 @@ export class Delayer<T> { this.completionPromise = null; this.onResolve = null; - var result = this.task!(); + let result = this.task!(); this.task = null; return result; diff --git a/extensions/php-language-features/src/features/validationProvider.ts b/extensions/php-language-features/src/features/validationProvider.ts index 85b2bb652f9..6198974da88 100644 --- a/extensions/php-language-features/src/features/validationProvider.ts +++ b/extensions/php-language-features/src/features/validationProvider.ts @@ -23,20 +23,20 @@ export class LineDecoder { } public write(buffer: Buffer): string[] { - var result: string[] = []; - var value = this.remaining + let result: string[] = []; + let value = this.remaining ? this.remaining + this.stringDecoder.write(buffer) : this.stringDecoder.write(buffer); if (value.length < 1) { return result; } - var start = 0; - var ch: number; + let start = 0; + let ch: number; while (start < value.length && ((ch = value.charCodeAt(start)) === 13 || ch === 10)) { start++; } - var idx = start; + let idx = start; while (idx < value.length) { ch = value.charCodeAt(idx); if (ch === 13 || ch === 10) { @@ -146,7 +146,7 @@ export default class PHPValidationProvider { } this.trigger = RunTrigger.from(section.get<string>('validate.run', RunTrigger.strings.onSave)); } - if (this.executableIsUserDefined !== true && this.workspaceStore.get<string | undefined>(CheckedExecutablePath, undefined) !== void 0) { + if (this.executableIsUserDefined !== true && this.workspaceStore.get<string | undefined>(CheckedExecutablePath, undefined) !== undefined) { vscode.commands.executeCommand('setContext', 'php.untrustValidationExecutableContext', true); } this.delayers = Object.create(null); @@ -195,7 +195,7 @@ export default class PHPValidationProvider { delayer.trigger(() => this.doValidate(textDocument)); }; - if (this.executableIsUserDefined !== void 0 && !this.executableIsUserDefined) { + if (this.executableIsUserDefined !== undefined && !this.executableIsUserDefined) { let checkedExecutablePath = this.workspaceStore.get<string | undefined>(CheckedExecutablePath, undefined); if (!checkedExecutablePath || checkedExecutablePath !== this.executable) { vscode.window.showInformationMessage<MessageItem>( diff --git a/extensions/php-language-features/yarn.lock b/extensions/php-language-features/yarn.lock index 5e39a356be6..1bcd757b8a1 100644 --- a/extensions/php-language-features/yarn.lock +++ b/extensions/php-language-features/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/php/build/update-grammar.js b/extensions/php/build/update-grammar.js index 22e0a05d988..a6bf6d52721 100644 --- a/extensions/php/build/update-grammar.js +++ b/extensions/php/build/update-grammar.js @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -var updateGrammar = require('../../../build/npm/update-grammar'); +const updateGrammar = require('../../../build/npm/update-grammar'); function adaptInjectionScope(grammar) { // we're using the HTML grammar from https://github.com/textmate/html.tmbundle which has moved away from source.js.embedded.html - let oldInjectionKey = "text.html.php - (meta.embedded | meta.tag), L:text.html.php meta.tag, L:source.js.embedded.html"; - let newInjectionKey = "text.html.php - (meta.embedded | meta.tag), L:text.html.php meta.tag, L:text.html.php source.js"; + const oldInjectionKey = "text.html.php - (meta.embedded | meta.tag), L:((text.html.php meta.tag) - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.js.embedded.html - (meta.embedded.block.php | meta.embedded.line.php))"; + const newInjectionKey = "text.html.php - (meta.embedded | meta.tag), L:((text.html.php meta.tag) - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.js - (meta.embedded.block.php | meta.embedded.line.php))"; - var injections = grammar.injections; - var injection = injections[oldInjectionKey]; - if (!injections) { - throw new Error("Can not find PHP injection"); + const injections = grammar.injections; + const injection = injections[oldInjectionKey]; + if (!injection) { + throw new Error("Can not find PHP injection to patch"); } delete injections[oldInjectionKey]; injections[newInjectionKey] = injection; @@ -37,4 +37,3 @@ function fixBadRegex(grammar) { updateGrammar.update('atom/language-php', 'grammars/php.cson', './syntaxes/php.tmLanguage.json', fixBadRegex); updateGrammar.update('atom/language-php', 'grammars/html.cson', './syntaxes/html.tmLanguage.json', adaptInjectionScope); - diff --git a/extensions/php/cgmanifest.json b/extensions/php/cgmanifest.json index 63002fc566c..669ade1d4cb 100644 --- a/extensions/php/cgmanifest.json +++ b/extensions/php/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "language-php", "repositoryUrl": "https://github.com/atom/language-php", - "commitHash": "b6c5e83016b52311cdc622c2579462861ee91587" + "commitHash": "b896ebfb6f669b8714f419527f047466420efe5c" } }, "license": "MIT", - "version": "0.0.0" + "version": "0.44.1" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/php/snippets/php.snippets.json b/extensions/php/snippets/php.snippets.json index 1f2a07bef20..a0705e3aed4 100644 --- a/extensions/php/snippets/php.snippets.json +++ b/extensions/php/snippets/php.snippets.json @@ -254,8 +254,8 @@ "body": [ "try {", "\t${1://code...}", - "} catch (${2:\\Throwable} ${3:$$th}) {", - "\t${4://throw $$th;}", + "} catch (${2:\\Throwable} ${3:\\$th}) {", + "\t${4://throw \\$th;}", "}" ], "description": "Try catch block" diff --git a/extensions/php/syntaxes/html.tmLanguage.json b/extensions/php/syntaxes/html.tmLanguage.json index 809b1120530..6a37157d024 100644 --- a/extensions/php/syntaxes/html.tmLanguage.json +++ b/extensions/php/syntaxes/html.tmLanguage.json @@ -8,13 +8,6 @@ "name": "PHP", "scopeName": "text.html.php", "injections": { - "text.html.php - (meta.embedded | meta.tag), L:((text.html.php meta.tag) - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.js.embedded.html - (meta.embedded.block.php | meta.embedded.line.php))": { - "patterns": [ - { - "include": "#php-tag" - } - ] - }, "L:source.php string.quoted.single.sql.php source.sql.embedded.php": { "patterns": [ { @@ -115,6 +108,13 @@ "include": "source.php#interpolation_double_quoted" } ] + }, + "text.html.php - (meta.embedded | meta.tag), L:((text.html.php meta.tag) - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.js - (meta.embedded.block.php | meta.embedded.line.php))": { + "patterns": [ + { + "include": "#php-tag" + } + ] } }, "patterns": [ diff --git a/extensions/php/syntaxes/php.tmLanguage.json b/extensions/php/syntaxes/php.tmLanguage.json index 62153a2b04b..bb4c134fbfc 100644 --- a/extensions/php/syntaxes/php.tmLanguage.json +++ b/extensions/php/syntaxes/php.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-php/commit/13842b6dfb4568f76e8fa0c08dbff74347ee13c6", + "version": "https://github.com/atom/language-php/commit/b896ebfb6f669b8714f419527f047466420efe5c", "scopeName": "source.php", "patterns": [ { @@ -2696,7 +2696,7 @@ "name": "support.function.com.php" }, { - "begin": "(?i)\\b(isset|unset|eval|empty|list)\\b", + "match": "(?i)\\b(isset|unset|eval|empty|list)\\b", "name": "support.function.construct.php" }, { diff --git a/extensions/php/test/colorize-results/issue-28354_php.json b/extensions/php/test/colorize-results/issue-28354_php.json index 329c9d6f998..3a34ef9deb8 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -1,5 +1,4 @@ -[ - { +[{ "c": "<", "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { @@ -66,41 +65,19 @@ } }, { - "c": "<", - "t": "text.html.php meta.embedded.block.html source.js keyword.operator.relational.js", + "c": "<?php", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php punctuation.section.embedded.begin.php", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "?", - "t": "text.html.php meta.embedded.block.html source.js keyword.operator.ternary.js", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "php", - "t": "text.html.php meta.embedded.block.html source.js variable.other.readwrite.js", - "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "meta.embedded: #D4D4D4", - "light_vs": "meta.embedded: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "punctuation.section.embedded.begin.php: #569CD6", + "light_plus": "punctuation.section.embedded.begin.php: #800000", + "dark_vs": "punctuation.section.embedded.begin.php: #569CD6", + "light_vs": "punctuation.section.embedded.begin.php: #800000", + "hc_black": "punctuation.section.embedded: #569CD6" } }, { "c": " ", - "t": "text.html.php meta.embedded.block.html source.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -111,18 +88,18 @@ }, { "c": "foreach", - "t": "text.html.php meta.embedded.block.html source.js meta.function-call.js entity.name.function.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php keyword.control.foreach.php", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.embedded: #D4D4D4", - "light_vs": "meta.embedded: #000000", - "hc_black": "entity.name.function: #DCDCAA" + "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": "text.html.php meta.embedded.block.html source.js meta.brace.round.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.definition.begin.bracket.round.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -132,8 +109,19 @@ } }, { - "c": "$actID", - "t": "text.html.php meta.embedded.block.html source.js variable.other.readwrite.js", + "c": "$", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php variable.other.php punctuation.definition.variable.php", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "actID", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php variable.other.php", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -144,7 +132,7 @@ }, { "c": " ", - "t": "text.html.php meta.embedded.block.html source.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -155,18 +143,18 @@ }, { "c": "AS", - "t": "text.html.php meta.embedded.block.html source.js variable.other.constant.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php keyword.operator.logical.php", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "meta.embedded: #D4D4D4", - "light_vs": "meta.embedded: #000000", - "hc_black": "variable: #9CDCFE" + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" } }, { "c": " ", - "t": "text.html.php meta.embedded.block.html source.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -176,8 +164,19 @@ } }, { - "c": "$act", - "t": "text.html.php meta.embedded.block.html source.js variable.other.readwrite.js", + "c": "$", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php variable.other.php punctuation.definition.variable.php", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "act", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php variable.other.php", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -188,7 +187,7 @@ }, { "c": ")", - "t": "text.html.php meta.embedded.block.html source.js meta.brace.round.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.definition.end.bracket.round.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -199,7 +198,7 @@ }, { "c": " ", - "t": "text.html.php meta.embedded.block.html source.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -210,7 +209,7 @@ }, { "c": "{", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js punctuation.definition.block.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.definition.begin.bracket.curly.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -220,8 +219,30 @@ } }, { - "c": " echo ", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js", + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "echo", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php support.function.construct.output.php", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -232,7 +253,7 @@ }, { "c": "'", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.begin.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php punctuation.definition.string.begin.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -243,7 +264,7 @@ }, { "c": "divNames.push(", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -254,7 +275,7 @@ }, { "c": "\\'", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js constant.character.escape.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #FF0000", @@ -265,7 +286,7 @@ }, { "c": "[nid=", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -276,7 +297,7 @@ }, { "c": "'", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.end.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php punctuation.definition.string.end.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -286,19 +307,52 @@ } }, { - "c": ".$act.", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "c": ".", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php keyword.operator.string.php", "r": { - "dark_plus": "meta.object-literal.key: #9CDCFE", - "light_plus": "meta.object-literal.key: #001080", + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "$", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php variable.other.php punctuation.definition.variable.php", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.object-literal.key: #9CDCFE" + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "act", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php variable.other.php", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php keyword.operator.string.php", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" } }, { "c": "'", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.begin.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php punctuation.definition.string.begin.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -309,7 +363,7 @@ }, { "c": "]", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -320,7 +374,7 @@ }, { "c": "\\'", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js constant.character.escape.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php constant.character.escape.php", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #FF0000", @@ -331,7 +385,7 @@ }, { "c": ");", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -342,7 +396,7 @@ }, { "c": "'", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js string.quoted.single.js punctuation.definition.string.end.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php string.quoted.single.php punctuation.definition.string.end.php", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -353,57 +407,134 @@ }, { "c": ";", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.terminator.expression.php", "r": { - "dark_plus": "meta.object-literal.key: #9CDCFE", - "light_plus": "meta.object-literal.key: #001080", + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.object-literal.key: #9CDCFE" + "hc_black": "meta.embedded: #FFFFFF" } }, { - "c": " }", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", "r": { - "dark_plus": "meta.object-literal.key: #9CDCFE", - "light_plus": "meta.object-literal.key: #001080", + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.object-literal.key: #9CDCFE" + "hc_black": "meta.embedded: #FFFFFF" } }, { - "c": " ?>", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "c": "}", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php punctuation.definition.end.bracket.curly.php", "r": { - "dark_plus": "meta.object-literal.key: #9CDCFE", - "light_plus": "meta.object-literal.key: #001080", + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.object-literal.key: #9CDCFE" + "hc_black": "meta.embedded: #FFFFFF" } }, { - "c": " ...", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", "r": { - "dark_plus": "meta.object-literal.key: #9CDCFE", - "light_plus": "meta.object-literal.key: #001080", + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.object-literal.key: #9CDCFE" + "hc_black": "meta.embedded: #FFFFFF" } }, { - "c": "</script>", - "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", + "c": "?", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php punctuation.section.embedded.end.php source.php", "r": { - "dark_plus": "meta.object-literal.key: #9CDCFE", - "light_plus": "meta.object-literal.key: #001080", + "dark_plus": "punctuation.section.embedded.end.php: #569CD6", + "light_plus": "punctuation.section.embedded.end.php: #800000", + "dark_vs": "punctuation.section.embedded.end.php: #569CD6", + "light_vs": "punctuation.section.embedded.end.php: #800000", + "hc_black": "punctuation.section.embedded: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php punctuation.section.embedded.end.php", + "r": { + "dark_plus": "punctuation.section.embedded.end.php: #569CD6", + "light_plus": "punctuation.section.embedded.end.php: #800000", + "dark_vs": "punctuation.section.embedded.end.php: #569CD6", + "light_vs": "punctuation.section.embedded.end.php: #800000", + "hc_black": "punctuation.section.embedded: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.php meta.embedded.block.html source.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.object-literal.key: #9CDCFE" + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "...", + "t": "text.html.php meta.embedded.block.html source.js keyword.operator.spread.js", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": "<", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "/", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "script", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" } } ] \ No newline at end of file diff --git a/extensions/powershell/cgmanifest.json b/extensions/powershell/cgmanifest.json index 9c01a619c59..e9adf03ca04 100644 --- a/extensions/powershell/cgmanifest.json +++ b/extensions/powershell/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/powershell/syntaxes/powershell.tmLanguage.json b/extensions/powershell/syntaxes/powershell.tmLanguage.json index 09e2aa4d85a..59ee6fbd130 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/12b7d7257eb493e45a9af0af9094ec0c2a996712", + "version": "https://github.com/PowerShell/EditorSyntax/commit/44eac8702f3cbe55a4ec70c1fdb163d42b4162fc", "name": "PowerShell", "scopeName": "source.powershell", "patterns": [ @@ -274,7 +274,7 @@ ] }, "attribute": { - "begin": "(\\[)\\s*\\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength)\\b", + "begin": "(\\[)\\s*\\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength|supportswildcards)\\b", "beginCaptures": { "1": { "name": "punctuation.section.bracket.begin.powershell" @@ -305,33 +305,6 @@ } }, "patterns": [ - { - "include": "#variable" - }, - { - "include": "#variableNoProperty" - }, - { - "include": "#hashtable" - }, - { - "include": "#scriptblock" - }, - { - "include": "#doubleQuotedStringEscapes" - }, - { - "include": "#doubleQuotedString" - }, - { - "include": "#type" - }, - { - "include": "#numericConstant" - }, - { - "include": "#doubleQuotedString" - }, { "include": "$self" }, @@ -345,27 +318,6 @@ "name": "keyword.operator.assignment.powershell" } } - }, - { - "begin": "(?<!')'", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.powershell" - } - }, - "end": "'(?!')", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.powershell" - } - }, - "name": "string.quoted.single.powershell", - "patterns": [ - { - "match": "''", - "name": "constant.character.escape.powershell" - } - ] } ] } @@ -434,15 +386,31 @@ "doubleQuotedStringEscapes": { "patterns": [ { - "match": "`[0abnfrvt\"'$`]", + "match": "`[`0abefnrtv\"'$]", "name": "constant.character.escape.powershell" }, + { + "include": "#unicodeEscape" + }, { "match": "\"\"", "name": "constant.character.escape.powershell" } ] }, + "unicodeEscape": { + "comment": "`u{xxxx} added in PowerShell 6.0", + "patterns": [ + { + "match": "`u\\{(?:(?:10)?([0-9a-fA-F]){1,4}|0?\\g<1>{1,5})}", + "name": "constant.character.escape.powershell" + }, + { + "match": "`u(?:\\{[0-9a-fA-F]{,6}.)?", + "name": "invalid.character.escape.powershell" + } + ] + }, "function": { "begin": "^(?:\\s*+)(?i)(function|filter|configuration|workflow)\\s+(?:(global|local|script|private):)?((?:\\p{L}|\\d|_|-|\\.)+)", "beginCaptures": { @@ -677,7 +645,7 @@ { "captures": { "0": { - "name": "support.constant.automatic.powershell" + "name": "support.variable.automatic.powershell" }, "1": { "name": "punctuation.definition.variable.powershell" @@ -687,7 +655,7 @@ } }, "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.", - "match": "(\\$)(?i:(\\$|\\^|\\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?\\b" + "match": "(\\$)((?:[$^?])|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\\b)((?:\\.(?:\\p{L}|\\d|_)+)*\\b)?" }, { "captures": { @@ -865,7 +833,7 @@ } }, "comment": "Automatic variables are not constants, but they are read-only...", - "match": "(\\$)(?i:(\\$|\\^|\\?|_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This))\\b" + "match": "(\\$)((?:[$^?])|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\\b)" }, { "captures": { @@ -897,7 +865,7 @@ "name": "variable.other.member.powershell" } }, - "match": "(?i:(\\$|@)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))" + "match": "(?i:(\\$)(global|local|private|script|using|workflow):((?:\\p{L}|\\d|_)+))" }, { "captures": { @@ -1023,9 +991,6 @@ { "include": "#variableNoProperty" }, - { - "include": "#variable" - }, { "include": "#doubleQuotedStringEscapes" }, diff --git a/extensions/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json index fd82cda536b..e41d1306499 100644 --- a/extensions/powershell/test/colorize-results/test_ps1.json +++ b/extensions/powershell/test/colorize-results/test_ps1.json @@ -716,24 +716,24 @@ }, { "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 support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "_", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1090,24 +1090,24 @@ }, { "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 support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "_", - "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1299,24 +1299,24 @@ }, { "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 support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "_", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1486,24 +1486,24 @@ }, { "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 support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "matches", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { @@ -1563,24 +1563,24 @@ }, { "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 support.variable.automatic.powershell punctuation.definition.variable.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { "c": "matches", - "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.constant.automatic.powershell", + "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell support.variable.automatic.powershell", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "support.variable: #9CDCFE" } }, { diff --git a/extensions/pug/cgmanifest.json b/extensions/pug/cgmanifest.json index cdfc2ded989..e7bd7bccc09 100644 --- a/extensions/pug/cgmanifest.json +++ b/extensions/pug/cgmanifest.json @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/pug/test/colorize-results/test_pug.json b/extensions/pug/test/colorize-results/test_pug.json index 64472e02b4e..ef42ebe541b 100644 --- a/extensions/pug/test/colorize-results/test_pug.json +++ b/extensions/pug/test/colorize-results/test_pug.json @@ -980,13 +980,13 @@ }, { "c": "name", - "t": "text.pug text.block.pug string.interpolated.pug support.variable.property.dom.js", + "t": "text.pug text.block.pug string.interpolated.pug variable.other.property.js", "r": { - "dark_plus": "support.variable: #9CDCFE", - "light_plus": "support.variable: #001080", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "string: #CE9178", "light_vs": "string.interpolated.pug: #0000FF", - "hc_black": "support.variable: #9CDCFE" + "hc_black": "variable: #9CDCFE" } }, { diff --git a/extensions/python/cgmanifest.json b/extensions/python/cgmanifest.json index 4f5dbadabc8..139ae46369b 100644 --- a/extensions/python/cgmanifest.json +++ b/extensions/python/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "MagicStack/MagicPython", "repositoryUrl": "https://github.com/MagicStack/MagicPython", - "commitHash": "8ff35b3e5fcde471fae62a57ea1ae1c7cd34c9fc" + "commitHash": "38422d302fe0b3e7716d26ce8cd7d0b9685f3a38" } }, "license": "MIT", - "version": "0.0.0" + "version": "1.1.1" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/python/package.json b/extensions/python/package.json index 48151bd6245..53ca25bd88f 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -10,7 +10,7 @@ "contributes": { "languages": [{ "id": "python", - "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk"], + "extensions": [ ".py", ".rpy", ".pyw", ".cpy", ".gyp", ".gypi", ".snakefile", ".smk", ".pyi", ".ipy"], "aliases": [ "Python", "py" ], "firstLine": "^#!\\s*/.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index e51fcce1a85..d89e7ed110d 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/8ff35b3e5fcde471fae62a57ea1ae1c7cd34c9fc", + "version": "https://github.com/MagicStack/MagicPython/commit/38422d302fe0b3e7716d26ce8cd7d0b9685f3a38", "name": "MagicPython", "scopeName": "source.python", "patterns": [ @@ -827,7 +827,7 @@ ] }, "f-expression": { - "comment": "All valid Python expressions, except comments and line cont", + "comment": "All valid Python expressions, except comments and line continuation", "patterns": [ { "include": "#expression-bare" diff --git a/extensions/r/cgmanifest.json b/extensions/r/cgmanifest.json index f520a2df9bf..0781a150722 100644 --- a/extensions/r/cgmanifest.json +++ b/extensions/r/cgmanifest.json @@ -10,8 +10,8 @@ } }, "license": "MIT", - "version": "0.0.0" + "version": "0.5.5" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/razor/cgmanifest.json b/extensions/razor/cgmanifest.json index 532a4c401c4..2ad1d82c1ea 100644 --- a/extensions/razor/cgmanifest.json +++ b/extensions/razor/cgmanifest.json @@ -10,8 +10,8 @@ } }, "license": "MIT", - "version": "0.0.0" + "version": "0.3.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/ruby/cgmanifest.json b/extensions/ruby/cgmanifest.json index dd4b29e5184..ffbbb7b71c6 100644 --- a/extensions/ruby/cgmanifest.json +++ b/extensions/ruby/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/rust/cgmanifest.json b/extensions/rust/cgmanifest.json index 640d650cfd0..c3056722ece 100644 --- a/extensions/rust/cgmanifest.json +++ b/extensions/rust/cgmanifest.json @@ -6,13 +6,13 @@ "git": { "name": "language-rust", "repositoryUrl": "https://github.com/zargony/atom-language-rust", - "commitHash": "179f449a69182cae4fcdf644d59d842b7e445f89" + "commitHash": "5238d9834953ed7c58d9b5b9bb0c084c3c11ecd6" } }, "license": "MIT", "description": "The files syntaxes/rust.tmLanguage.json was derived from the Atom package https://atom.io/packages/language-rust.", - "version": "0.4.9" + "version": "0.4.12" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/rust/syntaxes/rust.tmLanguage.json b/extensions/rust/syntaxes/rust.tmLanguage.json index 79b0a69e43a..9b7373336df 100644 --- a/extensions/rust/syntaxes/rust.tmLanguage.json +++ b/extensions/rust/syntaxes/rust.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/zargony/atom-language-rust/commit/179f449a69182cae4fcdf644d59d842b7e445f89", + "version": "https://github.com/zargony/atom-language-rust/commit/5238d9834953ed7c58d9b5b9bb0c084c3c11ecd6", "name": "Rust", "scopeName": "source.rust", "patterns": [ @@ -30,6 +30,9 @@ { "include": "#mut" }, + { + "include": "#dyn" + }, { "include": "#ref_lifetime" }, @@ -181,6 +184,12 @@ { "include": "#mut" }, + { + "include": "#dyn" + }, + { + "include": "#impl" + }, { "include": "#box" }, @@ -323,6 +332,12 @@ { "include": "#mut" }, + { + "include": "#dyn" + }, + { + "include": "#impl" + }, { "include": "#ref_lifetime" }, @@ -424,6 +439,12 @@ { "include": "#mut" }, + { + "include": "#dyn" + }, + { + "include": "#impl" + }, { "include": "#lifetime" }, @@ -528,6 +549,16 @@ "name": "storage.modifier.mut.rust", "match": "\\bmut\\b" }, + "dyn": { + "comment": "Dynamic modifier", + "name": "storage.modifier.dyn.rust", + "match": "\\bdyn\\b" + }, + "impl": { + "comment": "Existential type modifier", + "name": "storage.modifier.impl.rust", + "match": "\\bimpl\\b" + }, "box": { "comment": "Box storage modifier", "name": "storage.modifier.box.rust", @@ -628,6 +659,12 @@ { "include": "#mut" }, + { + "include": "#dyn" + }, + { + "include": "#impl" + }, { "include": "#lifetime" }, diff --git a/extensions/scss/cgmanifest.json b/extensions/scss/cgmanifest.json index 05499284db1..a67a4f54609 100644 --- a/extensions/scss/cgmanifest.json +++ b/extensions/scss/cgmanifest.json @@ -11,8 +11,8 @@ }, "license": "MIT", "description": "The file syntaxes/scss.json was derived from the Atom package https://github.com/atom/language-sass which was originally converted from the TextMate bundle https://github.com/alexsancho/SASS.tmbundle.", - "version": "0.52.0" + "version": "0.61.4" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/shaderlab/cgmanifest.json b/extensions/shaderlab/cgmanifest.json index 3558286bc04..2dd540dc872 100644 --- a/extensions/shaderlab/cgmanifest.json +++ b/extensions/shaderlab/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/shared.tsconfig.json b/extensions/shared.tsconfig.json index 1cbda1bf9f0..bb42dd479cf 100644 --- a/extensions/shared.tsconfig.json +++ b/extensions/shared.tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { - "target": "es2017", + "target": "es2018", "module": "commonjs", - "lib": [ - "es6", - "es2015.promise" - ], "strict": true, "alwaysStrict": true, "noImplicitAny": true, diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index 2916167b057..8e2288c1e61 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -51,8 +51,6 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { }, externals: { 'vscode': 'commonjs vscode', // ignored because it doesn't exist - - // "vscode-extension-telemetry": 'commonjs vscode-extension-telemetry', // commonly used }, output: { // all output goes into `dist`. diff --git a/extensions/shellscript/cgmanifest.json b/extensions/shellscript/cgmanifest.json index 0eee972fc9c..fcf7f676fa7 100644 --- a/extensions/shellscript/cgmanifest.json +++ b/extensions/shellscript/cgmanifest.json @@ -11,8 +11,8 @@ }, "license": "MIT", "description": "The file syntaxes/shell-unix-bash.tmLanguage.json was derived from the Atom package https://github.com/atom/language-shellscript which was originally converted from the TextMate bundle https://github.com/textmate/shellscript.tmbundle.", - "version": "0.0.0" + "version": "0.26.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index b3c2346380d..67a05c028ea 100644 --- a/extensions/shellscript/package.json +++ b/extensions/shellscript/package.json @@ -14,7 +14,7 @@ "aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh", "ksh"], "extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme", ".ksh"], "filenames": ["PKGBUILD"], - "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh|ksh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", + "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh|ksh|ash|qsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", "configuration": "./language-configuration.json", "mimetypes": ["text/x-shellscript"] }], diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index a40abdb6d89..4cdccf12d95 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "Microsoft/vscode-mssql", "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", - "commitHash": "68d4b740b6a9e12592a32f1c0c8a0dd987f19da8" + "commitHash": "cd754662e5607c62ecdc51d2a2dc844546a0bbb6" } }, "license": "MIT", - "version": "0.0.0" + "version": "1.4.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index c74e406a129..d7bc056c6eb 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.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-mssql/commit/68d4b740b6a9e12592a32f1c0c8a0dd987f19da8", + "version": "https://github.com/Microsoft/vscode-mssql/commit/cd754662e5607c62ecdc51d2a2dc844546a0bbb6", "name": "SQL", "scopeName": "source.sql", "patterns": [ @@ -17,7 +17,7 @@ "name": "text.bracketed" }, { - "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference |automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|bigint|binary|binding|bit|block|blocksize|bmk|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|cast|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|close|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\s+or\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare(\\s+cursor)?|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|waitfor|webmethod|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\\b", + "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference |automatic|autopilot|availability|availability_mode|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blocksize|bmk|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|cast|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|cleanup_policy|clear|clear_port|close|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\s+or\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|days|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare(\\s+cursor)?|decrypt|decrypt_a|decryption|default_database|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hours|http|identity|identity_value|if|ifnull|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minutes|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|schemabinding|scoped|scroll|scroll_locks|sddl|secexpr|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|tran|transaction|transfer|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|waitfor|webmethod|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|windows|with|within|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|zone)\\b", "name": "keyword.other.sql" }, { @@ -223,7 +223,7 @@ "name": "support.function.mathematical.sql" }, { - "match": "(?i)\\b(app_name|applock_mode|applock_test|assemblyproperty|col_length|col_name|columnproperty|database_principal_id|databasepropertyex|db_id|db_name|file_id|file_idex|file_name|filegroup_id|filegroup_name|filegroupproperty|fileproperty|fulltextcatalogproperty|fulltextserviceproperty|index_col|indexkey_property|indexproperty|object_definition|object_id|object_name|object_schema_name|objectproperty|objectpropertyex|original_db_name|parsename|schema_id|schema_name|scope_identity|severeproperty|stats_date|type_id|type_name|typeproperty)\\b", + "match": "(?i)\\b(app_name|applock_mode|applock_test|assemblyproperty|col_length|col_name|columnproperty|database_principal_id|databasepropertyex|db_id|db_name|file_id|file_idex|file_name|filegroup_id|filegroup_name|filegroupproperty|fileproperty|fulltextcatalogproperty|fulltextserviceproperty|index_col|indexkey_property|indexproperty|object_definition|object_id|object_name|object_schema_name|objectproperty|objectpropertyex|original_db_name|parsename|schema_id|schema_name|scope_identity|serverproperty|stats_date|type_id|type_name|typeproperty)\\b", "name": "support.function.metadata.sql" }, { diff --git a/extensions/swift/cgmanifest.json b/extensions/swift/cgmanifest.json index a3f7977cb10..a6e26d57d7c 100644 --- a/extensions/swift/cgmanifest.json +++ b/extensions/swift/cgmanifest.json @@ -71,4 +71,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/theme-abyss/cgmanifest.json b/extensions/theme-abyss/cgmanifest.json index f3bd1db1224..e0aaef639b4 100644 --- a/extensions/theme-abyss/cgmanifest.json +++ b/extensions/theme-abyss/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "description": "The themes in this folders are copied from colorsublime.com. <<<TODO check the licenses, we can easily drop the themes>>>", diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index d74d3d57ed5..1f7d48782d2 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -400,6 +400,7 @@ "statusBar.noFolderBackground": "#10192c", "statusBar.debuggingBackground": "#10192c", // "statusBar.foreground": "", + "statusBarItem.remoteBackground": "#0063a5", "statusBarItem.prominentBackground": "#0063a5", "statusBarItem.prominentHoverBackground": "#0063a5dd", // "statusBarItem.activeBackground": "", diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json index 276a71d0fdb..00c2ac8c36b 100644 --- a/extensions/theme-defaults/themes/dark_defaults.json +++ b/extensions/theme-defaults/themes/dark_defaults.json @@ -15,6 +15,8 @@ "settings.textInputBackground": "#292929", "settings.numberInputBackground": "#292929", "menu.background": "#252526", - "menu.foreground": "#CCCCCC" + "menu.foreground": "#CCCCCC", + "statusBarItem.remoteForeground": "#FFF", + "statusBarItem.remoteBackground": "#16825D" } } \ No newline at end of file diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index 3e2c5ac607c..e73cc3a09af 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -68,7 +68,13 @@ }, { "name": "Control flow keywords", - "scope": "keyword.control", + "scope": [ + "keyword.control", + "keyword.operator.new.cpp", + "keyword.operator.delete", + "keyword.other.using", + "keyword.other.operator" + ], "settings": { "foreground": "#C586C0" } diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index c34b0981fd6..870681495dc 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -4,7 +4,8 @@ "include": "./hc_black_defaults.json", "colors": { "selection.background": "#008000", - "editor.selectionBackground": "#FFFFFF" + "editor.selectionBackground": "#FFFFFF", + "statusBarItem.remoteBackground": "#00000000" }, "tokenColors": [ { @@ -65,7 +66,13 @@ }, { "name": "Control flow keywords", - "scope": "keyword.control", + "scope": [ + "keyword.control", + "keyword.operator.new.cpp", + "keyword.operator.delete.cpp", + "keyword.other.using", + "keyword.other.operator" + ], "settings": { "foreground": "#C586C0" } diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index 932ebabbbc8..9d11138a99b 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -6,6 +6,7 @@ "editor.foreground": "#FFFFFF", "editorIndentGuide.background": "#FFFFFF", "editorIndentGuide.activeBackground": "#FFFFFF", + "statusBarItem.remoteBackground": "#00000000", "sideBarTitle.foreground": "#FFFFFF" }, "settings": [ diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index 91c5fb1d93a..e28c9b8ed0b 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -14,6 +14,8 @@ "list.hoverBackground": "#E8E8E8", "input.placeholderForeground": "#767676", "settings.textInputBorder": "#CECECE", - "settings.numberInputBorder": "#CECECE" + "settings.numberInputBorder": "#CECECE", + "statusBarItem.remoteForeground": "#FFF", + "statusBarItem.remoteBackground": "#16825D" } } \ No newline at end of file diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index 726cae4058d..ce23ed901d6 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -68,7 +68,13 @@ }, { "name": "Control flow keywords", - "scope": "keyword.control", + "scope": [ + "keyword.control", + "keyword.operator.new.cpp", + "keyword.operator.delete.cpp", + "keyword.other.using", + "keyword.other.operator" + ], "settings": { "foreground": "#AF00DB" } diff --git a/extensions/theme-kimbie-dark/cgmanifest.json b/extensions/theme-kimbie-dark/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-kimbie-dark/cgmanifest.json +++ b/extensions/theme-kimbie-dark/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index d4fbd2f7e2e..fac06fd00d4 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -27,6 +27,7 @@ "statusBar.background": "#423523", "statusBar.debuggingBackground": "#423523", "statusBar.noFolderBackground": "#423523", + "statusBarItem.remoteBackground": "#6e583b", "activityBar.background": "#221a0f", "activityBar.foreground": "#d3af86", "sideBar.background": "#362712", @@ -113,7 +114,11 @@ "name": "Keywords", "scope": [ "keyword", - "keyword.control" + "keyword.control", + "keyword.operator.new.cpp", + "keyword.operator.delete.cpp", + "keyword.other.using", + "keyword.other.operator" ], "settings": { "foreground": "#98676a" diff --git a/extensions/theme-monokai-dimmed/cgmanifest.json b/extensions/theme-monokai-dimmed/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-monokai-dimmed/cgmanifest.json +++ b/extensions/theme-monokai-dimmed/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index b50041d6cdd..ed325071b64 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -31,6 +31,7 @@ "statusBar.debuggingBackground": "#505050", "statusBar.noFolderBackground": "#505050", "titleBar.activeBackground": "#505050", + "statusBarItem.remoteBackground": "#3655b5", "activityBar.background": "#353535", "activityBar.foreground": "#ffffff", "activityBarBadge.background": "#3655b5", @@ -255,7 +256,13 @@ }, { "name": "Keyword Control", - "scope": "keyword.control", + "scope": [ + "keyword.control", + "keyword.operator.new.cpp", + "keyword.operator.delete.cpp", + "keyword.other.using", + "keyword.other.operator" + ], "settings": { "fontStyle": "", "foreground": "#9872A2" diff --git a/extensions/theme-monokai/cgmanifest.json b/extensions/theme-monokai/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-monokai/cgmanifest.json +++ b/extensions/theme-monokai/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 3806508775a..14d616f6153 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -23,8 +23,8 @@ "selection.background": "#ccccc7", "editor.selectionHighlightBackground": "#575b6180", "editor.selectionBackground": "#878b9180", - "editor.wordHighlightBackground": "#4a4a7680", - "editor.wordHighlightStrongBackground": "#6a6a9680", + "editor.wordHighlightBackground": "#4a4a7680", + "editor.wordHighlightStrongBackground": "#6a6a9680", "editor.lineHighlightBackground": "#3e3d32", "editorLineNumber.activeForeground": "#c2c2bf", "editorCursor.foreground": "#f8f8f0", @@ -50,6 +50,7 @@ "statusBar.background": "#414339", "statusBar.noFolderBackground": "#414339", "statusBar.debuggingBackground": "#75715E", + "statusBarItem.remoteBackground": "#AC6218", "activityBar.background": "#272822", "activityBar.foreground": "#f8f8f2", "activityBar.dropBackground": "#414339", diff --git a/extensions/theme-quietlight/cgmanifest.json b/extensions/theme-quietlight/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-quietlight/cgmanifest.json +++ b/extensions/theme-quietlight/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 2cef3aa273f..67f7caa4da5 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -516,6 +516,7 @@ "statusBar.background": "#705697", "statusBar.noFolderBackground": "#705697", "statusBar.debuggingBackground": "#705697", + "statusBarItem.remoteBackground": "#4e3c69", "activityBar.background": "#EDEDF5", "activityBar.foreground": "#705697", "activityBarBadge.background": "#705697", diff --git a/extensions/theme-red/cgmanifest.json b/extensions/theme-red/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-red/cgmanifest.json +++ b/extensions/theme-red/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 3de9575b0db..18e5786a8f3 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -9,6 +9,7 @@ "sideBar.background": "#330000", "statusBar.background": "#700000", "statusBar.noFolderBackground": "#700000", + "statusBarItem.remoteBackground": "#c33", "editorGroupHeader.tabsBackground": "#330000", "titleBar.activeBackground": "#770000", "titleBar.inactiveBackground": "#772222", diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index c875a47c196..c60de74fe2c 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -35,16 +35,32 @@ let nonBuiltInLanguages = { // { fileNames, extensions } "todo": { fileNames: ['todo'] } }; -function getCommitSha(repoId, repoPath) { - let commitInfo = 'https://api.github.com/repos/' + repoId + '/commits?path=' + repoPath; +let FROM_DISK = false; // set to true to take content from a repo checkedout next to the vscode repo + +let font, fontMappingsFile, fileAssociationFile, colorsFile; +if (!FROM_DISK) { + font = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti/seti.woff'; + fontMappingsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; + fileAssociationFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/components/icons/mapping.less'; + colorsFile = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/ui-variables.less'; +} else { + font = '../../../seti-ui/styles/_fonts/seti/seti.woff'; + fontMappingsFile = '../../../seti-ui/styles/_fonts/seti.less'; + fileAssociationFile = '../../../seti-ui/styles/components/icons/mapping.less'; + colorsFile = '../../../seti-ui/styles/ui-variables.less'; +} + +function getCommitSha(repoId) { + let commitInfo = 'https://api.github.com/repos/' + repoId + '/commits/master'; return download(commitInfo).then(function (content) { try { - let lastCommit = JSON.parse(content)[0]; + let lastCommit = JSON.parse(content); return Promise.resolve({ commitSha: lastCommit.sha, commitDate: lastCommit.commit.author.date }); } catch (e) { + console.error('Failed parsing ' + content); return Promise.resolve(null); } }, function () { @@ -147,7 +163,7 @@ function darkenColor(color) { 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) { + if (hex.length === 1) { res += '0'; } res += hex; @@ -189,21 +205,10 @@ function getLanguageMappings() { 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 () { return downloadBinary(font, './icons/seti.woff'); }; -//let fontMappings = 'https://raw.githubusercontent.com/jesseweed/seti-ui/master/styles/_fonts/seti.less'; -//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 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 ' + fontMappingsFile); @@ -358,12 +363,18 @@ exports.update = function () { while ((match = regex3.exec(content)) !== null) { colorId2Value[match[1]] = match[2]; } - return getCommitSha('jesseweed/seti-ui', 'styles/_fonts/seti.less').then(function (info) { + return getCommitSha('jesseweed/seti-ui').then(function (info) { try { writeFileIconContent(info); - if (info) { - console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); - } + + let cgmanifestPath = './cgmanifest.json'; + let cgmanifest = fs.readFileSync(cgmanifestPath).toString(); + let cgmanifestContent = JSON.parse(cgmanifest); + cgmanifestContent['registrations'][0]['component']['git']['commitHash'] = info.commitSha; + fs.writeFileSync(cgmanifestPath, JSON.stringify(cgmanifestContent, null, '\t')); + console.log('updated ' + cgmanifestPath); + + console.log('Updated to jesseweed/seti-ui@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); } catch (e) { console.error(e); diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 98fcf55e0e7..2b449830f50 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "0b576faae405d3cd8df6ac1a397f287aa6d8b3fe" + "commitHash": "89175d7f9e0c70cd325b80a18a3c77fc8eb7c798" } }, "version": "0.1.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index 5447129b391..4aec2074092 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -1820,5 +1820,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/7714a720646300bb8f6d1690752cd71f50991414" + "version": "https://github.com/jesseweed/seti-ui/commit/89175d7f9e0c70cd325b80a18a3c77fc8eb7c798" } \ No newline at end of file diff --git a/extensions/theme-solarized-dark/cgmanifest.json b/extensions/theme-solarized-dark/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-solarized-dark/cgmanifest.json +++ b/extensions/theme-solarized-dark/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 30cbe306f4d..5605aba721d 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -448,6 +448,7 @@ "statusBar.background": "#00212B", "statusBar.debuggingBackground": "#00212B", "statusBar.noFolderBackground": "#00212B", + "statusBarItem.remoteBackground": "#2AA19899", "statusBarItem.prominentBackground": "#003847", "statusBarItem.prominentHoverBackground": "#003847", // "statusBarItem.activeBackground": "", diff --git a/extensions/theme-solarized-light/cgmanifest.json b/extensions/theme-solarized-light/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-solarized-light/cgmanifest.json +++ b/extensions/theme-solarized-light/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" 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 79caab20000..b94aeb71de8 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -447,6 +447,7 @@ "statusBar.debuggingBackground": "#EEE8D5", "statusBar.noFolderBackground": "#EEE8D5", // "statusBar.foreground": "", + "statusBarItem.remoteBackground": "#AC9D57", "statusBarItem.prominentBackground": "#DDD6C1", "statusBarItem.prominentHoverBackground": "#DDD6C199", // "statusBarItem.activeBackground": "", diff --git a/extensions/theme-tomorrow-night-blue/cgmanifest.json b/extensions/theme-tomorrow-night-blue/cgmanifest.json index 6c169cecb07..4968a0609a5 100644 --- a/extensions/theme-tomorrow-night-blue/cgmanifest.json +++ b/extensions/theme-tomorrow-night-blue/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Colorsublime-Themes", "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", - "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + "commitHash": "c10fdd8b144486b7a4f3cb4e2251c66df222a825" } }, "version": "0.1.0" diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json index 8d01980349f..2ca39bf565d 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json @@ -30,6 +30,7 @@ "debugToolBar.background": "#001c40", "titleBar.activeBackground": "#001126", "statusBar.background": "#001126", + "statusBarItem.remoteBackground": "#0e639c", "statusBar.noFolderBackground": "#001126", "statusBar.debuggingBackground": "#001126", "activityBar.background": "#001733", diff --git a/extensions/typescript-basics/build/update-grammars.js b/extensions/typescript-basics/build/update-grammars.js index bae3329d9b1..527fa2c1d43 100644 --- a/extensions/typescript-basics/build/update-grammars.js +++ b/extensions/typescript-basics/build/update-grammars.js @@ -2,16 +2,41 @@ * 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'; var updateGrammar = require('../../../build/npm/update-grammar'); +function removeDom(grammar) { + grammar.repository['support-objects'].patterns = grammar.repository['support-objects'].patterns.filter(pattern => { + if (pattern.match && pattern.match.match(/\b(HTMLElement|ATTRIBUTE_NODE|stopImmediatePropagation)\b/g)) { + return false; + } + return true; + }); + return grammar; +} + +function patchJsdoctype(grammar) { + grammar.repository['jsdoctype'].patterns = grammar.repository['jsdoctype'].patterns.filter(pattern => { + if (pattern.name && pattern.name.indexOf('illegal') >= -1) { + return false; + } + return true; + }); + return grammar; +} + +function patchGrammar(grammar) { + return removeDom(patchJsdoctype(grammar)); +} + function adaptToJavaScript(grammar, replacementScope) { grammar.name = 'JavaScript (with React support)'; - grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs' ]; + grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs']; grammar.scopeName = `source${replacementScope}`; - var fixScopeNames = function(rule) { + var fixScopeNames = function (rule) { if (typeof rule.name === 'string') { rule.name = rule.name.replace(/\.tsx/g, replacementScope); } @@ -33,12 +58,7 @@ function adaptToJavaScript(grammar, replacementScope) { } var tsGrammarRepo = 'Microsoft/TypeScript-TmLanguage'; -updateGrammar.update(tsGrammarRepo, 'TypeScript.tmLanguage', './syntaxes/TypeScript.tmLanguage.json'); -updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', './syntaxes/TypeScriptReact.tmLanguage.json'); -updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', '../javascript/syntaxes/JavaScript.tmLanguage.json', grammar => adaptToJavaScript(grammar, '.js')); -updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', '../javascript/syntaxes/JavaScriptReact.tmLanguage.json', grammar => adaptToJavaScript(grammar, '.js.jsx')); - - - - - +updateGrammar.update(tsGrammarRepo, 'TypeScript.tmLanguage', './syntaxes/TypeScript.tmLanguage.json', grammar => patchGrammar(grammar)); +updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', './syntaxes/TypeScriptReact.tmLanguage.json', grammar => patchGrammar(grammar)); +updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', '../javascript/syntaxes/JavaScript.tmLanguage.json', grammar => adaptToJavaScript(patchGrammar(grammar), '.js')); +updateGrammar.update(tsGrammarRepo, 'TypeScriptReact.tmLanguage', '../javascript/syntaxes/JavaScriptReact.tmLanguage.json', grammar => adaptToJavaScript(patchGrammar(grammar), '.js.jsx')); diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index d6220176fe6..ffd4150e68a 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,13 +6,13 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", - "commitHash": "3133e3d914db9a2bb8812119f9273727a305f16b" + "commitHash": "3508c88a4ac6112934e0c34de7942c67682b2321" } }, "license": "MIT", "description": "The files syntaxes/TypeScript.tmLanguage.json and syntaxes/TypeScriptReact.tmLanguage.json were derived from TypeScript.tmLanguage and TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage.", - "version": "0.1.8" + "version": "1.0.0" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index ee4e295af89..213cc051560 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -24,7 +24,7 @@ ["'", "'"], ["\"", "\""], ["`", "`"], - ["<", ">"], + ["<", ">"] ], "autoCloseBefore": ";:.,=}])>` \n\t", "folding": { @@ -33,4 +33,4 @@ "end": "^\\s*//\\s*#?endregion\\b" } } -} \ No newline at end of file +} diff --git a/extensions/typescript-basics/package.json b/extensions/typescript-basics/package.json index 2976499201d..f5532d15dd3 100644 --- a/extensions/typescript-basics/package.json +++ b/extensions/typescript-basics/package.json @@ -77,6 +77,16 @@ "meta.import string.quoted": "other", "variable.other.jsdoc": "other" } + }, + { + "scopeName": "documentation.injection", + "path": "./syntaxes/jsdoc.injection.tmLanguage.json", + "injectTo": [ + "source.ts", + "source.tsx", + "source.js", + "source.js.jsx" + ] } ], "snippets": [ diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 70f6d36f820..a3d9b24fe96 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/3133e3d914db9a2bb8812119f9273727a305f16b", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3508c88a4ac6112934e0c34de7942c67682b2321", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -132,6 +132,9 @@ "name": "keyword.control.switch.ts", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(case|default|switch)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" }, + { + "include": "#if-statement" + }, { "name": "keyword.control.conditional.ts", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(else|if)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" @@ -420,10 +423,13 @@ "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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" + }, + "2": { + "name": "keyword.operator.definiteassignment.ts" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -435,10 +441,13 @@ }, { "name": "meta.var-single-variable.expr.ts", - "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.ts variable.other.constant.ts" + }, + "2": { + "name": "keyword.operator.definiteassignment.ts" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -450,10 +459,13 @@ }, { "name": "meta.var-single-variable.expr.ts", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.ts variable.other.readwrite.ts" + }, + "2": { + "name": "keyword.operator.definiteassignment.ts" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -469,7 +481,7 @@ "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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" @@ -590,7 +602,7 @@ "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(?=,|\\})", "patterns": [ { @@ -621,7 +633,7 @@ "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(?=,|\\})", "patterns": [ { @@ -647,7 +659,7 @@ ] }, "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(:)", "endCaptures": { "0": { @@ -678,6 +690,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#object-binding-pattern" }, @@ -700,6 +718,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#object-binding-pattern-const" }, @@ -841,7 +865,7 @@ } }, { - "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[: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)))", + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[: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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -941,7 +965,7 @@ "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(?=,|\\})", "patterns": [ { @@ -974,6 +998,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#parameter-object-binding-pattern" }, @@ -1048,13 +1078,13 @@ }, "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_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\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_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(?:(?:(\\?)|(\\!))\\s*)?(=|:|;|,|$))))|(?<=\\})", "patterns": [ { "include": "#variable-initializer" @@ -1075,13 +1105,16 @@ "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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" + }, + "3": { + "name": "keyword.operator.definiteassignment.ts" } } }, @@ -1092,19 +1125,23 @@ { "name": "keyword.operator.optional.ts", "match": "\\?" + }, + { + "name": "keyword.operator.definiteassignment.ts", + "match": "\\!" } ] }, "variable-initializer": { "patterns": [ { - "begin": "(?<!=|!)(=)(?!=)(?=\\s*\\S)", + "begin": "(?<!=|!)(=)(?!=)(?=\\s*\\S)(?!\\s*.*=>\\s*$)", "beginCaptures": { "1": { "name": "keyword.operator.assignment.ts" } }, - "end": "(?=$|^|[,);}\\]])", + "end": "(?=$|^|[,);}\\]]|(\\s+(of|in)\\s+))", "patterns": [ { "include": "#expression" @@ -1118,7 +1155,7 @@ "name": "keyword.operator.assignment.ts" } }, - "end": "(?=[,);}\\]])|(?=^\\s*$)|(?<=\\S)(?<!=)(?=\\s*$)", + "end": "(?=[,);}\\]]|(\\s+(of|in)\\s+))|(?=^\\s*$)|(?<=\\S)(?<!=)(?=\\s*$)", "patterns": [ { "include": "#expression" @@ -1248,7 +1285,7 @@ }, { "name": "meta.method.declaration.ts", - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:(?:\\s*\\b(new)\\b(?!:)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|(?:(\\*)\\s*)?)(?=\\s*[\\(\\<])", + "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:(?:\\s*\\b(new)\\b(?!:)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|(?:(\\*)\\s*)?)(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.ts" @@ -1278,7 +1315,7 @@ }, { "name": "meta.method.declaration.ts", - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\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:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\s*)?(?=\\s*(((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??))\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.ts" @@ -1310,7 +1347,7 @@ }, "object-literal-method-declaration": { "name": "meta.method.declaration.ts", - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\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:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\s*)?(?=\\s*(((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??))\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -1331,7 +1368,7 @@ "include": "#function-body" }, { - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\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:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\s*)?(?=\\s*(((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??))\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -1353,7 +1390,7 @@ ] }, "method-declaration-name": { - "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*[\\(\\<])", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??)\\s*[\\(\\<])", "end": "(?=\\(|\\<)", "patterns": [ { @@ -1391,7 +1428,7 @@ }, { "name": "meta.arrow.ts", - "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -1544,7 +1581,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -1581,9 +1618,12 @@ }, "class-expression": { "name": "meta.class.ts", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(class)\\b(?=\\s+|[<{]|\\/[\\/*])", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(abstract)\\s+)?(class)\\b(?=\\s+|[<{]|\\/[\\/*])", "beginCaptures": { "1": { + "name": "storage.modifier.ts" + }, + "2": { "name": "storage.type.class.ts" } }, @@ -1727,6 +1767,15 @@ { "include": "#decorator" }, + { + "begin": "(?<=:)\\s*", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", + "patterns": [ + { + "include": "#expression" + } + ] + }, { "include": "#method-declaration" }, @@ -2360,6 +2409,67 @@ } ] }, + "if-statement": { + "patterns": [ + { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?=\\bif\\s*(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))\\s*(?!\\{))", + "end": "(?=;|$|\\})", + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(if)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.control.conditional.ts" + }, + "2": { + "name": "meta.brace.round.ts" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "meta.brace.round.ts" + } + }, + "patterns": [ + { + "include": "#expression" + } + ] + }, + { + "name": "string.regexp.ts", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ts" + } + }, + "end": "(/)([gimsuy]*)", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.ts" + }, + "2": { + "name": "keyword.other.ts" + } + }, + "patterns": [ + { + "include": "#regexp" + } + ] + }, + { + "include": "#statements" + } + ] + } + ] + }, "decl-block": { "name": "meta.block.ts", "begin": "\\{", @@ -2443,8 +2553,8 @@ }, { "name": "meta.object.member.ts meta.object-literal.key.ts", - "begin": "(?=[\\'\\\"])", - "end": "(?=:)|((?<=[\\'\\\"])(?=\\s*[\\(\\<]))", + "begin": "(?=[\\'\\\"\\`])", + "end": "(?=:)|((?<=[\\'\\\"\\`])(?=((\\s*[\\(\\<,}])|(\\s+(as)\\s+))))", "patterns": [ { "include": "#comment" @@ -2454,9 +2564,22 @@ } ] }, + { + "name": "meta.object.member.ts meta.object-literal.key.ts", + "begin": "(?x)(?=(\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$)))", + "end": "(?=:)|(?=\\s*([\\(\\<,}])|(\\s+as\\s+))", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#numeric-literal" + } + ] + }, { "name": "meta.method.declaration.ts", - "begin": "(?<=[\\]\\'\\\"])(?=\\s*[\\(\\<])", + "begin": "(?<=[\\]\\'\\\"\\`])(?=\\s*[\\(\\<])", "end": "(?=\\}|;|,)|(?<=\\})", "patterns": [ { @@ -2478,7 +2601,7 @@ }, { "name": "meta.object.member.ts", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\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)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.ts" @@ -2521,6 +2644,33 @@ } } }, + { + "name": "meta.object.member.ts", + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+(const)(?=\\s*([,}]|$))", + "captures": { + "1": { + "name": "keyword.control.as.ts" + }, + "2": { + "name": "storage.modifier.ts" + } + } + }, + { + "name": "meta.object.member.ts", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.control.as.ts" + } + }, + "end": "(?=$|^|[,}]|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", + "patterns": [ + { + "include": "#type" + } + ] + }, { "name": "meta.object.member.ts", "begin": "(?=[_$[:alpha:]][_$[:alnum:]]*\\s*=)", @@ -2626,29 +2776,16 @@ ] }, "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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", "patterns": [ { - "include": "#literal" - }, - { - "include": "#support-objects" - }, - { - "include": "#object-identifiers" - }, - { - "include": "#punctuation-accessor" - }, - { - "name": "keyword.operator.expression.import.ts", - "match": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))import(?=\\s*[\\(]\\s*[\\\"\\'\\`]))" + "include": "#support-function-call-identifiers" }, { "name": "entity.name.function.ts", @@ -2671,6 +2808,26 @@ } ] }, + "support-function-call-identifiers": { + "patterns": [ + { + "include": "#literal" + }, + { + "include": "#support-objects" + }, + { + "include": "#object-identifiers" + }, + { + "include": "#punctuation-accessor" + }, + { + "name": "keyword.operator.expression.import.ts", + "match": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))import(?=\\s*[\\(]\\s*[\\\"\\'\\`]))" + } + ] + }, "new-expr": { "name": "new.expr.ts", "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(new)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", @@ -2773,7 +2930,74 @@ "include": "#expressionWithoutIdentifiers" }, { - "include": "#function-parameters-body" + "include": "#comment" + }, + { + "include": "#string" + }, + { + "include": "#decorator" + }, + { + "include": "#destructuring-parameter" + }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|protected|private|readonly)\\s+(?=(public|protected|private|readonly)\\s+)", + "captures": { + "1": { + "name": "storage.modifier.ts" + } + } + }, + { + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[: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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "captures": { + "1": { + "name": "storage.modifier.ts" + }, + "2": { + "name": "keyword.operator.rest.ts" + }, + "3": { + "name": "entity.name.function.ts variable.language.this.ts" + }, + "4": { + "name": "entity.name.function.ts" + }, + "5": { + "name": "keyword.operator.optional.ts" + } + } + }, + { + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*:)", + "captures": { + "1": { + "name": "storage.modifier.ts" + }, + "2": { + "name": "keyword.operator.rest.ts" + }, + "3": { + "name": "variable.parameter.ts variable.language.this.ts" + }, + "4": { + "name": "variable.parameter.ts" + }, + "5": { + "name": "keyword.operator.optional.ts" + } + } + }, + { + "include": "#type-annotation" + }, + { + "include": "#variable-initializer" + }, + { + "name": "punctuation.separator.parameter.ts", + "match": "," }, { "include": "#identifiers" @@ -2804,6 +3028,21 @@ }, "cast": { "patterns": [ + { + "name": "cast.expr.ts", + "match": "\\s*(<)\\s*(const)\\s*(>)", + "captures": { + "1": { + "name": "meta.brace.angle.ts" + }, + "2": { + "name": "storage.modifier.ts" + }, + "3": { + "name": "meta.brace.angle.ts" + } + } + }, { "name": "cast.expr.ts", "begin": "(?:(?<!\\+\\+|--)(?<=^return|[^\\._$[:alnum:]]return|^throw|[^\\._$[:alnum:]]throw|^yield|[^\\._$[:alnum:]]yield|^await|[^\\._$[:alnum:]]await|^default|[^\\._$[:alnum:]]default|[=(,:>*?\\&\\|\\^]|[^_$[:alnum:]](?:\\+\\+|\\-\\-)|[^\\+]\\+|[^\\-]\\-))\\s*(<)(?!<?\\=)(?!\\s*$)", @@ -2812,7 +3051,7 @@ "name": "meta.brace.angle.ts" } }, - "end": "(\\>)\\s*", + "end": "(\\>)", "endCaptures": { "1": { "name": "meta.brace.angle.ts" @@ -2832,7 +3071,7 @@ "name": "meta.brace.angle.ts" } }, - "end": "(\\>)\\s*", + "end": "(\\>)", "endCaptures": { "1": { "name": "meta.brace.angle.ts" @@ -2909,6 +3148,17 @@ "name": "keyword.operator.expression.void.ts", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))void(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+(const)(?=\\s*($|[;,:})\\]]))", + "captures": { + "1": { + "name": "keyword.control.as.ts" + }, + "2": { + "name": "storage.modifier.ts" + } + } + }, { "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+", "beginCaptures": { @@ -3040,18 +3290,33 @@ "patterns": [ { "name": "constant.numeric.hex.ts", - "match": "\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$)" + "match": "\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$)", + "captures": { + "1": { + "name": "storage.type.numeric.bigint.ts" + } + } }, { "name": "constant.numeric.binary.ts", - "match": "\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$)" + "match": "\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$)", + "captures": { + "1": { + "name": "storage.type.numeric.bigint.ts" + } + } }, { "name": "constant.numeric.octal.ts", - "match": "\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$)" + "match": "\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$)", + "captures": { + "1": { + "name": "storage.type.numeric.bigint.ts" + } + } }, { - "match": "(?x)\n(?<!\\$)(?:\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)(?!\\$)", + "match": "(?x)\n(?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$)", "captures": { "0": { "name": "constant.numeric.decimal.ts" @@ -3060,19 +3325,43 @@ "name": "meta.delimiter.decimal.period.ts" }, "2": { - "name": "meta.delimiter.decimal.period.ts" + "name": "storage.type.numeric.bigint.ts" }, "3": { "name": "meta.delimiter.decimal.period.ts" }, "4": { - "name": "meta.delimiter.decimal.period.ts" + "name": "storage.type.numeric.bigint.ts" }, "5": { "name": "meta.delimiter.decimal.period.ts" }, "6": { + "name": "storage.type.numeric.bigint.ts" + }, + "7": { + "name": "storage.type.numeric.bigint.ts" + }, + "8": { "name": "meta.delimiter.decimal.period.ts" + }, + "9": { + "name": "storage.type.numeric.bigint.ts" + }, + "10": { + "name": "meta.delimiter.decimal.period.ts" + }, + "11": { + "name": "storage.type.numeric.bigint.ts" + }, + "12": { + "name": "meta.delimiter.decimal.period.ts" + }, + "13": { + "name": "storage.type.numeric.bigint.ts" + }, + "14": { + "name": "storage.type.numeric.bigint.ts" } } } @@ -3126,7 +3415,7 @@ }, { "name": "support.class.builtin.ts", - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Array|ArrayBuffer|Atomics|Boolean|DataView|Date|Float32Array|Float64Array|Function|Generator\n |GeneratorFunction|Int8Array|Int16Array|Int32Array|Intl|Map|Number|Object|Proxy\n |Reflect|RegExp|Set|SharedArrayBuffer|SIMD|String|Symbol|TypedArray\n |Uint8Array|Uint16Array|Uint32Array|Uint8ClampedArray|WeakMap|WeakSet)\\b(?!\\$)" + "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Array|ArrayBuffer|Atomics|BigInt|BigInt64Array|BigUint64Array|Boolean|DataView|Date|Float32Array\n |Float64Array|Function|Generator|GeneratorFunction|Int8Array|Int16Array|Int32Array|Intl|Map|Number|Object|Proxy\n |Reflect|RegExp|Set|SharedArrayBuffer|SIMD|String|Symbol|TypedArray\n |Uint8Array|Uint16Array|Uint32Array|Uint8ClampedArray|WeakMap|WeakSet)\\b(?!\\$)" }, { "name": "support.class.error.ts", @@ -3229,7 +3518,7 @@ } }, { - "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s* (?:\n (constructor|length|prototype|__proto__)\n |\n (EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY))\\b(?!\\$)", + "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s* (?:\n (?:(constructor|length|prototype|__proto__)\\b(?!\\$|\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\())\n |\n (?:(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\b(?!\\$)))", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3245,34 +3534,6 @@ } } }, - { - "match": "(?x) (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.)) \\b (?:\n (document|event|navigator|performance|screen|window)\n |\n (AnalyserNode|ArrayBufferView|Attr|AudioBuffer|AudioBufferSourceNode|AudioContext|AudioDestinationNode|AudioListener\n |AudioNode|AudioParam|BatteryManager|BeforeUnloadEvent|BiquadFilterNode|Blob|BufferSource|ByteString|CSS|CSSConditionRule\n |CSSCounterStyleRule|CSSGroupingRule|CSSMatrix|CSSMediaRule|CSSPageRule|CSSPrimitiveValue|CSSRule|CSSRuleList|CSSStyleDeclaration\n |CSSStyleRule|CSSStyleSheet|CSSSupportsRule|CSSValue|CSSValueList|CanvasGradient|CanvasImageSource|CanvasPattern\n |CanvasRenderingContext2D|ChannelMergerNode|ChannelSplitterNode|CharacterData|ChromeWorker|CloseEvent|Comment|CompositionEvent\n |Console|ConvolverNode|Coordinates|Credential|CredentialsContainer|Crypto|CryptoKey|CustomEvent|DOMError|DOMException\n |DOMHighResTimeStamp|DOMImplementation|DOMString|DOMStringList|DOMStringMap|DOMTimeStamp|DOMTokenList|DataTransfer\n |DataTransferItem|DataTransferItemList|DedicatedWorkerGlobalScope|DelayNode|DeviceProximityEvent|DirectoryEntry\n |DirectoryEntrySync|DirectoryReader|DirectoryReaderSync|Document|DocumentFragment|DocumentTouch|DocumentType|DragEvent\n |DynamicsCompressorNode|Element|Entry|EntrySync|ErrorEvent|Event|EventListener|EventSource|EventTarget|FederatedCredential\n |FetchEvent|File|FileEntry|FileEntrySync|FileException|FileList|FileReader|FileReaderSync|FileSystem|FileSystemSync\n |FontFace|FormData|GainNode|Gamepad|GamepadButton|GamepadEvent|Geolocation|GlobalEventHandlers|HTMLAnchorElement\n |HTMLAreaElement|HTMLAudioElement|HTMLBRElement|HTMLBaseElement|HTMLBodyElement|HTMLButtonElement|HTMLCanvasElement\n |HTMLCollection|HTMLContentElement|HTMLDListElement|HTMLDataElement|HTMLDataListElement|HTMLDialogElement|HTMLDivElement\n |HTMLDocument|HTMLElement|HTMLEmbedElement|HTMLFieldSetElement|HTMLFontElement|HTMLFormControlsCollection|HTMLFormElement\n |HTMLHRElement|HTMLHeadElement|HTMLHeadingElement|HTMLHtmlElement|HTMLIFrameElement|HTMLImageElement|HTMLInputElement\n |HTMLKeygenElement|HTMLLIElement|HTMLLabelElement|HTMLLegendElement|HTMLLinkElement|HTMLMapElement|HTMLMediaElement\n |HTMLMetaElement|HTMLMeterElement|HTMLModElement|HTMLOListElement|HTMLObjectElement|HTMLOptGroupElement|HTMLOptionElement\n |HTMLOptionsCollection|HTMLOutputElement|HTMLParagraphElement|HTMLParamElement|HTMLPreElement|HTMLProgressElement\n |HTMLQuoteElement|HTMLScriptElement|HTMLSelectElement|HTMLShadowElement|HTMLSourceElement|HTMLSpanElement|HTMLStyleElement\n |HTMLTableCaptionElement|HTMLTableCellElement|HTMLTableColElement|HTMLTableDataCellElement|HTMLTableElement|HTMLTableHeaderCellElement\n |HTMLTableRowElement|HTMLTableSectionElement|HTMLTextAreaElement|HTMLTimeElement|HTMLTitleElement|HTMLTrackElement\n |HTMLUListElement|HTMLUnknownElement|HTMLVideoElement|HashChangeEvent|History|IDBCursor|IDBCursorWithValue|IDBDatabase\n |IDBEnvironment|IDBFactory|IDBIndex|IDBKeyRange|IDBMutableFile|IDBObjectStore|IDBOpenDBRequest|IDBRequest|IDBTransaction\n |IDBVersionChangeEvent|IIRFilterNode|IdentityManager|ImageBitmap|ImageBitmapFactories|ImageData|Index|InputDeviceCapabilities\n |InputEvent|InstallEvent|InstallTrigger|KeyboardEvent|LinkStyle|LocalFileSystem|LocalFileSystemSync|Location|MIDIAccess\n |MIDIConnectionEvent|MIDIInput|MIDIInputMap|MIDIOutputMap|MediaElementAudioSourceNode|MediaError|MediaKeyMessageEvent\n |MediaKeySession|MediaKeyStatusMap|MediaKeySystemAccess|MediaKeySystemConfiguration|MediaKeys|MediaRecorder|MediaStream\n |MediaStreamAudioDestinationNode|MediaStreamAudioSourceNode|MessageChannel|MessageEvent|MessagePort|MouseEvent\n |MutationObserver|MutationRecord|NamedNodeMap|Navigator|NavigatorConcurrentHardware|NavigatorGeolocation|NavigatorID\n |NavigatorLanguage|NavigatorOnLine|Node|NodeFilter|NodeIterator|NodeList|NonDocumentTypeChildNode|Notification\n |OfflineAudioCompletionEvent|OfflineAudioContext|OscillatorNode|PageTransitionEvent|PannerNode|ParentNode|PasswordCredential\n |Path2D|PaymentAddress|PaymentRequest|PaymentResponse|Performance|PerformanceEntry|PerformanceFrameTiming|PerformanceMark\n |PerformanceMeasure|PerformanceNavigation|PerformanceNavigationTiming|PerformanceObserver|PerformanceObserverEntryList\n |PerformanceResourceTiming|PerformanceTiming|PeriodicSyncEvent|PeriodicWave|Plugin|Point|PointerEvent|PopStateEvent\n |PortCollection|Position|PositionError|PositionOptions|PresentationConnectionClosedEvent|PresentationConnectionList\n |PresentationReceiver|ProcessingInstruction|ProgressEvent|PromiseRejectionEvent|PushEvent|PushRegistrationManager\n |RTCCertificate|RTCConfiguration|RTCPeerConnection|RTCSessionDescriptionCallback|RTCStatsReport|RadioNodeList|RandomSource\n |Range|ReadableByteStream|RenderingContext|SVGAElement|SVGAngle|SVGAnimateColorElement|SVGAnimateElement|SVGAnimateMotionElement\n |SVGAnimateTransformElement|SVGAnimatedAngle|SVGAnimatedBoolean|SVGAnimatedEnumeration|SVGAnimatedInteger|SVGAnimatedLength\n |SVGAnimatedLengthList|SVGAnimatedNumber|SVGAnimatedNumberList|SVGAnimatedPoints|SVGAnimatedPreserveAspectRatio\n |SVGAnimatedRect|SVGAnimatedString|SVGAnimatedTransformList|SVGAnimationElement|SVGCircleElement|SVGClipPathElement\n |SVGCursorElement|SVGDefsElement|SVGDescElement|SVGElement|SVGEllipseElement|SVGEvent|SVGFilterElement|SVGFontElement\n |SVGFontFaceElement|SVGFontFaceFormatElement|SVGFontFaceNameElement|SVGFontFaceSrcElement|SVGFontFaceUriElement\n |SVGForeignObjectElement|SVGGElement|SVGGlyphElement|SVGGradientElement|SVGHKernElement|SVGImageElement|SVGLength\n |SVGLengthList|SVGLineElement|SVGLinearGradientElement|SVGMPathElement|SVGMaskElement|SVGMatrix|SVGMissingGlyphElement\n |SVGNumber|SVGNumberList|SVGPathElement|SVGPatternElement|SVGPoint|SVGPolygonElement|SVGPolylineElement|SVGPreserveAspectRatio\n |SVGRadialGradientElement|SVGRect|SVGRectElement|SVGSVGElement|SVGScriptElement|SVGSetElement|SVGStopElement|SVGStringList\n |SVGStylable|SVGStyleElement|SVGSwitchElement|SVGSymbolElement|SVGTRefElement|SVGTSpanElement|SVGTests|SVGTextElement\n |SVGTextPositioningElement|SVGTitleElement|SVGTransform|SVGTransformList|SVGTransformable|SVGUseElement|SVGVKernElement\n |SVGViewElement|ServiceWorker|ServiceWorkerContainer|ServiceWorkerGlobalScope|ServiceWorkerRegistration|ServiceWorkerState\n |ShadowRoot|SharedWorker|SharedWorkerGlobalScope|SourceBufferList|StereoPannerNode|Storage|StorageEvent|StyleSheet\n |StyleSheetList|SubtleCrypto|SyncEvent|Text|TextMetrics|TimeEvent|TimeRanges|Touch|TouchEvent|TouchList|Transferable\n |TreeWalker|UIEvent|USVString|VRDisplayCapabilities|ValidityState|WaveShaperNode|WebGL|WebGLActiveInfo|WebGLBuffer\n |WebGLContextEvent|WebGLFramebuffer|WebGLProgram|WebGLRenderbuffer|WebGLRenderingContext|WebGLShader|WebGLShaderPrecisionFormat\n |WebGLTexture|WebGLTimerQueryEXT|WebGLTransformFeedback|WebGLUniformLocation|WebGLVertexArrayObject|WebGLVertexArrayObjectOES\n |WebSocket|WebSockets|WebVTT|WheelEvent|Window|WindowBase64|WindowEventHandlers|WindowTimers|Worker|WorkerGlobalScope\n |WorkerLocation|WorkerNavigator|XMLHttpRequest|XMLHttpRequestEventTarget|XMLSerializer|XPathExpression|XPathResult\n |XSLTProcessor))\\b(?!\\$)", - "captures": { - "1": { - "name": "support.variable.dom.ts" - }, - "2": { - "name": "support.class.dom.ts" - } - } - }, - { - "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\()", - "captures": { - "1": { - "name": "punctuation.accessor.ts" - }, - "2": { - "name": "punctuation.accessor.optional.ts" - }, - "3": { - "name": "support.constant.dom.ts" - }, - "4": { - "name": "support.variable.property.dom.ts" - } - } - }, { "name": "support.class.node.ts", "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Buffer|EventEmitter|Server|Pipe|Socket|REPLServer|ReadStream|WriteStream|Stream\n |Inflate|Deflate|InflateRaw|DeflateRaw|GZip|GUnzip|Unzip|Zip)\\b(?!\\$)" @@ -3320,29 +3581,6 @@ { "name": "support.variable.object.node.ts", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(global|GLOBAL|root|__dirname|__filename)\\b(?!\\$)" - }, - { - "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s*\n(?:\n (on(?:Rowsinserted|Rowsdelete|Rowenter|Rowexit|Resize|Resizestart|Resizeend|Reset|\n Readystatechange|Mouseout|Mouseover|Mousedown|Mouseup|Mousemove|\n Before(?:cut|deactivate|unload|update|paste|print|editfocus|activate)|\n Blur|Scrolltop|Submit|Select|Selectstart|Selectionchange|Hover|Help|\n Change|Contextmenu|Controlselect|Cut|Cellchange|Clock|Close|Deactivate|\n Datasetchanged|Datasetcomplete|Dataavailable|Drop|Drag|Dragstart|Dragover|\n Dragdrop|Dragenter|Dragend|Dragleave|Dblclick|Unload|Paste|Propertychange|Error|\n Errorupdate|Keydown|Keyup|Keypress|Focus|Load|Activate|Afterupdate|Afterprint|Abort)\n ) |\n (shift|showModelessDialog|showModalDialog|showHelp|scroll|scrollX|scrollByPages|\n scrollByLines|scrollY|scrollTo|stop|strike|sizeToContent|sidebar|signText|sort|\n sup|sub|substr|substring|splice|split|send|set(?:Milliseconds|Seconds|Minutes|Hours|\n Month|Year|FullYear|Date|UTC(?:Milliseconds|Seconds|Minutes|Hours|Month|FullYear|Date)|\n Time|Hotkeys|Cursor|ZOptions|Active|Resizable|RequestHeader)|search|slice|\n savePreferences|small|home|handleEvent|navigate|char|charCodeAt|charAt|concat|\n contextual|confirm|compile|clear|captureEvents|call|createStyleSheet|createPopup|\n createEventObject|to(?:GMTString|UTCString|String|Source|UpperCase|LowerCase|LocaleString)|\n test|taint|taintEnabled|indexOf|italics|disableExternalCapture|dump|detachEvent|unshift|\n untaint|unwatch|updateCommands|join|javaEnabled|pop|push|plugins.refresh|paddings|parse|\n print|prompt|preference|enableExternalCapture|exec|execScript|valueOf|UTC|find|file|\n fileModifiedDate|fileSize|fileCreatedDate|fileUpdatedDate|fixed|fontsize|fontcolor|\n forward|fromCharCode|watch|link|load|lastIndexOf|anchor|attachEvent|atob|apply|alert|\n abort|routeEvents|resize|resizeBy|resizeTo|recalc|returnValue|replace|reverse|reload|\n releaseCapture|releaseEvents|go|get(?:Milliseconds|Seconds|Minutes|Hours|Month|Day|Year|FullYear|\n Time|Date|TimezoneOffset|UTC(?:Milliseconds|Seconds|Minutes|Hours|Day|Month|FullYear|Date)|\n Attention|Selection|ResponseHeader|AllResponseHeaders)|moveBy|moveBelow|moveTo|\n moveToAbsolute|moveAbove|mergeAttributes|match|margins|btoa|big|bold|borderWidths|blink|back\n ) |\n (acceptNode|add|addEventListener|addTextTrack|adoptNode|after|animate|append|\n appendChild|appendData|before|blur|canPlayType|captureStream|\n caretPositionFromPoint|caretRangeFromPoint|checkValidity|clear|click|\n cloneContents|cloneNode|cloneRange|close|closest|collapse|\n compareBoundaryPoints|compareDocumentPosition|comparePoint|contains|\n convertPointFromNode|convertQuadFromNode|convertRectFromNode|createAttribute|\n createAttributeNS|createCaption|createCDATASection|createComment|\n createContextualFragment|createDocument|createDocumentFragment|\n createDocumentType|createElement|createElementNS|createEntityReference|\n createEvent|createExpression|createHTMLDocument|createNodeIterator|\n createNSResolver|createProcessingInstruction|createRange|createShadowRoot|\n createTBody|createTextNode|createTFoot|createTHead|createTreeWalker|delete|\n deleteCaption|deleteCell|deleteContents|deleteData|deleteRow|deleteTFoot|\n deleteTHead|detach|disconnect|dispatchEvent|elementFromPoint|elementsFromPoint|\n enableStyleSheetsForSet|entries|evaluate|execCommand|exitFullscreen|\n exitPointerLock|expand|extractContents|fastSeek|firstChild|focus|forEach|get|\n getAll|getAnimations|getAttribute|getAttributeNames|getAttributeNode|\n getAttributeNodeNS|getAttributeNS|getBoundingClientRect|getBoxQuads|\n getClientRects|getContext|getDestinationInsertionPoints|getElementById|\n getElementsByClassName|getElementsByName|getElementsByTagName|\n getElementsByTagNameNS|getItem|getNamedItem|getSelection|getStartDate|\n getVideoPlaybackQuality|has|hasAttribute|hasAttributeNS|hasAttributes|\n hasChildNodes|hasFeature|hasFocus|importNode|initEvent|insertAdjacentElement|\n insertAdjacentHTML|insertAdjacentText|insertBefore|insertCell|insertData|\n insertNode|insertRow|intersectsNode|isDefaultNamespace|isEqualNode|\n isPointInRange|isSameNode|item|key|keys|lastChild|load|lookupNamespaceURI|\n lookupPrefix|matches|move|moveAttribute|moveAttributeNode|moveChild|\n moveNamedItem|namedItem|nextNode|nextSibling|normalize|observe|open|\n parentNode|pause|play|postMessage|prepend|preventDefault|previousNode|\n previousSibling|probablySupportsContext|queryCommandEnabled|\n queryCommandIndeterm|queryCommandState|queryCommandSupported|queryCommandValue|\n querySelector|querySelectorAll|registerContentHandler|registerElement|\n registerProtocolHandler|releaseCapture|releaseEvents|remove|removeAttribute|\n removeAttributeNode|removeAttributeNS|removeChild|removeEventListener|\n removeItem|replace|replaceChild|replaceData|replaceWith|reportValidity|\n requestFullscreen|requestPointerLock|reset|scroll|scrollBy|scrollIntoView|\n scrollTo|seekToNextFrame|select|selectNode|selectNodeContents|set|setAttribute|\n setAttributeNode|setAttributeNodeNS|setAttributeNS|setCapture|\n setCustomValidity|setEnd|setEndAfter|setEndBefore|setItem|setNamedItem|\n setRangeText|setSelectionRange|setSinkId|setStart|setStartAfter|setStartBefore|\n slice|splitText|stepDown|stepUp|stopImmediatePropagation|stopPropagation|\n submit|substringData|supports|surroundContents|takeRecords|terminate|toBlob|\n toDataURL|toggle|toString|values|write|writeln\n ) |\n (all|catch|finally|race|reject|resolve|then\n )\n)(?=\\s*\\()", - "captures": { - "1": { - "name": "punctuation.accessor.ts" - }, - "2": { - "name": "punctuation.accessor.optional.ts" - }, - "3": { - "name": "support.function.event-handler.ts" - }, - "4": { - "name": "support.function.ts" - }, - "5": { - "name": "support.function.dom.ts" - }, - "6": { - "name": "support.function.promise.ts" - } - } } ] }, @@ -3352,7 +3590,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3473,6 +3711,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.ts", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.ts" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { @@ -3626,6 +3883,13 @@ "name": "punctuation.definition.typeparameters.end.ts" } }, + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + "type-arguments-body": { "patterns": [ { "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(_)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", @@ -3661,7 +3925,7 @@ "include": "#type-builtin-literals" }, { - "include": "#type-arguments" + "include": "#type-parameters" }, { "include": "#type-tuple" @@ -3684,6 +3948,14 @@ { "include": "#type-function-return-type" }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(readonly)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*", + "captures": { + "1": { + "name": "storage.modifier.ts" + } + } + }, { "include": "#type-name" } @@ -3691,7 +3963,7 @@ }, "type-primitive": { "name": "support.type.primitive.ts", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(string|number|boolean|symbol|any|void|never|unknown)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(string|number|bigint|boolean|symbol|any|void|never|unknown)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" }, "type-builtin-literals": { "name": "support.type.builtin.ts", @@ -3885,13 +4157,21 @@ "type-fn-type-parameters": { "patterns": [ { - "name": "meta.type.constructor.ts", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(new)\\b(?=\\s*\\<)", - "captures": { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(new)\\b(?=\\s*\\<)", + "beginCaptures": { "1": { - "name": "keyword.control.new.ts" + "name": "meta.type.constructor.ts keyword.control.new.ts" } - } + }, + "end": "(?<=>)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type-parameters" + } + ] }, { "name": "meta.type.constructor.ts", @@ -4028,6 +4308,58 @@ }, "type-name": { "patterns": [ + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(<)", + "captures": { + "1": { + "name": "entity.name.type.module.ts" + }, + "2": { + "name": "punctuation.accessor.ts" + }, + "3": { + "name": "punctuation.accessor.optional.ts" + }, + "4": { + "name": "meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.ts punctuation.definition.typeparameters.end.ts" + } + }, + "contentName": "meta.type.parameters.ts", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(<)", + "beginCaptures": { + "1": { + "name": "entity.name.type.ts" + }, + "2": { + "name": "meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.ts punctuation.definition.typeparameters.end.ts" + } + }, + "contentName": "meta.type.parameters.ts", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, { "match": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))", "captures": { @@ -4134,7 +4466,30 @@ "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*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "end": "(?=`)", + "patterns": [ + { + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "patterns": [ + { + "include": "#support-function-call-identifiers" + }, + { + "name": "entity.name.function.tagged-template.ts", + "match": "([_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + { + "include": "#type-arguments" + } + ] + }, + { + "name": "string.template.ts", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.ts" @@ -4200,7 +4555,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" @@ -4223,7 +4578,7 @@ }, { "name": "string.regexp.ts", - "begin": "(?<![_$[:alnum:])\\]]|\\+\\+|--|})\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "((?<![_$[:alnum:])\\]]|\\+\\+|--|}|\\*\\/)|((?<=^return|[^\\._$[:alnum:]]return|^case|[^\\._$[:alnum:]]case))\\s*)\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.ts" @@ -4853,6 +5208,17 @@ }, { "include": "#inline-tags" + }, + { + "match": "((@)(?:[_$[:alpha:]][_$[:alnum:]]*))(?=\\s+)", + "captures": { + "1": { + "name": "storage.type.class.jsdoc" + }, + "2": { + "name": "punctuation.definition.block.tag.jsdoc" + } + } } ] }, @@ -4941,10 +5307,6 @@ }, "jsdoctype": { "patterns": [ - { - "name": "invalid.illegal.type.jsdoc", - "match": "\\G{(?:[^}*]|\\*[^/}])+$" - }, { "contentName": "entity.name.type.instance.jsdoc", "begin": "\\G({)", diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index 6d39e0862c5..3560a374b9d 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/3133e3d914db9a2bb8812119f9273727a305f16b", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3508c88a4ac6112934e0c34de7942c67682b2321", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -132,6 +132,9 @@ "name": "keyword.control.switch.tsx", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(case|default|switch)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" }, + { + "include": "#if-statement" + }, { "name": "keyword.control.conditional.tsx", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(else|if)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" @@ -423,10 +426,13 @@ "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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" + }, + "2": { + "name": "keyword.operator.definiteassignment.tsx" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -438,10 +444,13 @@ }, { "name": "meta.var-single-variable.expr.tsx", - "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "begin": "([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx variable.other.constant.tsx" + }, + "2": { + "name": "keyword.operator.definiteassignment.tsx" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -453,10 +462,13 @@ }, { "name": "meta.var-single-variable.expr.tsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)(\\!)?", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx variable.other.readwrite.tsx" + }, + "2": { + "name": "keyword.operator.definiteassignment.tsx" } }, "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", @@ -472,7 +484,7 @@ "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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" @@ -593,7 +605,7 @@ "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(?=,|\\})", "patterns": [ { @@ -624,7 +636,7 @@ "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(?=,|\\})", "patterns": [ { @@ -650,7 +662,7 @@ ] }, "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(:)", "endCaptures": { "0": { @@ -681,6 +693,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#object-binding-pattern" }, @@ -703,6 +721,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#object-binding-pattern-const" }, @@ -844,7 +868,7 @@ } }, { - "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[: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)))", + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[: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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -944,7 +968,7 @@ "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*(:))", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(:))", "end": "(?=,|\\})", "patterns": [ { @@ -977,6 +1001,12 @@ { "include": "#string" }, + { + "include": "#numeric-literal" + }, + { + "include": "#regex" + }, { "include": "#parameter-object-binding-pattern" }, @@ -1051,13 +1081,13 @@ }, "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_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\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_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(?:(?:(\\?)|(\\!))\\s*)?(=|:|;|,|$))))|(?<=\\})", "patterns": [ { "include": "#variable-initializer" @@ -1078,13 +1108,16 @@ "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)))", + "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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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" + }, + "3": { + "name": "keyword.operator.definiteassignment.tsx" } } }, @@ -1095,19 +1128,23 @@ { "name": "keyword.operator.optional.tsx", "match": "\\?" + }, + { + "name": "keyword.operator.definiteassignment.tsx", + "match": "\\!" } ] }, "variable-initializer": { "patterns": [ { - "begin": "(?<!=|!)(=)(?!=)(?=\\s*\\S)", + "begin": "(?<!=|!)(=)(?!=)(?=\\s*\\S)(?!\\s*.*=>\\s*$)", "beginCaptures": { "1": { "name": "keyword.operator.assignment.tsx" } }, - "end": "(?=$|^|[,);}\\]])", + "end": "(?=$|^|[,);}\\]]|(\\s+(of|in)\\s+))", "patterns": [ { "include": "#expression" @@ -1121,7 +1158,7 @@ "name": "keyword.operator.assignment.tsx" } }, - "end": "(?=[,);}\\]])|(?=^\\s*$)|(?<=\\S)(?<!=)(?=\\s*$)", + "end": "(?=[,);}\\]]|(\\s+(of|in)\\s+))|(?=^\\s*$)|(?<=\\S)(?<!=)(?=\\s*$)", "patterns": [ { "include": "#expression" @@ -1251,7 +1288,7 @@ }, { "name": "meta.method.declaration.tsx", - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:(?:\\s*\\b(new)\\b(?!:)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|(?:(\\*)\\s*)?)(?=\\s*[\\(\\<])", + "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:(?:\\s*\\b(new)\\b(?!:)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|(?:(\\*)\\s*)?)(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.tsx" @@ -1281,7 +1318,7 @@ }, { "name": "meta.method.declaration.tsx", - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\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:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(public|private|protected)\\s+)?(?:\\b(abstract)\\s+)?(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\s*)?(?=\\s*(((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??))\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.tsx" @@ -1313,7 +1350,7 @@ }, "object-literal-method-declaration": { "name": "meta.method.declaration.tsx", - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\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:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\s*)?(?=\\s*(((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??))\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -1334,7 +1371,7 @@ "include": "#function-body" }, { - "begin": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\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:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:\\b(async)\\s+)?(?:\\b(get|set)\\s+)?(?:(\\*)\\s*)?(?=\\s*(((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??))\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?[\\(])", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -1356,7 +1393,7 @@ ] }, "method-declaration-name": { - "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*[\\(\\<])", + "begin": "(?x)(?=((\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$))|([_$[:alpha:]][_$[:alnum:]]*)|(\\'([^\\'\\\\]|\\\\\\'|\\\\)*\\')|(\\\"([^\\\"\\\\]|\\\\\\\"|\\\\)*\\\")|(\\`([^\\`\\\\]|\\\\\\`|\\\\)*\\`)|(\\[([^\\[\\]]|\\[[^\\[\\]]*\\])+\\]))\\s*(\\??)\\s*[\\(\\<])", "end": "(?=\\(|\\<)", "patterns": [ { @@ -1394,7 +1431,7 @@ }, { "name": "meta.arrow.tsx", - "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", + "begin": "(?x) (?:\n (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(\\basync)\n)? ((?<![})!\\]])\\s*\n (?=\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n )\n)", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -1547,7 +1584,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -1584,9 +1621,12 @@ }, "class-expression": { "name": "meta.class.tsx", - "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(class)\\b(?=\\s+|[<{]|\\/[\\/*])", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(abstract)\\s+)?(class)\\b(?=\\s+|[<{]|\\/[\\/*])", "beginCaptures": { "1": { + "name": "storage.modifier.tsx" + }, + "2": { "name": "storage.type.class.tsx" } }, @@ -1730,6 +1770,15 @@ { "include": "#decorator" }, + { + "begin": "(?<=:)\\s*", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))", + "patterns": [ + { + "include": "#expression" + } + ] + }, { "include": "#method-declaration" }, @@ -2363,6 +2412,67 @@ } ] }, + "if-statement": { + "patterns": [ + { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?=\\bif\\s*(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))\\s*(?!\\{))", + "end": "(?=;|$|\\})", + "patterns": [ + { + "include": "#comment" + }, + { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(if)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.control.conditional.tsx" + }, + "2": { + "name": "meta.brace.round.tsx" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "meta.brace.round.tsx" + } + }, + "patterns": [ + { + "include": "#expression" + } + ] + }, + { + "name": "string.regexp.tsx", + "begin": "(?<=\\))\\s*\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.tsx" + } + }, + "end": "(/)([gimsuy]*)", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.tsx" + }, + "2": { + "name": "keyword.other.tsx" + } + }, + "patterns": [ + { + "include": "#regexp" + } + ] + }, + { + "include": "#statements" + } + ] + } + ] + }, "decl-block": { "name": "meta.block.tsx", "begin": "\\{", @@ -2446,8 +2556,8 @@ }, { "name": "meta.object.member.tsx meta.object-literal.key.tsx", - "begin": "(?=[\\'\\\"])", - "end": "(?=:)|((?<=[\\'\\\"])(?=\\s*[\\(\\<]))", + "begin": "(?=[\\'\\\"\\`])", + "end": "(?=:)|((?<=[\\'\\\"\\`])(?=((\\s*[\\(\\<,}])|(\\s+(as)\\s+))))", "patterns": [ { "include": "#comment" @@ -2457,9 +2567,22 @@ } ] }, + { + "name": "meta.object.member.tsx meta.object-literal.key.tsx", + "begin": "(?x)(?=(\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$))|(\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$))|((?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$)))", + "end": "(?=:)|(?=\\s*([\\(\\<,}])|(\\s+as\\s+))", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#numeric-literal" + } + ] + }, { "name": "meta.method.declaration.tsx", - "begin": "(?<=[\\]\\'\\\"])(?=\\s*[\\(\\<])", + "begin": "(?<=[\\]\\'\\\"\\`])(?=\\s*[\\(\\<])", "end": "(?=\\}|;|,)|(?<=\\})", "patterns": [ { @@ -2481,7 +2604,7 @@ }, { "name": "meta.object.member.tsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\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)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*:(\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.tsx" @@ -2524,6 +2647,33 @@ } } }, + { + "name": "meta.object.member.tsx", + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+(const)(?=\\s*([,}]|$))", + "captures": { + "1": { + "name": "keyword.control.as.tsx" + }, + "2": { + "name": "storage.modifier.tsx" + } + } + }, + { + "name": "meta.object.member.tsx", + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+", + "beginCaptures": { + "1": { + "name": "keyword.control.as.tsx" + } + }, + "end": "(?=$|^|[,}]|((?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+))", + "patterns": [ + { + "include": "#type" + } + ] + }, { "name": "meta.object.member.tsx", "begin": "(?=[_$[:alpha:]][_$[:alnum:]]*\\s*=)", @@ -2629,29 +2779,16 @@ ] }, "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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?\\()", "patterns": [ { - "include": "#literal" - }, - { - "include": "#support-objects" - }, - { - "include": "#object-identifiers" - }, - { - "include": "#punctuation-accessor" - }, - { - "name": "keyword.operator.expression.import.tsx", - "match": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))import(?=\\s*[\\(]\\s*[\\\"\\'\\`]))" + "include": "#support-function-call-identifiers" }, { "name": "entity.name.function.tsx", @@ -2674,6 +2811,26 @@ } ] }, + "support-function-call-identifiers": { + "patterns": [ + { + "include": "#literal" + }, + { + "include": "#support-objects" + }, + { + "include": "#object-identifiers" + }, + { + "include": "#punctuation-accessor" + }, + { + "name": "keyword.operator.expression.import.tsx", + "match": "(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))import(?=\\s*[\\(]\\s*[\\\"\\'\\`]))" + } + ] + }, "new-expr": { "name": "new.expr.tsx", "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(new)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", @@ -2776,7 +2933,74 @@ "include": "#expressionWithoutIdentifiers" }, { - "include": "#function-parameters-body" + "include": "#comment" + }, + { + "include": "#string" + }, + { + "include": "#decorator" + }, + { + "include": "#destructuring-parameter" + }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|protected|private|readonly)\\s+(?=(public|protected|private|readonly)\\s+)", + "captures": { + "1": { + "name": "storage.modifier.tsx" + } + } + }, + { + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[: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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "captures": { + "1": { + "name": "storage.modifier.tsx" + }, + "2": { + "name": "keyword.operator.rest.tsx" + }, + "3": { + "name": "entity.name.function.tsx variable.language.this.tsx" + }, + "4": { + "name": "entity.name.function.tsx" + }, + "5": { + "name": "keyword.operator.optional.tsx" + } + } + }, + { + "match": "(?x)(?:(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(public|private|protected|readonly)\\s+)?(?:(\\.\\.\\.)\\s*)?(?<!=|:)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(?:(this)|([_$[:alpha:]][_$[:alnum:]]*))(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*(\\??)(?=\\s*:)", + "captures": { + "1": { + "name": "storage.modifier.tsx" + }, + "2": { + "name": "keyword.operator.rest.tsx" + }, + "3": { + "name": "variable.parameter.tsx variable.language.this.tsx" + }, + "4": { + "name": "variable.parameter.tsx" + }, + "5": { + "name": "keyword.operator.optional.tsx" + } + } + }, + { + "include": "#type-annotation" + }, + { + "include": "#variable-initializer" + }, + { + "name": "punctuation.separator.parameter.tsx", + "match": "," }, { "include": "#identifiers" @@ -2875,6 +3099,17 @@ "name": "keyword.operator.expression.void.tsx", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))void(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+(const)(?=\\s*($|[;,:})\\]]))", + "captures": { + "1": { + "name": "keyword.control.as.tsx" + }, + "2": { + "name": "storage.modifier.tsx" + } + } + }, { "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(as)\\s+", "beginCaptures": { @@ -3006,18 +3241,33 @@ "patterns": [ { "name": "constant.numeric.hex.tsx", - "match": "\\b(?<!\\$)0(x|X)[0-9a-fA-F][0-9a-fA-F_]*\\b(?!\\$)" + "match": "\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$)", + "captures": { + "1": { + "name": "storage.type.numeric.bigint.tsx" + } + } }, { "name": "constant.numeric.binary.tsx", - "match": "\\b(?<!\\$)0(b|B)[01][01_]*\\b(?!\\$)" + "match": "\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$)", + "captures": { + "1": { + "name": "storage.type.numeric.bigint.tsx" + } + } }, { "name": "constant.numeric.octal.tsx", - "match": "\\b(?<!\\$)0(o|O)?[0-7][0-7_]*\\b(?!\\$)" + "match": "\\b(?<!\\$)0(?:o|O)?[0-7][0-7_]*(n)?\\b(?!\\$)", + "captures": { + "1": { + "name": "storage.type.numeric.bigint.tsx" + } + } }, { - "match": "(?x)\n(?<!\\$)(?:\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)(?!\\$)", + "match": "(?x)\n(?<!\\$)(?:\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.1E+3\n (?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1.E+3\n (?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # .1E+3\n (?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)| # 1E+3\n (?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)| # 1.1\n (?:\\b[0-9][0-9_]*(\\.)(n)?\\B)| # 1.\n (?:\\B(\\.)[0-9][0-9_]*(n)?\\b)| # .1\n (?:\\b[0-9][0-9_]*(n)?\\b(?!\\.)) # 1\n)(?!\\$)", "captures": { "0": { "name": "constant.numeric.decimal.tsx" @@ -3026,19 +3276,43 @@ "name": "meta.delimiter.decimal.period.tsx" }, "2": { - "name": "meta.delimiter.decimal.period.tsx" + "name": "storage.type.numeric.bigint.tsx" }, "3": { "name": "meta.delimiter.decimal.period.tsx" }, "4": { - "name": "meta.delimiter.decimal.period.tsx" + "name": "storage.type.numeric.bigint.tsx" }, "5": { "name": "meta.delimiter.decimal.period.tsx" }, "6": { + "name": "storage.type.numeric.bigint.tsx" + }, + "7": { + "name": "storage.type.numeric.bigint.tsx" + }, + "8": { "name": "meta.delimiter.decimal.period.tsx" + }, + "9": { + "name": "storage.type.numeric.bigint.tsx" + }, + "10": { + "name": "meta.delimiter.decimal.period.tsx" + }, + "11": { + "name": "storage.type.numeric.bigint.tsx" + }, + "12": { + "name": "meta.delimiter.decimal.period.tsx" + }, + "13": { + "name": "storage.type.numeric.bigint.tsx" + }, + "14": { + "name": "storage.type.numeric.bigint.tsx" } } } @@ -3092,7 +3366,7 @@ }, { "name": "support.class.builtin.tsx", - "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Array|ArrayBuffer|Atomics|Boolean|DataView|Date|Float32Array|Float64Array|Function|Generator\n |GeneratorFunction|Int8Array|Int16Array|Int32Array|Intl|Map|Number|Object|Proxy\n |Reflect|RegExp|Set|SharedArrayBuffer|SIMD|String|Symbol|TypedArray\n |Uint8Array|Uint16Array|Uint32Array|Uint8ClampedArray|WeakMap|WeakSet)\\b(?!\\$)" + "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Array|ArrayBuffer|Atomics|BigInt|BigInt64Array|BigUint64Array|Boolean|DataView|Date|Float32Array\n |Float64Array|Function|Generator|GeneratorFunction|Int8Array|Int16Array|Int32Array|Intl|Map|Number|Object|Proxy\n |Reflect|RegExp|Set|SharedArrayBuffer|SIMD|String|Symbol|TypedArray\n |Uint8Array|Uint16Array|Uint32Array|Uint8ClampedArray|WeakMap|WeakSet)\\b(?!\\$)" }, { "name": "support.class.error.tsx", @@ -3195,7 +3469,7 @@ } }, { - "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s* (?:\n (constructor|length|prototype|__proto__)\n |\n (EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY))\\b(?!\\$)", + "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s* (?:\n (?:(constructor|length|prototype|__proto__)\\b(?!\\$|\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\())\n |\n (?:(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\b(?!\\$)))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3211,34 +3485,6 @@ } } }, - { - "match": "(?x) (?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.)) \\b (?:\n (document|event|navigator|performance|screen|window)\n |\n (AnalyserNode|ArrayBufferView|Attr|AudioBuffer|AudioBufferSourceNode|AudioContext|AudioDestinationNode|AudioListener\n |AudioNode|AudioParam|BatteryManager|BeforeUnloadEvent|BiquadFilterNode|Blob|BufferSource|ByteString|CSS|CSSConditionRule\n |CSSCounterStyleRule|CSSGroupingRule|CSSMatrix|CSSMediaRule|CSSPageRule|CSSPrimitiveValue|CSSRule|CSSRuleList|CSSStyleDeclaration\n |CSSStyleRule|CSSStyleSheet|CSSSupportsRule|CSSValue|CSSValueList|CanvasGradient|CanvasImageSource|CanvasPattern\n |CanvasRenderingContext2D|ChannelMergerNode|ChannelSplitterNode|CharacterData|ChromeWorker|CloseEvent|Comment|CompositionEvent\n |Console|ConvolverNode|Coordinates|Credential|CredentialsContainer|Crypto|CryptoKey|CustomEvent|DOMError|DOMException\n |DOMHighResTimeStamp|DOMImplementation|DOMString|DOMStringList|DOMStringMap|DOMTimeStamp|DOMTokenList|DataTransfer\n |DataTransferItem|DataTransferItemList|DedicatedWorkerGlobalScope|DelayNode|DeviceProximityEvent|DirectoryEntry\n |DirectoryEntrySync|DirectoryReader|DirectoryReaderSync|Document|DocumentFragment|DocumentTouch|DocumentType|DragEvent\n |DynamicsCompressorNode|Element|Entry|EntrySync|ErrorEvent|Event|EventListener|EventSource|EventTarget|FederatedCredential\n |FetchEvent|File|FileEntry|FileEntrySync|FileException|FileList|FileReader|FileReaderSync|FileSystem|FileSystemSync\n |FontFace|FormData|GainNode|Gamepad|GamepadButton|GamepadEvent|Geolocation|GlobalEventHandlers|HTMLAnchorElement\n |HTMLAreaElement|HTMLAudioElement|HTMLBRElement|HTMLBaseElement|HTMLBodyElement|HTMLButtonElement|HTMLCanvasElement\n |HTMLCollection|HTMLContentElement|HTMLDListElement|HTMLDataElement|HTMLDataListElement|HTMLDialogElement|HTMLDivElement\n |HTMLDocument|HTMLElement|HTMLEmbedElement|HTMLFieldSetElement|HTMLFontElement|HTMLFormControlsCollection|HTMLFormElement\n |HTMLHRElement|HTMLHeadElement|HTMLHeadingElement|HTMLHtmlElement|HTMLIFrameElement|HTMLImageElement|HTMLInputElement\n |HTMLKeygenElement|HTMLLIElement|HTMLLabelElement|HTMLLegendElement|HTMLLinkElement|HTMLMapElement|HTMLMediaElement\n |HTMLMetaElement|HTMLMeterElement|HTMLModElement|HTMLOListElement|HTMLObjectElement|HTMLOptGroupElement|HTMLOptionElement\n |HTMLOptionsCollection|HTMLOutputElement|HTMLParagraphElement|HTMLParamElement|HTMLPreElement|HTMLProgressElement\n |HTMLQuoteElement|HTMLScriptElement|HTMLSelectElement|HTMLShadowElement|HTMLSourceElement|HTMLSpanElement|HTMLStyleElement\n |HTMLTableCaptionElement|HTMLTableCellElement|HTMLTableColElement|HTMLTableDataCellElement|HTMLTableElement|HTMLTableHeaderCellElement\n |HTMLTableRowElement|HTMLTableSectionElement|HTMLTextAreaElement|HTMLTimeElement|HTMLTitleElement|HTMLTrackElement\n |HTMLUListElement|HTMLUnknownElement|HTMLVideoElement|HashChangeEvent|History|IDBCursor|IDBCursorWithValue|IDBDatabase\n |IDBEnvironment|IDBFactory|IDBIndex|IDBKeyRange|IDBMutableFile|IDBObjectStore|IDBOpenDBRequest|IDBRequest|IDBTransaction\n |IDBVersionChangeEvent|IIRFilterNode|IdentityManager|ImageBitmap|ImageBitmapFactories|ImageData|Index|InputDeviceCapabilities\n |InputEvent|InstallEvent|InstallTrigger|KeyboardEvent|LinkStyle|LocalFileSystem|LocalFileSystemSync|Location|MIDIAccess\n |MIDIConnectionEvent|MIDIInput|MIDIInputMap|MIDIOutputMap|MediaElementAudioSourceNode|MediaError|MediaKeyMessageEvent\n |MediaKeySession|MediaKeyStatusMap|MediaKeySystemAccess|MediaKeySystemConfiguration|MediaKeys|MediaRecorder|MediaStream\n |MediaStreamAudioDestinationNode|MediaStreamAudioSourceNode|MessageChannel|MessageEvent|MessagePort|MouseEvent\n |MutationObserver|MutationRecord|NamedNodeMap|Navigator|NavigatorConcurrentHardware|NavigatorGeolocation|NavigatorID\n |NavigatorLanguage|NavigatorOnLine|Node|NodeFilter|NodeIterator|NodeList|NonDocumentTypeChildNode|Notification\n |OfflineAudioCompletionEvent|OfflineAudioContext|OscillatorNode|PageTransitionEvent|PannerNode|ParentNode|PasswordCredential\n |Path2D|PaymentAddress|PaymentRequest|PaymentResponse|Performance|PerformanceEntry|PerformanceFrameTiming|PerformanceMark\n |PerformanceMeasure|PerformanceNavigation|PerformanceNavigationTiming|PerformanceObserver|PerformanceObserverEntryList\n |PerformanceResourceTiming|PerformanceTiming|PeriodicSyncEvent|PeriodicWave|Plugin|Point|PointerEvent|PopStateEvent\n |PortCollection|Position|PositionError|PositionOptions|PresentationConnectionClosedEvent|PresentationConnectionList\n |PresentationReceiver|ProcessingInstruction|ProgressEvent|PromiseRejectionEvent|PushEvent|PushRegistrationManager\n |RTCCertificate|RTCConfiguration|RTCPeerConnection|RTCSessionDescriptionCallback|RTCStatsReport|RadioNodeList|RandomSource\n |Range|ReadableByteStream|RenderingContext|SVGAElement|SVGAngle|SVGAnimateColorElement|SVGAnimateElement|SVGAnimateMotionElement\n |SVGAnimateTransformElement|SVGAnimatedAngle|SVGAnimatedBoolean|SVGAnimatedEnumeration|SVGAnimatedInteger|SVGAnimatedLength\n |SVGAnimatedLengthList|SVGAnimatedNumber|SVGAnimatedNumberList|SVGAnimatedPoints|SVGAnimatedPreserveAspectRatio\n |SVGAnimatedRect|SVGAnimatedString|SVGAnimatedTransformList|SVGAnimationElement|SVGCircleElement|SVGClipPathElement\n |SVGCursorElement|SVGDefsElement|SVGDescElement|SVGElement|SVGEllipseElement|SVGEvent|SVGFilterElement|SVGFontElement\n |SVGFontFaceElement|SVGFontFaceFormatElement|SVGFontFaceNameElement|SVGFontFaceSrcElement|SVGFontFaceUriElement\n |SVGForeignObjectElement|SVGGElement|SVGGlyphElement|SVGGradientElement|SVGHKernElement|SVGImageElement|SVGLength\n |SVGLengthList|SVGLineElement|SVGLinearGradientElement|SVGMPathElement|SVGMaskElement|SVGMatrix|SVGMissingGlyphElement\n |SVGNumber|SVGNumberList|SVGPathElement|SVGPatternElement|SVGPoint|SVGPolygonElement|SVGPolylineElement|SVGPreserveAspectRatio\n |SVGRadialGradientElement|SVGRect|SVGRectElement|SVGSVGElement|SVGScriptElement|SVGSetElement|SVGStopElement|SVGStringList\n |SVGStylable|SVGStyleElement|SVGSwitchElement|SVGSymbolElement|SVGTRefElement|SVGTSpanElement|SVGTests|SVGTextElement\n |SVGTextPositioningElement|SVGTitleElement|SVGTransform|SVGTransformList|SVGTransformable|SVGUseElement|SVGVKernElement\n |SVGViewElement|ServiceWorker|ServiceWorkerContainer|ServiceWorkerGlobalScope|ServiceWorkerRegistration|ServiceWorkerState\n |ShadowRoot|SharedWorker|SharedWorkerGlobalScope|SourceBufferList|StereoPannerNode|Storage|StorageEvent|StyleSheet\n |StyleSheetList|SubtleCrypto|SyncEvent|Text|TextMetrics|TimeEvent|TimeRanges|Touch|TouchEvent|TouchList|Transferable\n |TreeWalker|UIEvent|USVString|VRDisplayCapabilities|ValidityState|WaveShaperNode|WebGL|WebGLActiveInfo|WebGLBuffer\n |WebGLContextEvent|WebGLFramebuffer|WebGLProgram|WebGLRenderbuffer|WebGLRenderingContext|WebGLShader|WebGLShaderPrecisionFormat\n |WebGLTexture|WebGLTimerQueryEXT|WebGLTransformFeedback|WebGLUniformLocation|WebGLVertexArrayObject|WebGLVertexArrayObjectOES\n |WebSocket|WebSockets|WebVTT|WheelEvent|Window|WindowBase64|WindowEventHandlers|WindowTimers|Worker|WorkerGlobalScope\n |WorkerLocation|WorkerNavigator|XMLHttpRequest|XMLHttpRequestEventTarget|XMLSerializer|XPathExpression|XPathResult\n |XSLTProcessor))\\b(?!\\$)", - "captures": { - "1": { - "name": "support.variable.dom.tsx" - }, - "2": { - "name": "support.class.dom.tsx" - } - } - }, - { - "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s* (?:\n (ATTRIBUTE_NODE|CDATA_SECTION_NODE|COMMENT_NODE|DOCUMENT_FRAGMENT_NODE|DOCUMENT_NODE|DOCUMENT_TYPE_NODE\n |DOMSTRING_SIZE_ERR|ELEMENT_NODE|ENTITY_NODE|ENTITY_REFERENCE_NODE|HIERARCHY_REQUEST_ERR|INDEX_SIZE_ERR\n |INUSE_ATTRIBUTE_ERR|INVALID_CHARACTER_ERR|NO_DATA_ALLOWED_ERR|NO_MODIFICATION_ALLOWED_ERR|NOT_FOUND_ERR\n |NOT_SUPPORTED_ERR|NOTATION_NODE|PROCESSING_INSTRUCTION_NODE|TEXT_NODE|WRONG_DOCUMENT_ERR)\n |\n (_content|[xyz]|abbr|above|accept|acceptCharset|accessKey|action|align|[av]Link(?:color)?|all|alt|anchors|appCodeName\n |appCore|applets|appMinorVersion|appName|appVersion|archive|areas|arguments|attributes|availHeight|availLeft|availTop\n |availWidth|axis|background|backgroundColor|backgroundImage|below|bgColor|body|border|borderBottomWidth|borderColor\n |borderLeftWidth|borderRightWidth|borderStyle|borderTopWidth|borderWidth|bottom|bufferDepth|callee|caller|caption\n |cellPadding|cells|cellSpacing|ch|characterSet|charset|checked|childNodes|chOff|cite|classes|className|clear\n |clientInformation|clip|clipBoardData|closed|code|codeBase|codeType|color|colorDepth|cols|colSpan|compact|complete\n |components|content|controllers|cookie|cookieEnabled|cords|cpuClass|crypto|current|data|dateTime|declare|defaultCharset\n |defaultChecked|defaultSelected|defaultStatus|defaultValue|defaultView|defer|description|dialogArguments|dialogHeight\n |dialogLeft|dialogTop|dialogWidth|dir|directories|disabled|display|docmain|doctype|documentElement|elements|embeds\n |enabledPlugin|encoding|enctype|entities|event|expando|external|face|fgColor|filename|firstChild|fontFamily|fontSize\n |fontWeight|form|formName|forms|frame|frameBorder|frameElement|frames|hasFocus|hash|headers|height|history|host\n |hostname|href|hreflang|hspace|htmlFor|httpEquiv|id|ids|ignoreCase|images|implementation|index|innerHeight|innerWidth\n |input|isMap|label|lang|language|lastChild|lastIndex|lastMatch|lastModified|lastParen|layer[sXY]|left|leftContext\n |lineHeight|link|linkColor|links|listStyleType|localName|location|locationbar|longDesc|lowsrc|lowSrc|marginBottom\n |marginHeight|marginLeft|marginRight|marginTop|marginWidth|maxLength|media|menubar|method|mimeTypes|multiline|multiple\n |name|nameProp|namespaces|namespaceURI|next|nextSibling|nodeName|nodeType|nodeValue|noHref|noResize|noShade|notationName\n |notations|noWrap|object|offscreenBuffering|onLine|onreadystatechange|opener|opsProfile|options|oscpu|outerHeight\n |outerWidth|ownerDocument|paddingBottom|paddingLeft|paddingRight|paddingTop|page[XY]|page[XY]Offset|parent|parentLayer\n |parentNode|parentWindow|pathname|personalbar|pixelDepth|pkcs11|platform|plugins|port|prefix|previous|previousDibling\n |product|productSub|profile|profileend|prompt|prompter|protocol|publicId|readOnly|readyState|referrer|rel|responseText\n |responseXML|rev|right|rightContext|rowIndex|rows|rowSpan|rules|scheme|scope|screen[XY]|screenLeft|screenTop|scripts\n |scrollbars|scrolling|sectionRowIndex|security|securityPolicy|selected|selectedIndex|selection|self|shape|siblingAbove\n |siblingBelow|size|source|specified|standby|start|status|statusbar|statusText|style|styleSheets|suffixes|summary\n |systemId|systemLanguage|tagName|tags|target|tBodies|text|textAlign|textDecoration|textIndent|textTransform|tFoot|tHead\n |title|toolbar|top|type|undefined|uniqueID|updateInterval|URL|URLUnencoded|useMap|userAgent|userLanguage|userProfile\n |vAlign|value|valueType|vendor|vendorSub|version|visibility|vspace|whiteSpace|width|X[MS]LDocument|zIndex))\\b(?!\\$|\\s*(<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\\()", - "captures": { - "1": { - "name": "punctuation.accessor.tsx" - }, - "2": { - "name": "punctuation.accessor.optional.tsx" - }, - "3": { - "name": "support.constant.dom.tsx" - }, - "4": { - "name": "support.variable.property.dom.tsx" - } - } - }, { "name": "support.class.node.tsx", "match": "(?x)(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(Buffer|EventEmitter|Server|Pipe|Socket|REPLServer|ReadStream|WriteStream|Stream\n |Inflate|Deflate|InflateRaw|DeflateRaw|GZip|GUnzip|Unzip|Zip)\\b(?!\\$)" @@ -3286,29 +3532,6 @@ { "name": "support.variable.object.node.tsx", "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(global|GLOBAL|root|__dirname|__filename)\\b(?!\\$)" - }, - { - "match": "(?x) (?:(\\.)|(\\?\\.(?!\\s*[[:digit:]]))) \\s*\n(?:\n (on(?:Rowsinserted|Rowsdelete|Rowenter|Rowexit|Resize|Resizestart|Resizeend|Reset|\n Readystatechange|Mouseout|Mouseover|Mousedown|Mouseup|Mousemove|\n Before(?:cut|deactivate|unload|update|paste|print|editfocus|activate)|\n Blur|Scrolltop|Submit|Select|Selectstart|Selectionchange|Hover|Help|\n Change|Contextmenu|Controlselect|Cut|Cellchange|Clock|Close|Deactivate|\n Datasetchanged|Datasetcomplete|Dataavailable|Drop|Drag|Dragstart|Dragover|\n Dragdrop|Dragenter|Dragend|Dragleave|Dblclick|Unload|Paste|Propertychange|Error|\n Errorupdate|Keydown|Keyup|Keypress|Focus|Load|Activate|Afterupdate|Afterprint|Abort)\n ) |\n (shift|showModelessDialog|showModalDialog|showHelp|scroll|scrollX|scrollByPages|\n scrollByLines|scrollY|scrollTo|stop|strike|sizeToContent|sidebar|signText|sort|\n sup|sub|substr|substring|splice|split|send|set(?:Milliseconds|Seconds|Minutes|Hours|\n Month|Year|FullYear|Date|UTC(?:Milliseconds|Seconds|Minutes|Hours|Month|FullYear|Date)|\n Time|Hotkeys|Cursor|ZOptions|Active|Resizable|RequestHeader)|search|slice|\n savePreferences|small|home|handleEvent|navigate|char|charCodeAt|charAt|concat|\n contextual|confirm|compile|clear|captureEvents|call|createStyleSheet|createPopup|\n createEventObject|to(?:GMTString|UTCString|String|Source|UpperCase|LowerCase|LocaleString)|\n test|taint|taintEnabled|indexOf|italics|disableExternalCapture|dump|detachEvent|unshift|\n untaint|unwatch|updateCommands|join|javaEnabled|pop|push|plugins.refresh|paddings|parse|\n print|prompt|preference|enableExternalCapture|exec|execScript|valueOf|UTC|find|file|\n fileModifiedDate|fileSize|fileCreatedDate|fileUpdatedDate|fixed|fontsize|fontcolor|\n forward|fromCharCode|watch|link|load|lastIndexOf|anchor|attachEvent|atob|apply|alert|\n abort|routeEvents|resize|resizeBy|resizeTo|recalc|returnValue|replace|reverse|reload|\n releaseCapture|releaseEvents|go|get(?:Milliseconds|Seconds|Minutes|Hours|Month|Day|Year|FullYear|\n Time|Date|TimezoneOffset|UTC(?:Milliseconds|Seconds|Minutes|Hours|Day|Month|FullYear|Date)|\n Attention|Selection|ResponseHeader|AllResponseHeaders)|moveBy|moveBelow|moveTo|\n moveToAbsolute|moveAbove|mergeAttributes|match|margins|btoa|big|bold|borderWidths|blink|back\n ) |\n (acceptNode|add|addEventListener|addTextTrack|adoptNode|after|animate|append|\n appendChild|appendData|before|blur|canPlayType|captureStream|\n caretPositionFromPoint|caretRangeFromPoint|checkValidity|clear|click|\n cloneContents|cloneNode|cloneRange|close|closest|collapse|\n compareBoundaryPoints|compareDocumentPosition|comparePoint|contains|\n convertPointFromNode|convertQuadFromNode|convertRectFromNode|createAttribute|\n createAttributeNS|createCaption|createCDATASection|createComment|\n createContextualFragment|createDocument|createDocumentFragment|\n createDocumentType|createElement|createElementNS|createEntityReference|\n createEvent|createExpression|createHTMLDocument|createNodeIterator|\n createNSResolver|createProcessingInstruction|createRange|createShadowRoot|\n createTBody|createTextNode|createTFoot|createTHead|createTreeWalker|delete|\n deleteCaption|deleteCell|deleteContents|deleteData|deleteRow|deleteTFoot|\n deleteTHead|detach|disconnect|dispatchEvent|elementFromPoint|elementsFromPoint|\n enableStyleSheetsForSet|entries|evaluate|execCommand|exitFullscreen|\n exitPointerLock|expand|extractContents|fastSeek|firstChild|focus|forEach|get|\n getAll|getAnimations|getAttribute|getAttributeNames|getAttributeNode|\n getAttributeNodeNS|getAttributeNS|getBoundingClientRect|getBoxQuads|\n getClientRects|getContext|getDestinationInsertionPoints|getElementById|\n getElementsByClassName|getElementsByName|getElementsByTagName|\n getElementsByTagNameNS|getItem|getNamedItem|getSelection|getStartDate|\n getVideoPlaybackQuality|has|hasAttribute|hasAttributeNS|hasAttributes|\n hasChildNodes|hasFeature|hasFocus|importNode|initEvent|insertAdjacentElement|\n insertAdjacentHTML|insertAdjacentText|insertBefore|insertCell|insertData|\n insertNode|insertRow|intersectsNode|isDefaultNamespace|isEqualNode|\n isPointInRange|isSameNode|item|key|keys|lastChild|load|lookupNamespaceURI|\n lookupPrefix|matches|move|moveAttribute|moveAttributeNode|moveChild|\n moveNamedItem|namedItem|nextNode|nextSibling|normalize|observe|open|\n parentNode|pause|play|postMessage|prepend|preventDefault|previousNode|\n previousSibling|probablySupportsContext|queryCommandEnabled|\n queryCommandIndeterm|queryCommandState|queryCommandSupported|queryCommandValue|\n querySelector|querySelectorAll|registerContentHandler|registerElement|\n registerProtocolHandler|releaseCapture|releaseEvents|remove|removeAttribute|\n removeAttributeNode|removeAttributeNS|removeChild|removeEventListener|\n removeItem|replace|replaceChild|replaceData|replaceWith|reportValidity|\n requestFullscreen|requestPointerLock|reset|scroll|scrollBy|scrollIntoView|\n scrollTo|seekToNextFrame|select|selectNode|selectNodeContents|set|setAttribute|\n setAttributeNode|setAttributeNodeNS|setAttributeNS|setCapture|\n setCustomValidity|setEnd|setEndAfter|setEndBefore|setItem|setNamedItem|\n setRangeText|setSelectionRange|setSinkId|setStart|setStartAfter|setStartBefore|\n slice|splitText|stepDown|stepUp|stopImmediatePropagation|stopPropagation|\n submit|substringData|supports|surroundContents|takeRecords|terminate|toBlob|\n toDataURL|toggle|toString|values|write|writeln\n ) |\n (all|catch|finally|race|reject|resolve|then\n )\n)(?=\\s*\\()", - "captures": { - "1": { - "name": "punctuation.accessor.tsx" - }, - "2": { - "name": "punctuation.accessor.optional.tsx" - }, - "3": { - "name": "support.function.event-handler.tsx" - }, - "4": { - "name": "support.function.tsx" - }, - "5": { - "name": "support.function.dom.tsx" - }, - "6": { - "name": "support.function.promise.tsx" - } - } } ] }, @@ -3318,7 +3541,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\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*(\\/\\*([^\\*]|(\\*[^\\/]))*\\*\\/\\s*)*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3439,6 +3662,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.tsx", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.tsx" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { @@ -3592,6 +3834,13 @@ "name": "punctuation.definition.typeparameters.end.tsx" } }, + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + "type-arguments-body": { "patterns": [ { "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(_)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))", @@ -3627,7 +3876,7 @@ "include": "#type-builtin-literals" }, { - "include": "#type-arguments" + "include": "#type-parameters" }, { "include": "#type-tuple" @@ -3650,6 +3899,14 @@ { "include": "#type-function-return-type" }, + { + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(readonly)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))\\s*", + "captures": { + "1": { + "name": "storage.modifier.tsx" + } + } + }, { "include": "#type-name" } @@ -3657,7 +3914,7 @@ }, "type-primitive": { "name": "support.type.primitive.tsx", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(string|number|boolean|symbol|any|void|never|unknown)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" + "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(string|number|bigint|boolean|symbol|any|void|never|unknown)(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.))" }, "type-builtin-literals": { "name": "support.type.builtin.tsx", @@ -3851,13 +4108,21 @@ "type-fn-type-parameters": { "patterns": [ { - "name": "meta.type.constructor.tsx", - "match": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(new)\\b(?=\\s*\\<)", - "captures": { + "begin": "(?<![_$[:alnum:]])(?:(?<=\\.\\.\\.)|(?<!\\.))(new)\\b(?=\\s*\\<)", + "beginCaptures": { "1": { - "name": "keyword.control.new.tsx" + "name": "meta.type.constructor.tsx keyword.control.new.tsx" } - } + }, + "end": "(?<=>)", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#type-parameters" + } + ] }, { "name": "meta.type.constructor.tsx", @@ -3994,6 +4259,58 @@ }, "type-name": { "patterns": [ + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(<)", + "captures": { + "1": { + "name": "entity.name.type.module.tsx" + }, + "2": { + "name": "punctuation.accessor.tsx" + }, + "3": { + "name": "punctuation.accessor.optional.tsx" + }, + "4": { + "name": "meta.type.parameters.tsx punctuation.definition.typeparameters.begin.tsx" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.tsx punctuation.definition.typeparameters.end.tsx" + } + }, + "contentName": "meta.type.parameters.tsx", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, + { + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(<)", + "beginCaptures": { + "1": { + "name": "entity.name.type.tsx" + }, + "2": { + "name": "meta.type.parameters.tsx punctuation.definition.typeparameters.begin.tsx" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "meta.type.parameters.tsx punctuation.definition.typeparameters.end.tsx" + } + }, + "contentName": "meta.type.parameters.tsx", + "patterns": [ + { + "include": "#type-arguments-body" + } + ] + }, { "match": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))", "captures": { @@ -4100,7 +4417,30 @@ "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*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "end": "(?=`)", + "patterns": [ + { + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", + "end": "(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)?`)", + "patterns": [ + { + "include": "#support-function-call-identifiers" + }, + { + "name": "entity.name.function.tagged-template.tsx", + "match": "([_$[:alpha:]][_$[:alnum:]]*)" + } + ] + }, + { + "include": "#type-arguments" + } + ] + }, + { + "name": "string.template.tsx", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?<!=)\\>))*(?<!=)\\>)*(?<!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.tsx" @@ -4166,7 +4506,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" @@ -4189,7 +4529,7 @@ }, { "name": "string.regexp.tsx", - "begin": "(?<![_$[:alnum:])\\]]|\\+\\+|--|})\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "((?<![_$[:alnum:])\\]]|\\+\\+|--|}|\\*\\/)|((?<=^return|[^\\._$[:alnum:]]return|^case|[^\\._$[:alnum:]]case))\\s*)\\/(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.tsx" @@ -4819,6 +5159,17 @@ }, { "include": "#inline-tags" + }, + { + "match": "((@)(?:[_$[:alpha:]][_$[:alnum:]]*))(?=\\s+)", + "captures": { + "1": { + "name": "storage.type.class.jsdoc" + }, + "2": { + "name": "punctuation.definition.block.tag.jsdoc" + } + } } ] }, @@ -4907,10 +5258,6 @@ }, "jsdoctype": { "patterns": [ - { - "name": "invalid.illegal.type.jsdoc", - "match": "\\G{(?:[^}*]|\\*[^/}])+$" - }, { "contentName": "entity.name.type.instance.jsdoc", "begin": "\\G({)", diff --git a/extensions/typescript-basics/syntaxes/jsdoc.injection.tmLanguage.json b/extensions/typescript-basics/syntaxes/jsdoc.injection.tmLanguage.json new file mode 100644 index 00000000000..b7b4db2d2a5 --- /dev/null +++ b/extensions/typescript-basics/syntaxes/jsdoc.injection.tmLanguage.json @@ -0,0 +1,50 @@ +{ + "injectionSelector": "L:comment.block.documentation", + "patterns": [ + { + "include": "#jsdocbody" + } + ], + "repository": { + "jsdocbody": { + "begin": "(?<=/\\*\\*)([^*]|\\*(?!/))*$", + "while": "(^|\\G)\\s*\\*(?!/)(?=([^*]|[*](?!/))*$)", + "patterns": [ + { + "include": "text.html.markdown#fenced_code_block" + }, + { + "include": "text.html.markdown#lists" + }, + { + "include": "#example" + }, + { + "include": "source.ts#docblock" + }, + { + "include": "text.html.markdown#inline" + } + ] + }, + "example": { + "begin": "((@)example)\\s+(?=([^*]|[*](?!/))*$).*$", + "while": "(^|\\G)\\s(?!@)(?=([^*]|[*](?!/))*$)", + "beginCaptures": { + "1": { + "name": "storage.type.class.jsdoc" + }, + "2": { + "name": "punctuation.definition.block.tag.jsdoc" + } + }, + "contentName": "meta.embedded.block.example.source.ts", + "patterns": [ + { + "include": "source.tsx" + } + ] + } + }, + "scopeName": "documentation.injection" +} \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-example.ts b/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-example.ts new file mode 100644 index 00000000000..411f9bbede3 --- /dev/null +++ b/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-example.ts @@ -0,0 +1,8 @@ +/** + * @example + * 1 + 1 + * + * @other + * not colored + */ +const a = 1 \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-markdown.ts b/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-markdown.ts new file mode 100644 index 00000000000..136f8b1bafb --- /dev/null +++ b/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-markdown.ts @@ -0,0 +1,7 @@ +/** + * **Bold** + * ```js + * 1 + code + * ``` + */ +const a = 1 \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-multiline-type.ts b/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-multiline-type.ts new file mode 100644 index 00000000000..eda831efe02 --- /dev/null +++ b/extensions/typescript-basics/test/colorize-fixtures/test-jsdoc-multiline-type.ts @@ -0,0 +1,22 @@ +/** + * @typedef {{ + * id: number, + * fn: !Function, + * context: (!Object|undefined) + * }} + * @private + */ +goog.dom.animationFrame.Task_; + + +/** + * @typedef {{ + * measureTask: goog.dom.animationFrame.Task_, + * mutateTask: goog.dom.animationFrame.Task_, + * state: (!Object|undefined), + * args: (!Array|undefined), + * isScheduled: boolean + * }} + * @private + */ +goog.dom.animationFrame.TaskSet_; \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-results/test-function-inv_ts.json b/extensions/typescript-basics/test/colorize-results/test-function-inv_ts.json index c99ec3d0ce2..a6f0571c6e5 100644 --- a/extensions/typescript-basics/test/colorize-results/test-function-inv_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-function-inv_ts.json @@ -23,13 +23,13 @@ }, { "c": "push", - "t": "source.ts meta.function-call.ts support.function.ts", + "t": "source.ts meta.function-call.ts entity.name.function.ts", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { diff --git a/extensions/typescript-basics/test/colorize-results/test-jsdoc-example_ts.json b/extensions/typescript-basics/test/colorize-results/test-jsdoc-example_ts.json new file mode 100644 index 00000000000..ea15234c214 --- /dev/null +++ b/extensions/typescript-basics/test/colorize-results/test-jsdoc-example_ts.json @@ -0,0 +1,277 @@ +[ + { + "c": "/**", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " * ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "@", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc punctuation.definition.block.tag.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "example", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts meta.embedded.block.example.source.ts", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.ts comment.block.documentation.ts meta.embedded.block.example.source.ts constant.numeric.decimal.tsx", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts meta.embedded.block.example.source.ts", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "+", + "t": "source.ts comment.block.documentation.ts meta.embedded.block.example.source.ts keyword.operator.arithmetic.tsx", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts meta.embedded.block.example.source.ts", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.ts comment.block.documentation.ts meta.embedded.block.example.source.ts constant.numeric.decimal.tsx", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " * ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "@", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc punctuation.definition.block.tag.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "other", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " * not colored", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "*/", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "a", + "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", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ts meta.var.expr.ts keyword.operator.assignment.ts", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.ts meta.var.expr.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + } +] \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-results/test-jsdoc-markdown_ts.json b/extensions/typescript-basics/test/colorize-results/test-jsdoc-markdown_ts.json new file mode 100644 index 00000000000..eea45a645eb --- /dev/null +++ b/extensions/typescript-basics/test/colorize-results/test-jsdoc-markdown_ts.json @@ -0,0 +1,310 @@ +[ + { + "c": "/**", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " * ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "**", + "t": "source.ts comment.block.documentation.ts markup.bold.markdown punctuation.definition.bold.markdown", + "r": { + "dark_plus": "markup.bold: #569CD6", + "light_plus": "markup.bold: #000080", + "dark_vs": "markup.bold: #569CD6", + "light_vs": "markup.bold: #000080", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "Bold", + "t": "source.ts comment.block.documentation.ts markup.bold.markdown", + "r": { + "dark_plus": "markup.bold: #569CD6", + "light_plus": "markup.bold: #000080", + "dark_vs": "markup.bold: #569CD6", + "light_vs": "markup.bold: #000080", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "**", + "t": "source.ts comment.block.documentation.ts markup.bold.markdown punctuation.definition.bold.markdown", + "r": { + "dark_plus": "markup.bold: #569CD6", + "light_plus": "markup.bold: #000080", + "dark_vs": "markup.bold: #569CD6", + "light_vs": "markup.bold: #000080", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "```", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown punctuation.definition.markdown", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "js", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown meta.embedded.block.javascript", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown meta.embedded.block.javascript constant.numeric.decimal.js", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown meta.embedded.block.javascript", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "+", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown meta.embedded.block.javascript keyword.operator.arithmetic.js", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown meta.embedded.block.javascript", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "code", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown meta.embedded.block.javascript variable.other.readwrite.js", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "```", + "t": "source.ts comment.block.documentation.ts markup.fenced_code.block.markdown punctuation.definition.markdown", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "*/", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "a", + "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", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "=", + "t": "source.ts meta.var.expr.ts keyword.operator.assignment.ts", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.ts meta.var.expr.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #09885A", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #09885A", + "hc_black": "constant.numeric: #B5CEA8" + } + } +] \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-results/test-jsdoc-multiline-type_ts.json b/extensions/typescript-basics/test/colorize-results/test-jsdoc-multiline-type_ts.json new file mode 100644 index 00000000000..cc1eba5f197 --- /dev/null +++ b/extensions/typescript-basics/test/colorize-results/test-jsdoc-multiline-type_ts.json @@ -0,0 +1,684 @@ +[ + { + "c": "/**", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " * ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "@", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc punctuation.definition.block.tag.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "typedef", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "{", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc punctuation.definition.bracket.curly.begin.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " id: number,", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " fn: !Function,", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " context: (!Object|undefined)", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " }", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "}", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc punctuation.definition.bracket.curly.end.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " * ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "@", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc punctuation.definition.block.tag.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "private", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "*/", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "goog", + "t": "source.ts variable.other.object.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "dom", + "t": "source.ts variable.other.object.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "animationFrame", + "t": "source.ts variable.other.object.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "Task_", + "t": "source.ts variable.other.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/**", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " * ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "@", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc punctuation.definition.block.tag.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "typedef", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "{", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc punctuation.definition.bracket.curly.begin.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "{", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " measureTask: goog.dom.animationFrame.Task_,", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " mutateTask: goog.dom.animationFrame.Task_,", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " state: (!Object|undefined),", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " args: (!Array|undefined),", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " isScheduled: boolean", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " *", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " }", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": "}", + "t": "source.ts comment.block.documentation.ts entity.name.type.instance.jsdoc punctuation.definition.bracket.curly.end.jsdoc", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "entity.name.type: #4EC9B0" + } + }, + { + "c": " * ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "@", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc punctuation.definition.block.tag.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "private", + "t": "source.ts comment.block.documentation.ts storage.type.class.jsdoc", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": " ", + "t": "source.ts comment.block.documentation.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "*/", + "t": "source.ts comment.block.documentation.ts punctuation.definition.comment.ts", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "goog", + "t": "source.ts variable.other.object.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "dom", + "t": "source.ts variable.other.object.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "animationFrame", + "t": "source.ts variable.other.object.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "source.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "TaskSet_", + "t": "source.ts variable.other.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + } +] \ No newline at end of file diff --git a/extensions/typescript-basics/test/colorize-results/test_ts.json b/extensions/typescript-basics/test/colorize-results/test_ts.json index bf74c1c2174..4a2773591c7 100644 --- a/extensions/typescript-basics/test/colorize-results/test_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test_ts.json @@ -9153,13 +9153,13 @@ }, { "c": "push", - "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts meta.method.declaration.ts meta.block.ts meta.block.ts meta.block.ts meta.function-call.ts support.function.ts", + "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts meta.method.declaration.ts meta.block.ts meta.block.ts meta.block.ts meta.function-call.ts entity.name.function.ts", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { @@ -9406,13 +9406,13 @@ }, { "c": "push", - "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts meta.method.declaration.ts meta.block.ts meta.block.ts meta.function-call.ts support.function.ts", + "t": "source.ts meta.namespace.declaration.ts meta.block.ts meta.class.ts meta.method.declaration.ts meta.block.ts meta.block.ts meta.function-call.ts entity.name.function.ts", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.function: #DCDCAA" + "hc_black": "entity.name.function: #DCDCAA" } }, { diff --git a/extensions/typescript-language-features/README.md b/extensions/typescript-language-features/README.md index 26942f9d460..b3a5a072e35 100644 --- a/extensions/typescript-language-features/README.md +++ b/extensions/typescript-language-features/README.md @@ -1,7 +1,7 @@ -# Language Features for Typescript and Javascript files +# Language Features for TypeScript and JavaScript files **Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. ## Features -See [Typescript in Visual Studio Code](https://code.visualstudio.com/docs/languages/typescript) and [Javascript in Visual Studio Code](https://code.visualstudio.com/docs/languages/javascript) to learn about the features of this extension. \ No newline at end of file +See [TypeScript in Visual Studio Code](https://code.visualstudio.com/docs/languages/typescript) and [JavaScript in Visual Studio Code](https://code.visualstudio.com/docs/languages/javascript) to learn about the features of this extension. diff --git a/extensions/typescript-language-features/cgmanifest.json b/extensions/typescript-language-features/cgmanifest.json index b0a5735344a..11b737dc59a 100644 --- a/extensions/typescript-language-features/cgmanifest.json +++ b/extensions/typescript-language-features/cgmanifest.json @@ -28,7 +28,8 @@ "type": "other", "other": { "name": "Unicode", - "downloadUrl": "http://www.unicode.org/" + "downloadUrl": "http://www.unicode.org/", + "version": "12.0.0" } }, "licenseDetail": [ @@ -88,6 +89,7 @@ "use or other dealings in these Data Files or Software without prior", "written authorization of the copyright holder." ], + "version": "12.0.0", "license": "UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE" }, { @@ -95,7 +97,8 @@ "type": "other", "other": { "name": "Document Object Model", - "downloadUrl": "https://www.w3.org/DOM/" + "downloadUrl": "https://www.w3.org/DOM/", + "version": "4.0.0" } }, "licenseDetail": [ @@ -116,7 +119,8 @@ "The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. ", "Title to copyright in this work will at all times remain with copyright holders." ], - "license": "W3C License" + "license": "W3C License", + "version": "4.0.0" }, { "component": { diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 92160253eba..a5147d60f8e 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -10,20 +10,22 @@ "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "enableProposedApi": true, "engines": { - "vscode": "*" + "vscode": "^1.30.0" }, "categories": [ "Programming Languages" ], "dependencies": { "jsonc-parser": "^2.0.1", + "rimraf": "^2.6.3", "semver": "5.5.1", - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "8.0.33", + "@types/node": "^12.0.2", "@types/semver": "^5.5.0", + "@types/rimraf": "2.0.2", "vscode": "^1.1.10" }, "scripts": { @@ -83,7 +85,7 @@ ], "default": null, "description": "%typescript.npm%", - "scope": "application" + "scope": "machine" }, "typescript.check.npmIsInstalled": { "type": "boolean", @@ -129,7 +131,7 @@ }, "default": [], "description": "%typescript.tsserver.pluginPaths%", - "scope": "application" + "scope": "machine" }, "typescript.tsserver.trace": { "type": "string", @@ -358,12 +360,6 @@ "description": "%format.placeOpenBraceOnNewLineForControlBlocks%", "scope": "resource" }, - "jsDocCompletion.enabled": { - "type": "boolean", - "default": true, - "description": "%jsDocCompletion.enabled%", - "scope": "resource" - }, "javascript.implicitProjectConfig.checkJs": { "type": "boolean", "default": false, @@ -424,6 +420,18 @@ "description": "%configuration.suggest.autoImports%", "scope": "resource" }, + "javascript.suggest.completeJSDocs": { + "type": "boolean", + "default": true, + "description": "%configuration.suggest.completeJSDocs%", + "scope": "resource" + }, + "typescript.suggest.completeJSDocs": { + "type": "boolean", + "default": true, + "description": "%configuration.suggest.completeJSDocs%", + "scope": "resource" + }, "typescript.locale": { "type": [ "string", @@ -512,6 +520,18 @@ "description": "%typescript.preferences.importModuleSpecifier%", "scope": "resource" }, + "javascript.preferences.renameShorthandProperties": { + "type": "boolean", + "default": true, + "description": "%typescript.preferences.renameShorthandProperties%", + "scope": "resource" + }, + "typescript.preferences.renameShorthandProperties": { + "type": "boolean", + "default": true, + "description": "%typescript.preferences.renameShorthandProperties%", + "scope": "resource" + }, "typescript.updateImportsOnFileMove.enabled": { "type": "string", "enum": [ @@ -687,7 +707,7 @@ "problemPatterns": [ { "name": "tsc", - "regexp": "^([^\\s].*)[\\(:](\\d+)[,:](\\d+)(?:\\):\\s+|\\s+-\\s+)(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", + "regexp": "^([^\\s].*)[\\(:](\\d+)[,:](\\d+)(?:\\):\\s+|\\s+-\\s+)(error|warning|info)\\s+TS(\\d+)\\s*:\\s*(.*)$", "file": 1, "line": 2, "column": 3, @@ -732,4 +752,4 @@ } ] } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 2cd347a012a..7e31edca479 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -33,11 +33,10 @@ "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. 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.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.", @@ -58,7 +57,7 @@ "typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor. Requires using TypeScript 2.8 or newer in the workspace.", "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: `single` quotes, `double` quotes, or `auto` infer quote type from existing imports. Requires using TypeScript 2.9 or newer in the workspace.", "typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports.", - "typescript.preferences.importModuleSpecifier.auto": "Infer the shortest path type.", + "typescript.preferences.importModuleSpecifier.auto": "Automatically select import path style. Prefers using a relative import if `baseUrl` is configured and the relative path has fewer segments than the non-relative import.", "typescript.preferences.importModuleSpecifier.relative": "Relative to the file location.", "typescript.preferences.importModuleSpecifier.nonRelative": "Based on the `baseUrl` configured in your `jsconfig.json` / `tsconfig.json`.", "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Requires using TypeScript 2.9 or newer in the workspace.", @@ -67,5 +66,7 @@ "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.", "typescript.suggest.enabled": "Enabled/disable autocomplete suggestions.", - "configuration.surveys.enabled": "Enabled/disable occasional surveys that help us improve VS Code's JavaScript and TypeScript support." + "configuration.surveys.enabled": "Enabled/disable occasional surveys that help us improve VS Code's JavaScript and TypeScript support.", + "configuration.suggest.completeJSDocs": "Enable/disable suggestion to complete JSDoc comments.", + "typescript.preferences.renameShorthandProperties": "Enable/disable introducing aliases for object shorthand properties during renames. Requires using TypeScript 3.4 or newer in the workspace." } \ No newline at end of file diff --git a/extensions/typescript-language-features/schemas/package.schema.json b/extensions/typescript-language-features/schemas/package.schema.json index 3e77cb4cc89..c26af63c93b 100644 --- a/extensions/typescript-language-features/schemas/package.schema.json +++ b/extensions/typescript-language-features/schemas/package.schema.json @@ -16,6 +16,11 @@ "name": { "type": "string", "description": "Name of the plugin as listed in the package.json." + }, + "enableForWorkspaceTypeScriptVersions": { + "type": "boolean", + "default": false, + "description": "Should the plugin be loaded when using workspace versions of TypeScript?" } } } diff --git a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts index 61729eafa8b..9273683ba4c 100644 --- a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts +++ b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts @@ -10,6 +10,7 @@ import { nulToken } from '../utils/cancellation'; import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile, openOrCreateConfigFile } from '../utils/tsconfig'; +import { ServerResponse } from '../typescriptService'; const localize = nls.loadMessageBundle(); @@ -68,13 +69,13 @@ async function goToProjectConfig( return; } - let res: protocol.ProjectInfoResponse | undefined; + let res: ServerResponse.Response<protocol.ProjectInfoResponse> | undefined; try { res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); } catch { // noop } - if (!res || !res.body) { + if (!res || res.type !== 'response' || !res.body) { vscode.window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project')); return; } diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index 782aa98106d..a4ec5f1bc0e 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -10,6 +10,8 @@ import { LanguageConfigurationManager } from './features/languageConfiguration'; import TypeScriptTaskProviderManager from './features/task'; import TypeScriptServiceClientHost from './typeScriptServiceClientHost'; import { flatten } from './utils/arrays'; +import * as electron from './utils/electron'; +import * as rimraf from 'rimraf'; import { CommandManager } from './utils/commandManager'; import * as fileSchemes from './utils/fileSchemes'; import { standardLanguageDescriptions } from './utils/languageDescription'; @@ -128,4 +130,8 @@ function isSupportedDocument( return false; } return fileSchemes.isSupportedScheme(document.uri.scheme); +} + +export function deactivate() { + rimraf.sync(electron.getInstanceDir()); } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts index ba25a71bcc6..d5f2f0538d0 100644 --- a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts @@ -4,10 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; -import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { ITypeScriptServiceClient } from '../typescriptService'; import { escapeRegExp } from '../utils/regexp'; import * as typeConverters from '../utils/typeConverters'; +import { CachedResponse } from '../tsServer/cachedResponse'; + +const localize = nls.loadMessageBundle(); export class ReferencesCodeLens extends vscode.CodeLens { constructor( @@ -19,38 +23,19 @@ export class ReferencesCodeLens extends vscode.CodeLens { } } -export class CachedResponse<T extends Proto.Response> { - private response?: Promise<ServerResponse<T>>; - private version: number = -1; - private document: string = ''; - - public execute( - document: vscode.TextDocument, - f: () => Promise<ServerResponse<T>> - ) { - if (this.matches(document)) { - return this.response; - } - - return this.update(document, f()); - } - - private matches(document: vscode.TextDocument): boolean { - return this.version === document.version && this.document === document.uri.toString(); - } - - private update( - document: vscode.TextDocument, - response: Promise<ServerResponse<T>> - ): Promise<ServerResponse<T>> { - this.response = response; - this.version = document.version; - this.document = document.uri.toString(); - return response; - } -} - export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensProvider { + + public static readonly cancelledCommand: vscode.Command = { + // Cancellation is not an error. Just show nothing until we can properly re-compute the code lens + title: '', + command: '' + }; + + public static readonly errorCommand: vscode.Command = { + title: localize('referenceErrorLabel', 'Could not determine references'), + command: '' + }; + private onDidChangeCodeLensesEmitter = new vscode.EventEmitter<void>(); public constructor( @@ -63,13 +48,13 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP } async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> { - const filepath = this.client.toPath(document.uri); + const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return []; } const response = await this.cachedResponse.execute(document, () => this.client.execute('navtree', { file: filepath }, token)); - if (!response || response.type !== 'response') { + if (response.type !== 'response') { return []; } diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index f84f9d31aab..887aec2fa74 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; @@ -14,11 +13,17 @@ import * as languageModeIds from '../utils/languageModeIds'; import { ResourceMap } from '../utils/resourceMap'; import * as typeConverters from '../utils/typeConverters'; -enum BufferKind { +const enum BufferKind { TypeScript = 1, JavaScript = 2, } +const enum BufferState { + Initial = 1, + Open = 2, + Closed = 2, +} + function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined { switch (mode) { case languageModeIds.typescript: return 'TS'; @@ -29,12 +34,124 @@ function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined return undefined; } +/** + * Manages synchronization of buffers with the TS server. + * + * If supported, batches together file changes. This allows the TS server to more efficiently process changes. + */ +class BufferSynchronizer { + + private _pending: Proto.UpdateOpenRequestArgs = {}; + private _pendingFiles = new Set<string>(); + + constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public open(args: Proto.OpenRequestArgs) { + if (this.supportsBatching) { + this.updatePending(args.file, pending => { + if (!pending.openFiles) { + pending.openFiles = []; + } + pending.openFiles.push(args); + }); + } else { + this.client.executeWithoutWaitingForResponse('open', args); + } + } + + public close(filepath: string) { + if (this.supportsBatching) { + this.updatePending(filepath, pending => { + if (!pending.closedFiles) { + pending.closedFiles = []; + } + pending.closedFiles.push(filepath); + }); + } else { + const args: Proto.FileRequestArgs = { file: filepath }; + this.client.executeWithoutWaitingForResponse('close', args); + } + } + + public change(filepath: string, events: vscode.TextDocumentContentChangeEvent[]) { + if (!events.length) { + return; + } + + if (this.supportsBatching) { + this.updatePending(filepath, pending => { + if (!pending.changedFiles) { + pending.changedFiles = []; + } + + pending.changedFiles.push({ + fileName: filepath, + textChanges: events.map((change): Proto.CodeEdit => ({ + newText: change.text, + start: typeConverters.Position.toLocation(change.range.start), + end: typeConverters.Position.toLocation(change.range.end), + })).reverse(), // Send the edits end-of-document to start-of-document order + }); + }); + } else { + for (const { range, text } of events) { + const args: Proto.ChangeRequestArgs = { + insertString: text, + ...typeConverters.Range.toFormattingRequestArgs(filepath, range) + }; + this.client.executeWithoutWaitingForResponse('change', args); + } + } + } + + public beforeCommand(command: string) { + if (command === 'updateOpen') { + return; + } + + this.flush(); + } + + private flush() { + if (!this.supportsBatching) { + // We've already eagerly synchronized + return; + } + + if (this._pending.changedFiles || this._pending.closedFiles || this._pending.openFiles) { + this.client.executeWithoutWaitingForResponse('updateOpen', this._pending); + this._pending = {}; + this._pendingFiles.clear(); + } + } + + private get supportsBatching(): boolean { + return this.client.apiVersion.gte(API.v340) && vscode.workspace.getConfiguration('typescript', null).get<boolean>('useBatchedBufferSync', true); + } + + private updatePending(filepath: string, f: (pending: Proto.UpdateOpenRequestArgs) => void): void { + if (this.supportsBatching && this._pendingFiles.has(filepath)) { + this.flush(); + this._pendingFiles.clear(); + f(this._pending); + this._pendingFiles.add(filepath); + } else { + f(this._pending); + } + } +} + class SyncedBuffer { + private state = BufferState.Initial; + constructor( private readonly document: vscode.TextDocument, public readonly filepath: string, - private readonly client: ITypeScriptServiceClient + private readonly client: ITypeScriptServiceClient, + private readonly synchronizer: BufferSynchronizer, ) { } public open(): void { @@ -63,7 +180,8 @@ class SyncedBuffer { } } - this.client.executeWithoutWaitingForResponse('open', args); + this.synchronizer.open(args); + this.state = BufferState.Open; } public get resource(): vscode.Uri { @@ -88,20 +206,16 @@ class SyncedBuffer { } public close(): void { - const args: Proto.FileRequestArgs = { - file: this.filepath - }; - this.client.executeWithoutWaitingForResponse('close', args); + this.synchronizer.close(this.filepath); + this.state = BufferState.Closed; } 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.executeWithoutWaitingForResponse('change', args); + if (this.state !== BufferState.Open) { + console.error(`Unexpected buffer state: ${this.state}`); } + + this.synchronizer.change(this.filepath, events); } } @@ -124,7 +238,7 @@ class PendingDiagnostics extends ResourceMap<number> { const map = new ResourceMap<void>(); for (const resource of orderedResources) { - map.set(resource, void 0); + map.set(resource, undefined); } return map; } @@ -157,8 +271,7 @@ class GetErrRequest { }; client.executeAsync('geterr', args, _token.token) - .catch(() => true) - .then(() => { + .finally(() => { if (this._done) { return; } @@ -188,6 +301,7 @@ export default class BufferSyncSupport extends Disposable { private readonly diagnosticDelayer: Delayer<any>; private pendingGetErr: GetErrRequest | undefined; private listening: boolean = false; + private readonly synchronizer: BufferSynchronizer; constructor( client: ITypeScriptServiceClient, @@ -202,6 +316,7 @@ export default class BufferSyncSupport extends Disposable { const pathNormalizer = (path: vscode.Uri) => this.client.normalizedPath(path); this.syncedBuffers = new SyncedBufferMap(pathNormalizer); this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); + this.synchronizer = new BufferSynchronizer(client); this.updateConfiguration(); vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); @@ -253,7 +368,7 @@ export default class BufferSyncSupport extends Disposable { return; } - const syncedBuffer = new SyncedBuffer(document, filepath, this.client); + const syncedBuffer = new SyncedBuffer(document, filepath, this.client, this.synchronizer); this.syncedBuffers.set(resource, syncedBuffer); syncedBuffer.open(); this.requestDiagnostic(syncedBuffer); @@ -267,10 +382,8 @@ export default class BufferSyncSupport extends Disposable { this.pendingDiagnostics.delete(resource); this.syncedBuffers.delete(resource); syncedBuffer.close(); - if (!fs.existsSync(resource.fsPath)) { - this._onDelete.fire(resource); - this.requestAllDiagnostics(); - } + this._onDelete.fire(resource); + this.requestAllDiagnostics(); } public interuptGetErr<R>(f: () => R): R { @@ -285,6 +398,10 @@ export default class BufferSyncSupport extends Disposable { return result; } + public beforeCommand(command: string): void { + this.synchronizer.beforeCommand(command); + } + private onDidCloseTextDocument(document: vscode.TextDocument): void { this.closeResource(document.uri); } @@ -355,7 +472,7 @@ export default class BufferSyncSupport extends Disposable { // Add all open TS buffers to the geterr request. They might be visible for (const buffer of this.syncedBuffers.values) { - orderedFileSet.set(buffer.resource, void 0); + orderedFileSet.set(buffer.resource, undefined); } if (orderedFileSet.size) { @@ -363,7 +480,7 @@ export default class BufferSyncSupport extends Disposable { this.pendingGetErr.cancel(); for (const file of this.pendingGetErr.files.entries) { - orderedFileSet.set(file.resource, void 0); + orderedFileSet.set(file.resource, undefined); } } diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index bc1a27d637d..89a62e79772 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import * as PConst from '../protocol.const'; -import { ITypeScriptServiceClient } from '../typescriptService'; +import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; import { nulToken } from '../utils/cancellation'; import { applyCodeAction } from '../utils/codeAction'; @@ -15,16 +15,25 @@ import { Command, CommandManager } from '../utils/commandManager'; import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; import { memoize } from '../utils/memoize'; import * as Previewer from '../utils/previewer'; +import { snippetForFunctionCall } from '../utils/snippetForFunctionCall'; +import TelemetryReporter from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import TypingsStatus from '../utils/typingsStatus'; import FileConfigurationManager from './fileConfigurationManager'; const localize = nls.loadMessageBundle(); -interface CommitCharactersSettings { +interface DotAccessorContext { + readonly range: vscode.Range; + readonly text: string; +} + +interface CompletionContext { readonly isNewIdentifierLocation: boolean; + readonly isMemberCompletion: boolean; readonly isInValidCommitCharacterContext: boolean; readonly enableCallCompletions: boolean; + readonly dotAccessorContext?: DotAccessorContext; } class MyCompletionItem extends vscode.CompletionItem { @@ -36,7 +45,7 @@ class MyCompletionItem extends vscode.CompletionItem { line: string, public readonly tsEntry: Proto.CompletionEntry, useCodeSnippetsOnMethodSuggest: boolean, - public readonly commitCharactersSettings: CommitCharactersSettings, + public readonly completionContext: CompletionContext, public readonly metadata: any | undefined, ) { super(tsEntry.name, MyCompletionItem.convertKind(tsEntry.kind)); @@ -56,23 +65,23 @@ class MyCompletionItem extends vscode.CompletionItem { this.position = position; this.useCodeSnippet = useCodeSnippetsOnMethodSuggest && (this.kind === vscode.CompletionItemKind.Function || this.kind === vscode.CompletionItemKind.Method); + if (tsEntry.replacementSpan) { this.range = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan); + // Make sure we only replace a single line at most + if (!this.range.isSingleLine) { + this.range = new vscode.Range(this.range.start.line, this.range.start.character, this.range.start.line, line.length); + } } - if (tsEntry.insertText) { - this.insertText = tsEntry.insertText; + this.insertText = tsEntry.insertText; + this.filterText = tsEntry.insertText; - if (tsEntry.replacementSpan) { - this.range = typeConverters.Range.fromTextSpan(tsEntry.replacementSpan); - if (this.insertText[0] === '[') { // o.x -> o['x'] - this.filterText = '.' + this.label; - } - - // Make sure we only replace a single line at most - if (!this.range.isSingleLine) { - this.range = new vscode.Range(this.range.start.line, this.range.start.character, this.range.start.line, line.length); - } + if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) { + this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label); + if (!this.range) { + this.range = completionContext.dotAccessorContext.range; + this.insertText = this.filterText; } } @@ -115,13 +124,21 @@ class MyCompletionItem extends vscode.CompletionItem { return; } - // Try getting longer, prefix based range for completions that span words + const wordRange = this.document.getWordRangeAtPosition(this.position); + if (wordRange) { + // TODO: Reverted next line due to https://github.com/Microsoft/vscode/issues/66187 + // this.range = wordRange; + } + + // Try getting longer, prefix based range for completions that span words const text = line.slice(Math.max(0, this.position.character - this.label.length), this.position.character).toLowerCase(); const entryName = this.label.toLowerCase(); for (let i = entryName.length; i >= 0; --i) { if (text.endsWith(entryName.substr(0, i)) && (!wordRange || wordRange.start.character > this.position.character - i)) { - this.range = new vscode.Range(this.position.line, Math.max(0, this.position.character - i), this.position.line, this.position.character); + this.range = new vscode.Range( + new vscode.Position(this.position.line, Math.max(0, this.position.character - i)), + this.position); break; } } @@ -161,6 +178,7 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.interface: return vscode.CompletionItemKind.Interface; case PConst.Kind.warning: + return vscode.CompletionItemKind.Text; case PConst.Kind.script: return vscode.CompletionItemKind.File; case PConst.Kind.directory: @@ -173,7 +191,7 @@ class MyCompletionItem extends vscode.CompletionItem { @memoize public get commitCharacters(): string[] | undefined { - if (this.commitCharactersSettings.isNewIdentifierLocation || !this.commitCharactersSettings.isInValidCommitCharacterContext) { + if (this.completionContext.isNewIdentifierLocation || !this.completionContext.isInValidCommitCharacterContext) { return undefined; } @@ -203,7 +221,7 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.keyword: case PConst.Kind.parameter: commitCharacters.push('.', ',', ';'); - if (this.commitCharactersSettings.enableCallCompletions) { + if (this.completionContext.enableCallCompletions) { commitCharacters.push('('); } break; @@ -316,6 +334,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider private readonly typingsStatus: TypingsStatus, private readonly fileConfigurationManager: FileConfigurationManager, commandManager: CommandManager, + private readonly telemetryReporter: TelemetryReporter, onCompletionAccepted: (item: vscode.CompletionItem) => void ) { commandManager.register(new ApplyCompletionCodeActionCommand(this.client)); @@ -340,7 +359,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider }); } - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return null; } @@ -352,7 +371,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider return null; } - await this.client.interuptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); + await this.client.interruptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); const args: Proto.CompletionsRequestArgs = { ...typeConverters.Position.toFileLocationRequestArgs(file, position), @@ -363,19 +382,53 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider let isNewIdentifierLocation = true; let isIncomplete = false; + let isMemberCompletion = false; + let dotAccessorContext: DotAccessorContext | undefined; let entries: ReadonlyArray<Proto.CompletionEntry>; let metadata: any | undefined; if (this.client.apiVersion.gte(API.v300)) { - const response = await this.client.interuptGetErr(() => this.client.execute('completionInfo', args, token)); + const startTime = Date.now(); + let response: ServerResponse.Response<Proto.CompletionInfoResponse> | undefined; + try { + response = await this.client.interruptGetErr(() => this.client.execute('completionInfo', args, token)); + } finally { + const duration: number = Date.now() - startTime; + + /* __GDPR__ + "completions.execute" : { + "duration" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "type" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "count" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('completions.execute', { + duration: duration + '', + type: response ? response.type : 'unknown', + count: (response && response.type === 'response' && response.body ? response.body.entries.length : 0) + '' + }); + } + if (response.type !== 'response' || !response.body) { return null; } isNewIdentifierLocation = response.body.isNewIdentifierLocation; + isMemberCompletion = response.body.isMemberCompletion; + if (isMemberCompletion) { + const dotMatch = line.text.slice(0, position.character).match(/\.\s*$/) || undefined; + if (dotMatch) { + const range = new vscode.Range(position.translate({ characterDelta: -dotMatch[0].length }), position); + const text = document.getText(range); + dotAccessorContext = { range, text }; + } + } isIncomplete = (response as any).metadata && (response as any).metadata.isIncomplete; entries = response.body.entries; metadata = response.metadata; } else { - const response = await this.client.interuptGetErr(() => this.client.execute('completions', args, token)); + const response = await this.client.interruptGetErr(() => this.client.execute('completions', args, token)); if (response.type !== 'response' || !response.body) { return null; } @@ -389,6 +442,8 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider .filter(entry => !shouldExcludeCompletionEntry(entry, completionConfiguration)) .map(entry => new MyCompletionItem(position, document, line.text, entry, completionConfiguration.useCodeSnippetsOnMethodSuggest, { isNewIdentifierLocation, + isMemberCompletion, + dotAccessorContext, isInValidCommitCharacterContext, enableCallCompletions: !completionConfiguration.useCodeSnippetsOnMethodSuggest }, metadata)); @@ -414,7 +469,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider return undefined; } - const filepath = this.client.toPath(item.document.uri); + const filepath = this.client.toOpenedFilePath(item.document); if (!filepath) { return undefined; } @@ -426,8 +481,8 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ] }; - const response = await this.client.execute('completionEntryDetails', args, token); - if (response.type !== 'response' || !response.body) { + const response = await this.client.interruptGetErr(() => this.client.execute('completionEntryDetails', args, token)); + if (response.type !== 'response' || !response.body || !response.body.length) { return item; } @@ -450,10 +505,13 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider item.additionalTextEdits = codeAction.additionalTextEdits; if (detail && item.useCodeSnippet) { - const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, token); + const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, item.document, token); if (shouldCompleteFunction) { - item.insertText = this.snippetForFunctionCall(item, detail); - commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + const { snippet, parameterCount } = snippetForFunctionCall(item, detail.displayParts); + item.insertText = snippet; + if (parameterCount > 0) { + commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + } } } @@ -597,6 +655,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider private async isValidFunctionCompletionContext( filepath: string, position: vscode.Position, + document: vscode.TextDocument, token: vscode.CancellationToken ): Promise<boolean> { // Workaround for https://github.com/Microsoft/TypeScript/issues/12677 @@ -604,80 +663,23 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider try { const args: Proto.FileLocationRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); const response = await this.client.execute('quickinfo', args, token); - if (response.type !== 'response') { - return true; - } - - const { body } = response; - switch (body && body.kind) { - case 'var': - case 'let': - case 'const': - case 'alias': - return false; - default: - return true; - } - } catch (e) { - return true; - } - } - - private snippetForFunctionCall( - item: vscode.CompletionItem, - detail: Proto.CompletionEntryDetails - ): vscode.SnippetString { - let hasOptionalParameters = false; - let hasAddedParameters = false; - - const snippet = new vscode.SnippetString(); - const methodName = detail.displayParts.find(part => part.kind === 'methodName'); - if (item.insertText) { - if (typeof item.insertText === 'string') { - snippet.appendText(item.insertText); - } else { - return item.insertText; - } - } else { - snippet.appendText((methodName && methodName.text) || item.label); - } - snippet.appendText('('); - - let parenCount = 0; - let i = 0; - for (; i < detail.displayParts.length; ++i) { - const part = detail.displayParts[i]; - // Only take top level paren names - if (part.kind === 'parameterName' && parenCount === 1) { - const next = detail.displayParts[i + 1]; - // Skip optional parameters - const nameIsFollowedByOptionalIndicator = next && next.text === '?'; - if (!nameIsFollowedByOptionalIndicator) { - if (hasAddedParameters) { - snippet.appendText(', '); - } - hasAddedParameters = true; - snippet.appendPlaceholder(part.text); - } - hasOptionalParameters = hasOptionalParameters || nameIsFollowedByOptionalIndicator; - } else if (part.kind === 'punctuation') { - if (part.text === '(') { - ++parenCount; - } else if (part.text === ')') { - --parenCount; - } else if (part.text === '...' && parenCount === 1) { - // Found rest parmeter. Do not fill in any further arguments - hasOptionalParameters = true; - break; + if (response.type === 'response' && response.body) { + switch (response.body.kind) { + case 'var': + case 'let': + case 'const': + case 'alias': + return false; } } + } catch { + // Noop } - if (hasOptionalParameters) { - snippet.appendTabstop(); - } - snippet.appendText(')'); - snippet.appendTabstop(0); - return snippet; + + // Don't complete function call if there is already something that looks like a function call + // https://github.com/Microsoft/vscode/issues/18131 + const after = document.lineAt(position.line).text.slice(position.character); + return after.match(/^[a-z_$0-9]*\s*\(/gi) === null; } } @@ -700,10 +702,11 @@ export function register( typingsStatus: TypingsStatus, fileConfigurationManager: FileConfigurationManager, commandManager: CommandManager, + telemetryReporter: TelemetryReporter, onCompletionAccepted: (item: vscode.CompletionItem) => void ) { return new ConfigurationDependentRegistration(modeId, 'suggest.enabled', () => vscode.languages.registerCompletionItemProvider(selector, - new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, onCompletionAccepted), + new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, telemetryReporter, onCompletionAccepted), ...TypeScriptCompletionItemProvider.triggerCharacters)); } diff --git a/extensions/typescript-language-features/src/features/definitionProviderBase.ts b/extensions/typescript-language-features/src/features/definitionProviderBase.ts index 253485edf16..2ce74daa6fb 100644 --- a/extensions/typescript-language-features/src/features/definitionProviderBase.ts +++ b/extensions/typescript-language-features/src/features/definitionProviderBase.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import * as typeConverters from '../utils/typeConverters'; @@ -20,20 +19,18 @@ export default class TypeScriptDefinitionProviderBase { position: vscode.Position, token: vscode.CancellationToken ): Promise<vscode.Location[] | undefined> { - const filepath = this.client.toPath(document.uri); - if (!filepath) { + const file = this.client.toOpenedFilePath(document); + if (!file) { return undefined; } - const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - + const args = typeConverters.Position.toFileLocationRequestArgs(file, position); const response = await this.client.execute(definitionType, args, token); - if (response.type !== 'response') { + if (response.type !== 'response' || !response.body) { return undefined; } - const locations: Proto.FileSpan[] = (response && response.body) || []; - return locations.map(location => + return response.body.map(location => typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location)); } } \ 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 7f085ef9383..5fe72c2005f 100644 --- a/extensions/typescript-language-features/src/features/definitions.ts +++ b/extensions/typescript-language-features/src/features/definitions.ts @@ -22,7 +22,7 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase token: vscode.CancellationToken ): Promise<vscode.DefinitionLink[] | vscode.Definition | undefined> { if (this.client.apiVersion.gte(API.v270)) { - const filepath = this.client.toPath(document.uri); + const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return undefined; } @@ -35,13 +35,13 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase const span = response.body.textSpan ? typeConverters.Range.fromTextSpan(response.body.textSpan) : undefined; return response.body.definitions - .map(location => { + .map((location): vscode.DefinitionLink => { const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); return { originSelectionRange: span, targetRange: target.range, targetUri: target.uri, - } as vscode.DefinitionLink; + }; }); } diff --git a/extensions/typescript-language-features/src/features/diagnostics.ts b/extensions/typescript-language-features/src/features/diagnostics.ts index 4dc7a52725c..a9de15ab539 100644 --- a/extensions/typescript-language-features/src/features/diagnostics.ts +++ b/extensions/typescript-language-features/src/features/diagnostics.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { ResourceMap } from '../utils/resourceMap'; -import { DiagnosticLanguage, allDiagnosticLangauges } from '../utils/languageDescription'; +import { DiagnosticLanguage, allDiagnosticLanguages } from '../utils/languageDescription'; export const enum DiagnosticKind { Syntax, @@ -71,21 +71,21 @@ class FileDiagnostics { } } -interface LangaugeDiagnosticSettings { +interface LanguageDiagnosticSettings { readonly validate: boolean; readonly enableSuggestions: boolean; } class DiagnosticSettings { - private static readonly defaultSettings: LangaugeDiagnosticSettings = { + private static readonly defaultSettings: LanguageDiagnosticSettings = { validate: true, enableSuggestions: true }; - private readonly _languageSettings = new Map<DiagnosticLanguage, LangaugeDiagnosticSettings>(); + private readonly _languageSettings = new Map<DiagnosticLanguage, LanguageDiagnosticSettings>(); constructor() { - for (const language of allDiagnosticLangauges) { + for (const language of allDiagnosticLanguages) { this._languageSettings.set(language, DiagnosticSettings.defaultSettings); } } @@ -112,11 +112,11 @@ class DiagnosticSettings { })); } - private get(language: DiagnosticLanguage): LangaugeDiagnosticSettings { + private get(language: DiagnosticLanguage): LanguageDiagnosticSettings { return this._languageSettings.get(language) || DiagnosticSettings.defaultSettings; } - private update(language: DiagnosticLanguage, f: (x: LangaugeDiagnosticSettings) => LangaugeDiagnosticSettings): boolean { + private update(language: DiagnosticLanguage, f: (x: LanguageDiagnosticSettings) => LanguageDiagnosticSettings): boolean { const currentSettings = this.get(language); const newSettings = f(currentSettings); this._languageSettings.set(language, newSettings); @@ -129,7 +129,7 @@ export class DiagnosticsManager { private readonly _diagnostics = new ResourceMap<FileDiagnostics>(); private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; - private _pendingUpdates = new ResourceMap<any>(); + private readonly _pendingUpdates = new ResourceMap<any>(); private readonly _updateDelay = 50; @@ -145,7 +145,7 @@ export class DiagnosticsManager { for (const value of this._pendingUpdates.values) { clearTimeout(value); } - this._pendingUpdates = new ResourceMap<any>(); + this._pendingUpdates.clear(); } public reInitialize(): void { @@ -173,7 +173,6 @@ export class DiagnosticsManager { kind: DiagnosticKind, diagnostics: vscode.Diagnostic[] ): void { - let didUpdate = false; const entry = this._diagnostics.get(file); if (entry) { @@ -224,7 +223,7 @@ export class DiagnosticsManager { private rebuild(): void { this._currentDiagnostics.clear(); - for (const fileDiagnostic of Array.from(this._diagnostics.values)) { + for (const fileDiagnostic of this._diagnostics.values) { this._currentDiagnostics.set(fileDiagnostic.file, fileDiagnostic.getDiagnostics(this._settings)); } } diff --git a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts b/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts index 8ec98eefb8f..0b1ddeca929 100644 --- a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts +++ b/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts @@ -36,6 +36,8 @@ const directives: Directive[] = [ ]; class DirectiveCommentCompletionProvider implements vscode.CompletionItemProvider { + public static readonly minVersion = API.v230; + constructor( private readonly client: ITypeScriptServiceClient, ) { } @@ -45,7 +47,7 @@ class DirectiveCommentCompletionProvider implements vscode.CompletionItemProvide position: vscode.Position, _token: vscode.CancellationToken ): vscode.CompletionItem[] { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return []; } @@ -69,7 +71,7 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, API.v230, () => { + return new VersionDependentRegistration(client, DirectiveCommentCompletionProvider.minVersion, () => { return vscode.languages.registerCompletionItemProvider(selector, new DirectiveCommentCompletionProvider(client), '@'); diff --git a/extensions/typescript-language-features/src/features/documentHighlight.ts b/extensions/typescript-language-features/src/features/documentHighlight.ts index b81ead9e0de..970a265f218 100644 --- a/extensions/typescript-language-features/src/features/documentHighlight.ts +++ b/extensions/typescript-language-features/src/features/documentHighlight.ts @@ -6,6 +6,7 @@ import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; +import { flatten } from '../utils/arrays'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightProvider { @@ -14,31 +15,36 @@ class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightPro ) { } public async provideDocumentHighlights( - resource: vscode.TextDocument, + document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken ): Promise<vscode.DocumentHighlight[]> { - const file = this.client.toPath(resource.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return []; } - const args = typeConverters.Position.toFileLocationRequestArgs(file, position); - const response = await this.client.execute('references', args, token); + const args = { + ...typeConverters.Position.toFileLocationRequestArgs(file, position), + filesToSearch: [file] + }; + const response = await this.client.execute('documentHighlights', args, token); if (response.type !== 'response' || !response.body) { return []; } - return response.body.refs - .filter(ref => ref.file === file) - .map(documentHighlightFromReference); + return flatten( + response.body + .filter(highlight => highlight.file === file) + .map(convertDocumentHighlight)); } } -function documentHighlightFromReference(reference: Proto.ReferencesResponseItem): vscode.DocumentHighlight { - return new vscode.DocumentHighlight( - typeConverters.Range.fromTextSpan(reference), - reference.isWriteAccess ? vscode.DocumentHighlightKind.Write : vscode.DocumentHighlightKind.Read); +function convertDocumentHighlight(highlight: Proto.DocumentHighlightsItem): ReadonlyArray<vscode.DocumentHighlight> { + return highlight.highlightSpans.map(span => + new vscode.DocumentHighlight( + typeConverters.Range.fromTextSpan(span), + span.kind === 'writtenReference' ? vscode.DocumentHighlightKind.Write : vscode.DocumentHighlightKind.Read)); } export function register( diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts index cc66470eed0..68635b346a6 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/features/documentSymbol.ts @@ -8,6 +8,7 @@ import * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; import * as typeConverters from '../utils/typeConverters'; +import { CachedResponse } from '../tsServer/cachedResponse'; const getSymbolKind = (kind: string): vscode.SymbolKind => { switch (kind) { @@ -30,17 +31,18 @@ const getSymbolKind = (kind: string): vscode.SymbolKind => { class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider { public constructor( - private readonly client: ITypeScriptServiceClient + private readonly client: ITypeScriptServiceClient, + private cachedResponse: CachedResponse<Proto.NavTreeResponse>, ) { } - public async provideDocumentSymbols(resource: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.DocumentSymbol[] | undefined> { - const file = this.client.toPath(resource.uri); + public async provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.DocumentSymbol[] | undefined> { + const file = this.client.toOpenedFilePath(document); if (!file) { return undefined; } const args: Proto.FileRequestArgs = { file }; - const response = await this.client.execute('navtree', args, token); + const response = await this.cachedResponse.execute(document, () => this.client.execute('navtree', args, token)); if (response.type !== 'response' || !response.body) { return undefined; } @@ -49,7 +51,7 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider if (tree && tree.childItems) { // The root represents the file. Ignore this when showing in the UI const result: vscode.DocumentSymbol[] = []; - tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(resource.uri, result, item)); + tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(document.uri, result, item)); return result; } @@ -97,7 +99,8 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, + cachedResponse: CachedResponse<Proto.NavTreeResponse>, ) { return vscode.languages.registerDocumentSymbolProvider(selector, - new TypeScriptDocumentSymbolProvider(client), { label: 'TypeScript' }); + new TypeScriptDocumentSymbolProvider(client, cachedResponse), { label: 'TypeScript' }); } diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts index 64c0324cf22..6d35cd1b92b 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts @@ -9,12 +9,12 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { isTypeScriptDocument } from '../utils/languageModeIds'; import { ResourceMap } from '../utils/resourceMap'; +import { Disposable } from '../utils/dispose'; function objsAreEqual<T>(a: T, b: T): boolean { let keys = Object.keys(a); - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; + for (const key of keys) { if ((a as any)[key] !== (b as any)[key]) { return false; } @@ -34,27 +34,20 @@ function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration): ); } -export default class FileConfigurationManager { - private onDidCloseTextDocumentSub: vscode.Disposable | undefined; +export default class FileConfigurationManager extends Disposable { private readonly formatOptions = new ResourceMap<Promise<FileConfiguration | undefined>>(); public constructor( private readonly client: ITypeScriptServiceClient ) { - this.onDidCloseTextDocumentSub = vscode.workspace.onDidCloseTextDocument(textDocument => { + super(); + 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. this.formatOptions.delete(textDocument.uri); - }); - } - - public dispose() { - if (this.onDidCloseTextDocumentSub) { - this.onDidCloseTextDocumentSub.dispose(); - this.onDidCloseTextDocumentSub = undefined; - } + }, undefined, this._disposables); } public async ensureConfigurationForDocument( @@ -84,7 +77,7 @@ export default class FileConfigurationManager { options: vscode.FormattingOptions, token: vscode.CancellationToken ): Promise<void> { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return; } @@ -180,23 +173,25 @@ export default class FileConfigurationManager { return {}; } - const preferences = vscode.workspace.getConfiguration( + const config = vscode.workspace.getConfiguration( isTypeScriptDocument(document) ? 'typescript.preferences' : 'javascript.preferences', document.uri); return { - quotePreference: getQuoteStylePreference(preferences), - importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferences), - allowTextChangesInNewFiles: document.uri.scheme === 'file' + quotePreference: this.getQuoteStylePreference(config), + importModuleSpecifierPreference: getImportModuleSpecifierPreference(config), + allowTextChangesInNewFiles: document.uri.scheme === 'file', + providePrefixAndSuffixTextForRename: config.get<boolean>('renameShorthandProperties', true), + allowRenameOfImportPath: true, }; } -} -function getQuoteStylePreference(config: vscode.WorkspaceConfiguration) { - switch (config.get<string>('quoteStyle')) { - case 'single': return 'single'; - case 'double': return 'double'; - default: return undefined; + private getQuoteStylePreference(config: vscode.WorkspaceConfiguration) { + switch (config.get<string>('quoteStyle')) { + case 'single': return 'single'; + case 'double': return 'double'; + default: return this.client.apiVersion.gte(API.v333) ? 'auto' : undefined; + } } } diff --git a/extensions/typescript-language-features/src/features/fixAll.ts b/extensions/typescript-language-features/src/features/fixAll.ts new file mode 100644 index 00000000000..8c6f09659c3 --- /dev/null +++ b/extensions/typescript-language-features/src/features/fixAll.ts @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import * as Proto from '../protocol'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; +import * as typeConverters from '../utils/typeConverters'; +import { DiagnosticsManager } from './diagnostics'; +import FileConfigurationManager from './fileConfigurationManager'; + +const localize = nls.loadMessageBundle(); + +const autoFixableDiagnosticCodes = new Set<number>([ + 2420, // Incorrectly implemented interface + 2552, // Cannot find name +]); + +class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { + + public static readonly metadata: vscode.CodeActionProviderMetadata = { + providedCodeActionKinds: [vscode.CodeActionKind.SourceFixAll] + }; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly fileConfigurationManager: FileConfigurationManager, + private readonly diagnosticsManager: DiagnosticsManager, + ) { } + + public async provideCodeActions( + document: vscode.TextDocument, + _range: vscode.Range, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): Promise<vscode.CodeAction[] | undefined> { + if (!context.only || !vscode.CodeActionKind.SourceFixAll.intersects(context.only)) { + return undefined; + } + + const file = this.client.toOpenedFilePath(document); + if (!file) { + return undefined; + } + + const autoFixableDiagnostics = this.getAutoFixableDiagnostics(document); + if (!autoFixableDiagnostics.length) { + return undefined; + } + + const fixAllAction = await this.getFixAllCodeAction(document, file, autoFixableDiagnostics, token); + return fixAllAction ? [fixAllAction] : undefined; + } + + private getAutoFixableDiagnostics( + document: vscode.TextDocument + ): vscode.Diagnostic[] { + if (this.client.bufferSyncSupport.hasPendingDiagnostics(document.uri)) { + return []; + } + + return this.diagnosticsManager.getDiagnostics(document.uri) + .filter(x => autoFixableDiagnosticCodes.has(x.code as number)); + } + + private async getFixAllCodeAction( + document: vscode.TextDocument, + file: string, + diagnostics: ReadonlyArray<vscode.Diagnostic>, + token: vscode.CancellationToken, + ): Promise<vscode.CodeAction | undefined> { + await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); + + const autoFixResponse = await this.getAutoFixEdit(file, diagnostics, token); + if (!autoFixResponse) { + return undefined; + } + const { edit, fixedDiagnostics } = autoFixResponse; + const codeAction = new vscode.CodeAction( + localize('autoFix.label', 'Auto fix'), + vscode.CodeActionKind.SourceFixAll); + codeAction.edit = edit; + codeAction.diagnostics = fixedDiagnostics; + + return codeAction; + } + + private async getAutoFixEdit( + file: string, + diagnostics: ReadonlyArray<vscode.Diagnostic>, + token: vscode.CancellationToken, + ): Promise<{ edit: vscode.WorkspaceEdit, fixedDiagnostics: vscode.Diagnostic[] } | undefined> { + const edit = new vscode.WorkspaceEdit(); + const fixedDiagnostics: vscode.Diagnostic[] = []; + for (const diagnostic of diagnostics) { + const args: Proto.CodeFixRequestArgs = { + ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), + errorCodes: [+(diagnostic.code!)] + }; + const response = await this.client.execute('getCodeFixes', args, token); + if (response.type !== 'response' || !response.body || response.body.length > 1) { + return undefined; + } + + const fix = response.body[0]; + if (new Set<string>(['fixClassIncorrectlyImplementsInterface', 'spelling']).has(fix.fixName)) { + typeConverters.WorkspaceEdit.withFileCodeEdits(edit, this.client, fix.changes); + fixedDiagnostics.push(diagnostic); + } + } + + if (!fixedDiagnostics.length) { + return undefined; + } + + return { edit, fixedDiagnostics }; + } +} + +export function register( + selector: vscode.DocumentSelector, + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, + diagnosticsManager: DiagnosticsManager) { + return new VersionDependentRegistration(client, API.v300, () => + new ConfigurationDependentRegistration('typescript', 'experimental.autoFix.enabled', () => + vscode.languages.registerCodeActionsProvider(selector, + new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager), + TypeScriptAutoFixProvider.metadata))); +} diff --git a/extensions/typescript-language-features/src/features/folding.ts b/extensions/typescript-language-features/src/features/folding.ts index 584072c596c..a00a2db5f00 100644 --- a/extensions/typescript-language-features/src/features/folding.ts +++ b/extensions/typescript-language-features/src/features/folding.ts @@ -11,6 +11,8 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { + public static readonly minVersion = API.v280; + public constructor( private readonly client: ITypeScriptServiceClient ) { } @@ -20,7 +22,7 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { _context: vscode.FoldingContext, token: vscode.CancellationToken ): Promise<vscode.FoldingRange[] | undefined> { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return; } @@ -75,7 +77,7 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new VersionDependentRegistration(client, API.v280, () => { + return new VersionDependentRegistration(client, TypeScriptFoldingProvider.minVersion, () => { return vscode.languages.registerFoldingRangeProvider(selector, new TypeScriptFoldingProvider(client)); }); diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts index 7d0094d08bb..d64effd1a32 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -22,7 +22,7 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit options: vscode.FormattingOptions, token: vscode.CancellationToken ): Promise<vscode.TextEdit[] | undefined> { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return undefined; } @@ -45,7 +45,7 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit options: vscode.FormattingOptions, token: vscode.CancellationToken ): Promise<vscode.TextEdit[]> { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return []; } diff --git a/extensions/typescript-language-features/src/features/hover.ts b/extensions/typescript-language-features/src/features/hover.ts index c8e4b60d106..5077f9648a0 100644 --- a/extensions/typescript-language-features/src/features/hover.ts +++ b/extensions/typescript-language-features/src/features/hover.ts @@ -21,13 +21,13 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { position: vscode.Position, token: vscode.CancellationToken ): Promise<vscode.Hover | undefined> { - const filepath = this.client.toPath(document.uri); + const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return undefined; } const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - const response = await this.client.interuptGetErr(() => this.client.execute('quickinfo', args, token)); + const response = await this.client.interruptGetErr(() => this.client.execute('quickinfo', args, token)); if (response.type !== 'response' || !response.body) { return undefined; } diff --git a/extensions/typescript-language-features/src/features/implementations.ts b/extensions/typescript-language-features/src/features/implementations.ts index d750587ea8d..7b7dc130175 100644 --- a/extensions/typescript-language-features/src/features/implementations.ts +++ b/extensions/typescript-language-features/src/features/implementations.ts @@ -10,6 +10,8 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import DefinitionProviderBase from './definitionProviderBase'; class TypeScriptImplementationProvider extends DefinitionProviderBase implements vscode.ImplementationProvider { + public static readonly minVersion = API.v220; + public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Definition | undefined> { return this.getSymbolLocations('implementation', document, position, token); } @@ -19,7 +21,7 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, API.v220, () => { + return new VersionDependentRegistration(client, TypeScriptImplementationProvider.minVersion, () => { return vscode.languages.registerImplementationProvider(selector, new TypeScriptImplementationProvider(client)); }); diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts index 1d352dc4f25..63cfc6222d9 100644 --- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts @@ -10,30 +10,46 @@ import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import { CachedResponse, ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'; +import { TypeScriptBaseCodeLensProvider, ReferencesCodeLens, getSymbolRange } from './baseCodeLensProvider'; +import { CachedResponse } from '../tsServer/cachedResponse'; +import * as typeConverters from '../utils/typeConverters'; + const localize = nls.loadMessageBundle(); export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider { + public static readonly minVersion = API.v220; public async resolveCodeLens( inputCodeLens: vscode.CodeLens, - _token: vscode.CancellationToken, + token: vscode.CancellationToken, ): Promise<vscode.CodeLens> { const codeLens = inputCodeLens as ReferencesCodeLens; - try { - const locations: vscode.Location[] | undefined = await vscode.commands.executeCommand<vscode.Location[]>('vscode.executeImplementationProvider', codeLens.document, codeLens.range.start); - if (locations) { - codeLens.command = this.getCommand(locations, codeLens); - return codeLens; - } - } catch { - // noop + + const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); + const response = await this.client.execute('implementation', args, token, /* lowPriority */ true); + if (response.type !== 'response' || !response.body) { + codeLens.command = response.type === 'cancelled' + ? TypeScriptBaseCodeLensProvider.cancelledCommand + : TypeScriptBaseCodeLensProvider.errorCommand; + return codeLens; } - codeLens.command = { - title: localize('implementationsErrorLabel', 'Could not determine implementations'), - command: '' - }; + const locations = response.body + .map(reference => + // Only take first line on implementation: https://github.com/Microsoft/vscode/issues/23924 + new vscode.Location(this.client.toResource(reference.file), + reference.start.line === reference.end.line + ? typeConverters.Range.fromTextSpan(reference) + : new vscode.Range( + typeConverters.Position.fromLocation(reference.start), + new vscode.Position(reference.start.line, 0)))) + // Exclude original from implementations + .filter(location => + !(location.uri.toString() === codeLens.document.toString() && + location.range.start.line === codeLens.range.start.line && + location.range.start.character === codeLens.range.start.character)); + + codeLens.command = this.getCommand(locations, codeLens); return codeLens; } @@ -80,7 +96,7 @@ export function register( client: ITypeScriptServiceClient, cachedResponse: CachedResponse<Proto.NavTreeResponse>, ) { - return new VersionDependentRegistration(client, API.v220, () => + return new VersionDependentRegistration(client, TypeScriptImplementationsCodeLensProvider.minVersion, () => new ConfigurationDependentRegistration(modeId, 'implementationsCodeLens.enabled', () => { return vscode.languages.registerCodeLensProvider(selector, new TypeScriptImplementationsCodeLensProvider(client, cachedResponse)); diff --git a/extensions/typescript-language-features/src/features/jsDocCompletions.ts b/extensions/typescript-language-features/src/features/jsDocCompletions.ts index 2125ee0e5ac..36f7a751522 100644 --- a/extensions/typescript-language-features/src/features/jsDocCompletions.ts +++ b/extensions/typescript-language-features/src/features/jsDocCompletions.ts @@ -44,7 +44,7 @@ class JsDocCompletionProvider implements vscode.CompletionItemProvider { position: vscode.Position, token: vscode.CancellationToken ): Promise<vscode.CompletionItem[] | undefined> { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return undefined; } @@ -112,9 +112,10 @@ export function templateToSnippet(template: string): vscode.SnippetString { export function register( selector: vscode.DocumentSelector, + modeId: string, client: ITypeScriptServiceClient, ): vscode.Disposable { - return new ConfigurationDependentRegistration('jsDocCompletion', 'enabled', () => { + return new ConfigurationDependentRegistration(modeId, 'suggest.completeJSDocs', () => { return vscode.languages.registerCompletionItemProvider(selector, new JsDocCompletionProvider(client), '*'); diff --git a/extensions/typescript-language-features/src/features/languageConfiguration.ts b/extensions/typescript-language-features/src/features/languageConfiguration.ts index 33691a615cc..59fefa1007b 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/features/languageConfiguration.ts @@ -42,6 +42,11 @@ const jsTsLanguageConfiguration: vscode.LanguageConfiguration = { // e.g. *-----*/| beforeText: /^(\t|[ ])*[ ]\*[^/]*\*\/\s*$/, action: { indentAction: vscode.IndentAction.None, removeText: 1 }, + }, + { + beforeText: /^\s*(\bcase\s.+:|\bdefault:)$/, + afterText: /^(?!\s*(\bcase\b|\bdefault\b))/, + action: { indentAction: vscode.IndentAction.Indent }, } ] }; @@ -52,14 +57,26 @@ const jsxTagsLanguageConfiguration: vscode.LanguageConfiguration = { wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, onEnterRules: [ { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), + beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w\\-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>$/i, action: { indentAction: vscode.IndentAction.IndentOutdent } }, { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), + beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w\\-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), action: { indentAction: vscode.IndentAction.Indent } - } + }, + { + // `beforeText` only applies to tokens of a given language. Since we are dealing with jsx-tags, + // make sure we apply to the closing `>` of a tag so that mixed language spans + // such as `<div onclick={1}>` are handled properly. + beforeText: /^>$/, + afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>$/i, + action: { indentAction: vscode.IndentAction.IndentOutdent } + }, + { + beforeText: /^>$/, + action: { indentAction: vscode.IndentAction.Indent } + }, ], }; diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/features/organizeImports.ts index d26fabbc41f..bb7880800a6 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/features/organizeImports.ts @@ -46,7 +46,7 @@ class OrganizeImportsCommand implements Command { } } }; - const response = await this.client.execute('organizeImports', args, nulToken); + const response = await this.client.interruptGetErr(() => this.client.execute('organizeImports', args, nulToken)); if (response.type !== 'response' || !response.body) { return false; } @@ -57,6 +57,8 @@ class OrganizeImportsCommand implements Command { } export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvider { + public static readonly minVersion = API.v280; + public constructor( private readonly client: ITypeScriptServiceClient, commandManager: CommandManager, @@ -77,7 +79,7 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi context: vscode.CodeActionContext, token: vscode.CancellationToken ): vscode.CodeAction[] { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return []; } @@ -89,7 +91,7 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi this.fileConfigManager.ensureConfigurationForDocument(document, token); const action = new vscode.CodeAction( - localize('oraganizeImportsAction.title', "Organize Imports"), + localize('organizeImportsAction.title', "Organize Imports"), vscode.CodeActionKind.SourceOrganizeImports); action.command = { title: '', command: OrganizeImportsCommand.Id, arguments: [file] }; return [action]; @@ -103,7 +105,7 @@ export function register( fileConfigurationManager: FileConfigurationManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, API.v280, () => { + return new VersionDependentRegistration(client, OrganizeImportsCodeActionProvider.minVersion, () => { const organizeImportsProvider = new OrganizeImportsCodeActionProvider(client, commandManager, fileConfigurationManager, telemetryReporter); return vscode.languages.registerCodeActionsProvider(selector, organizeImportsProvider, diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 23f428092fb..8972df52185 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -8,14 +8,15 @@ import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; +import { nulToken } from '../utils/cancellation'; import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction'; import { Command, CommandManager } from '../utils/commandManager'; import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { memoize } from '../utils/memoize'; 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(); @@ -127,8 +128,8 @@ class DiagnosticsSet { } class CodeActionSet { - private _actions = new Set<vscode.CodeAction>(); - private _fixAllActions = new Map<{}, vscode.CodeAction>(); + private readonly _actions = new Set<vscode.CodeAction>(); + private readonly _fixAllActions = new Map<{}, vscode.CodeAction>(); public get values(): Iterable<vscode.CodeAction> { return this._actions; @@ -141,7 +142,7 @@ class CodeActionSet { public addFixAllAction(fixId: {}, action: vscode.CodeAction) { const existing = this._fixAllActions.get(fixId); if (existing) { - // reinsert action at back + // reinsert action at back of actions list this._actions.delete(existing); } this.addAction(action); @@ -154,29 +155,26 @@ class CodeActionSet { } class SupportedCodeActionProvider { - private _supportedCodeActions?: Thenable<Set<number>>; - public constructor( private readonly client: ITypeScriptServiceClient ) { } public async getFixableDiagnosticsForContext(context: vscode.CodeActionContext): Promise<DiagnosticsSet> { - const supportedActions = await this.supportedCodeActions; - return DiagnosticsSet.from(context.diagnostics.filter(diagnostic => supportedActions.has(+(diagnostic.code!)))); + const fixableCodes = await this.fixableDiagnosticCodes; + return DiagnosticsSet.from( + context.diagnostics.filter(diagnostic => typeof diagnostic.code !== 'undefined' && fixableCodes.has(diagnostic.code + ''))); } - private get supportedCodeActions(): Thenable<Set<number>> { - if (!this._supportedCodeActions) { - this._supportedCodeActions = this.client.execute('getSupportedCodeFixes', null, nulToken) - .then(response => response.type === 'response' ? response.body || [] : []) - .then(codes => codes.map(code => +code).filter(code => !isNaN(code))) - .then(codes => new Set(codes)); - } - return this._supportedCodeActions; + @memoize + private get fixableDiagnosticCodes(): Thenable<Set<string>> { + return this.client.execute('getSupportedCodeFixes', null, nulToken) + .then(response => response.type === 'response' ? response.body || [] : []) + .then(codes => new Set(codes)); } } class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { + public static readonly minVersion = API.v213; public static readonly metadata: vscode.CodeActionProviderMetadata = { providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] @@ -203,7 +201,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { context: vscode.CodeActionContext, token: vscode.CancellationToken ): Promise<vscode.CodeAction[]> { - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return []; } @@ -219,33 +217,33 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { await this.formattingConfigurationManager.ensureConfigurationForDocument(document, token); - const results: vscode.CodeAction[] = []; + const results = new CodeActionSet(); for (const diagnostic of fixableDiagnostics.values) { - results.push(...await this.getFixesForDiagnostic(document, file, diagnostic, token)); + await this.getFixesForDiagnostic(document, file, diagnostic, results, token); } - return results; + return Array.from(results.values); } private async getFixesForDiagnostic( document: vscode.TextDocument, file: string, diagnostic: vscode.Diagnostic, - token: vscode.CancellationToken - ): Promise<Iterable<vscode.CodeAction>> { + results: CodeActionSet, + token: vscode.CancellationToken, + ): Promise<CodeActionSet> { const args: Proto.CodeFixRequestArgs = { ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), errorCodes: [+(diagnostic.code!)] }; const response = await this.client.execute('getCodeFixes', args, token); if (response.type !== 'response' || !response.body) { - return []; + return results; } - const results = new CodeActionSet(); for (const tsCodeFix of response.body) { - this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix); + this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix as Proto.CodeFixAction); } - return results.values; + return results; } private addAllFixesForTsCodeAction( @@ -253,7 +251,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { document: vscode.TextDocument, file: string, diagnostic: vscode.Diagnostic, - tsAction: Proto.CodeAction + tsAction: Proto.CodeFixAction ): CodeActionSet { results.addAction(this.getSingleFixForTsCodeAction(diagnostic, tsAction)); this.addFixAllForTsCodeAction(results, document, file, diagnostic, tsAction as Proto.CodeFixAction); @@ -262,7 +260,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { private getSingleFixForTsCodeAction( diagnostic: vscode.Diagnostic, - tsAction: Proto.CodeAction + tsAction: Proto.CodeFixAction ): vscode.CodeAction { const codeAction = new vscode.CodeAction(tsAction.description, vscode.CodeActionKind.QuickFix); codeAction.edit = getEditForCodeAction(this.client, tsAction); @@ -272,6 +270,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { arguments: [tsAction], title: '' }; + codeAction.isPreferred = isPreferredFix(tsAction); return codeAction; } @@ -282,7 +281,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction, ): CodeActionSet { - if (!tsAction.fixId || this.client.apiVersion.lt(API.v270) || results.hasFixAllAction(results)) { + if (!tsAction.fixId || this.client.apiVersion.lt(API.v270) || results.hasFixAllAction(tsAction.fixId)) { return results; } @@ -305,6 +304,21 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { } } +const preferredFixes = new Set([ + 'annotateWithTypeFromJSDoc', + 'constructorForDerivedNeedSuperCall', + 'extendsInterfaceBecomesImplements', + 'fixAwaitInSyncFunction', + 'fixClassIncorrectlyImplementsInterface', + 'fixUnreachableCode', + 'forgottenThisPropertyAccess', + 'spelling', + 'unusedIdentifier', +]); +function isPreferredFix(tsAction: Proto.CodeFixAction): boolean { + return preferredFixes.has(tsAction.fixName); +} + export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, @@ -313,7 +327,7 @@ export function register( diagnosticsManager: DiagnosticsManager, telemetryReporter: TelemetryReporter ) { - return new VersionDependentRegistration(client, API.v213, () => + return new VersionDependentRegistration(client, TypeScriptQuickFixProvider.minVersion, () => vscode.languages.registerCodeActionsProvider(selector, new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter), TypeScriptQuickFixProvider.metadata)); diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index c0ad9cb63da..a3a7bb629e7 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -4,15 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; +import { nulToken } from '../utils/cancellation'; import { Command, CommandManager } from '../utils/commandManager'; 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'; + +const localize = nls.loadMessageBundle(); class ApplyRefactoringCommand implements Command { @@ -49,7 +52,12 @@ class ApplyRefactoringCommand implements Command { action, }; const response = await this.client.execute('getEditsForRefactor', args, nulToken); - if (response.type !== 'response' || !response.body || !response.body.edits.length) { + if (response.type !== 'response' || !response.body) { + return false; + } + + if (!response.body.edits.length) { + vscode.window.showErrorMessage(localize('refactoringFailed', "Could not apply refactoring")); return false; } @@ -104,6 +112,8 @@ class SelectRefactorCommand implements Command { } class TypeScriptRefactorProvider implements vscode.CodeActionProvider { + public static readonly minVersion = API.v240; + private static readonly extractFunctionKind = vscode.CodeActionKind.RefactorExtract.append('function'); private static readonly extractConstantKind = vscode.CodeActionKind.RefactorExtract.append('constant'); private static readonly moveKind = vscode.CodeActionKind.Refactor.append('move'); @@ -132,15 +142,17 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { return undefined; } - const file = this.client.toPath(document.uri); + const file = this.client.toOpenedFilePath(document); if (!file) { return undefined; } - await this.formattingOptionsManager.ensureConfigurationForDocument(document, token); - const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection); - const response = await this.client.execute('getApplicableRefactors', args, token); + const response = await this.client.interruptGetErr(() => { + this.formattingOptionsManager.ensureConfigurationForDocument(document, token); + + return this.client.execute('getApplicableRefactors', args, token); + }); if (response.type !== 'response' || !response.body) { return undefined; } @@ -186,6 +198,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { command: ApplyRefactoringCommand.ID, arguments: [document, file, info.name, action.name, rangeOrSelection], }; + codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action); return codeAction; } @@ -207,6 +220,15 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } return vscode.CodeActionKind.Refactor; } + + private static isPreferred( + action: Proto.RefactorActionInfo + ): boolean { + if (action.name.startsWith('constant_')) { + return action.name.endsWith('scope_0'); + } + return false; + } } export function register( @@ -216,7 +238,7 @@ export function register( commandManager: CommandManager, telemetryReporter: TelemetryReporter, ) { - return new VersionDependentRegistration(client, API.v240, () => { + return new VersionDependentRegistration(client, TypeScriptRefactorProvider.minVersion, () => { return vscode.languages.registerCodeActionsProvider(selector, new TypeScriptRefactorProvider(client, formattingOptionsManager, commandManager, telemetryReporter), TypeScriptRefactorProvider.metadata); diff --git a/extensions/typescript-language-features/src/features/references.ts b/extensions/typescript-language-features/src/features/references.ts index fe854d422e8..8af1912776a 100644 --- a/extensions/typescript-language-features/src/features/references.ts +++ b/extensions/typescript-language-features/src/features/references.ts @@ -19,7 +19,7 @@ class TypeScriptReferenceSupport implements vscode.ReferenceProvider { options: vscode.ReferenceContext, token: vscode.CancellationToken ): Promise<vscode.Location[]> { - const filepath = this.client.toPath(document.uri); + const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return []; } diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts index 2fecabafb5b..ff09a9a4346 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts @@ -10,39 +10,46 @@ import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import { CachedResponse, ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'; +import * as typeConverters from '../utils/typeConverters'; +import { ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'; +import { CachedResponse } from '../tsServer/cachedResponse'; const localize = nls.loadMessageBundle(); class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider { + public static readonly minVersion = API.v206; - public resolveCodeLens(inputCodeLens: vscode.CodeLens, _token: vscode.CancellationToken): Thenable<vscode.CodeLens> { + public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> { const codeLens = inputCodeLens as ReferencesCodeLens; - return vscode.commands.executeCommand<vscode.Location[]>('vscode.executeReferenceProvider', codeLens.document, codeLens.range.start).then((locations: vscode.Location[] | undefined) => { - if (!locations) { - throw codeLens; - } + const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); + const response = await this.client.execute('references', args, token, /* lowPriority */ true); + if (response.type !== 'response' || !response.body) { + codeLens.command = response.type === 'cancelled' + ? TypeScriptBaseCodeLensProvider.cancelledCommand + : TypeScriptBaseCodeLensProvider.errorCommand; + return codeLens; + } - const referenceLocations = locations.filter(location => + const locations = response.body.refs + .map(reference => + typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)) + .filter(location => // Exclude original definition from references !(location.uri.toString() === codeLens.document.toString() && location.range.start.isEqual(codeLens.range.start))); - codeLens.command = { - title: referenceLocations.length === 1 - ? localize('oneReferenceLabel', '1 reference') - : localize('manyReferenceLabel', '{0} references', referenceLocations.length), - command: referenceLocations.length ? 'editor.action.showReferences' : '', - arguments: [codeLens.document, codeLens.range.start, referenceLocations] - }; - return codeLens; - }).then(undefined, () => { - codeLens.command = { - title: localize('referenceErrorLabel', 'Could not determine references'), - command: '' - }; - return codeLens; - }); + codeLens.command = { + title: this.getCodeLensLabel(locations), + command: locations.length ? 'editor.action.showReferences' : '', + arguments: [codeLens.document, codeLens.range.start, locations] + }; + return codeLens; + } + + private getCodeLensLabel(locations: ReadonlyArray<vscode.Location>): string { + return locations.length === 1 + ? localize('oneReferenceLabel', '1 reference') + : localize('manyReferenceLabel', '{0} references', locations.length); } protected extractSymbol( @@ -92,7 +99,7 @@ export function register( client: ITypeScriptServiceClient, cachedResponse: CachedResponse<Proto.NavTreeResponse>, ) { - return new VersionDependentRegistration(client, API.v206, () => + return new VersionDependentRegistration(client, TypeScriptReferencesCodeLensProvider.minVersion, () => new ConfigurationDependentRegistration(modeId, 'referencesCodeLens.enabled', () => { return vscode.languages.registerCodeLensProvider(selector, new TypeScriptReferencesCodeLensProvider(client, cachedResponse)); diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index a82bcb6ccee..aff2c508c53 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -10,12 +10,14 @@ import * as Proto from '../protocol'; import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; import * as typeConverters from '../utils/typeConverters'; +import FileConfigurationManager from './fileConfigurationManager'; const localize = nls.loadMessageBundle(); class TypeScriptRenameProvider implements vscode.RenameProvider { public constructor( - private readonly client: ITypeScriptServiceClient + private readonly client: ITypeScriptServiceClient, + private readonly fileConfigurationManager: FileConfigurationManager ) { } public async prepareRename( @@ -36,8 +38,7 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { if (this.client.apiVersion.gte(API.v310)) { const triggerSpan = renameInfo.triggerSpan; if (triggerSpan) { - const range = typeConverters.Range.fromTextSpan(triggerSpan); - return range; + return typeConverters.Range.fromTextSpan(triggerSpan); } } @@ -78,8 +79,8 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken - ): Promise<ServerResponse<Proto.RenameResponse> | undefined> { - const file = this.client.toPath(document.uri); + ): Promise<ServerResponse.Response<Proto.RenameResponse> | undefined> { + const file = this.client.toOpenedFilePath(document); if (!file) { return undefined; } @@ -90,7 +91,10 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { findInComments: false }; - return this.client.execute('rename', args, token); + return this.client.interruptGetErr(() => { + this.fileConfigurationManager.ensureConfigurationForDocument(document, token); + return this.client.execute('rename', args, token); + }); } private updateLocs( @@ -115,7 +119,7 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { newName: string, token: vscode.CancellationToken, ): Promise<vscode.WorkspaceEdit | undefined> { - // Make sure we preserve file exension if none provided + // Make sure we preserve file extension if none provided if (!path.extname(newName)) { newName += path.extname(fileToRename); } @@ -142,6 +146,8 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, ) { - return vscode.languages.registerRenameProvider(selector, new TypeScriptRenameProvider(client)); + return vscode.languages.registerRenameProvider(selector, + new TypeScriptRenameProvider(client, fileConfigurationManager)); } diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/features/signatureHelp.ts index 8eac3a9ca91..189185a37fc 100644 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ b/extensions/typescript-language-features/src/features/signatureHelp.ts @@ -22,18 +22,18 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, - context?: vscode.SignatureHelpContext, + context: vscode.SignatureHelpContext, ): Promise<vscode.SignatureHelp | undefined> { - const filepath = this.client.toPath(document.uri); + const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return undefined; } const args: Proto.SignatureHelpRequestArgs = { ...typeConverters.Position.toFileLocationRequestArgs(filepath, position), - triggerReason: toTsTriggerReason(context!) + triggerReason: toTsTriggerReason(context) }; - const response = await this.client.execute('signatureHelp', args, token); + const response = await this.client.interruptGetErr(() => this.client.execute('signatureHelp', args, token)); if (response.type !== 'response' || !response.body) { return undefined; } @@ -60,20 +60,34 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { Previewer.plain(item.prefixDisplayParts), Previewer.markdownDocumentation(item.documentation, item.tags.filter(x => x.name !== 'param'))); - signature.parameters = item.parameters.map(p => - new vscode.ParameterInformation( - Previewer.plain(p.displayParts), - Previewer.markdownDocumentation(p.documentation, []))); + let textIndex = signature.label.length; + const separatorLabel = Previewer.plain(item.separatorDisplayParts); + for (let i = 0; i < item.parameters.length; ++i) { + const parameter = item.parameters[i]; + const label = Previewer.plain(parameter.displayParts); + + signature.parameters.push( + new vscode.ParameterInformation( + [textIndex, textIndex + label.length], + Previewer.markdownDocumentation(parameter.documentation, []))); + + textIndex += label.length; + signature.label += label; + + if (i !== item.parameters.length - 1) { + signature.label += separatorLabel; + textIndex += separatorLabel.length; + } + } - signature.label += signature.parameters.map(parameter => parameter.label).join(Previewer.plain(item.separatorDisplayParts)); signature.label += Previewer.plain(item.suffixDisplayParts); return signature; } } function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.SignatureHelpTriggerReason { - switch (context.triggerReason) { - case vscode.SignatureHelpTriggerReason.TriggerCharacter: + switch (context.triggerKind) { + case vscode.SignatureHelpTriggerKind.TriggerCharacter: if (context.triggerCharacter) { if (context.isRetrigger) { return { kind: 'retrigger', triggerCharacter: context.triggerCharacter as any }; @@ -84,10 +98,10 @@ function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.Signatur return { kind: 'invoked' }; } - case vscode.SignatureHelpTriggerReason.ContentChange: + case vscode.SignatureHelpTriggerKind.ContentChange: return context.isRetrigger ? { kind: 'retrigger' } : { kind: 'invoked' }; - case vscode.SignatureHelpTriggerReason.Invoke: + case vscode.SignatureHelpTriggerKind.Invoke: default: return { kind: 'invoked' }; } diff --git a/extensions/typescript-language-features/src/features/smartSelect.ts b/extensions/typescript-language-features/src/features/smartSelect.ts new file mode 100644 index 00000000000..3d05d1309a2 --- /dev/null +++ b/extensions/typescript-language-features/src/features/smartSelect.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import * as typeConverters from '../utils/typeConverters'; + +class SmartSelection implements vscode.SelectionRangeProvider { + public static readonly minVersion = API.v350; + + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public async provideSelectionRanges( + document: vscode.TextDocument, + positions: vscode.Position[], + token: vscode.CancellationToken, + ): Promise<vscode.SelectionRange[] | undefined> { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return undefined; + } + + const args: Proto.SelectionRangeRequestArgs = { + file, + locations: positions.map(typeConverters.Position.toLocation) + }; + const response = await this.client.execute('selectionRange', args, token); + if (response.type !== 'response' || !response.body) { + return undefined; + } + return response.body.map(SmartSelection.convertSelectionRange); + } + + private static convertSelectionRange( + selectionRange: Proto.SelectionRange + ): vscode.SelectionRange { + return new vscode.SelectionRange( + typeConverters.Range.fromTextSpan(selectionRange.textSpan), + selectionRange.parent ? SmartSelection.convertSelectionRange(selectionRange.parent) : undefined, + ); + } +} + +export function register( + selector: vscode.DocumentSelector, + client: ITypeScriptServiceClient, +) { + return new VersionDependentRegistration(client, SmartSelection.minVersion, () => + vscode.languages.registerSelectionRangeProvider(selector, new SmartSelection(client))); +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/features/tagClosing.ts index c854db7d6ae..c8db74597e5 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/features/tagClosing.ts @@ -12,6 +12,7 @@ import { Disposable } from '../utils/dispose'; import * as typeConverters from '../utils/typeConverters'; class TagClosing extends Disposable { + public static readonly minVersion = API.v300; private _disposed = false; private _timeout: NodeJS.Timer | undefined = undefined; @@ -52,7 +53,7 @@ class TagClosing extends Disposable { return; } - const filepath = this.client.toPath(document.uri); + const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return; } @@ -144,6 +145,7 @@ export class ActiveDocumentDependentRegistration extends Disposable { super(); this._registration = this._register(new ConditionalRegistration(register)); vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); + vscode.workspace.onDidOpenTextDocument(this.onDidOpenDocument, this, this._disposables); this.update(); } @@ -152,6 +154,13 @@ export class ActiveDocumentDependentRegistration extends Disposable { const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); this._registration.update(enabled); } + + private onDidOpenDocument(openedDocument: vscode.TextDocument) { + if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document === openedDocument) { + // The active document's language may have changed + this.update(); + } + } } export function register( @@ -159,7 +168,7 @@ export function register( modeId: string, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, API.v300, () => + return new VersionDependentRegistration(client, TagClosing.minVersion, () => new ConfigurationDependentRegistration(modeId, 'autoClosingTags', () => new ActiveDocumentDependentRegistration(selector, () => new TagClosing(client)))); diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index 6d6f1e04f46..1b1d4d3bded 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -7,10 +7,12 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import * as jsonc from 'jsonc-parser'; import { ITypeScriptServiceClient } from '../typescriptService'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile } from '../utils/tsconfig'; import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider'; +import { Disposable } from '../utils/dispose'; const localize = nls.loadMessageBundle(); @@ -158,7 +160,7 @@ class TscTaskProvider implements vscode.TaskProvider { return undefined; } - private getActiveTypeScriptFile(): string | null { + private getActiveTypeScriptFile(): string | undefined { const editor = vscode.window.activeTextEditor; if (editor) { const document = editor.document; @@ -166,7 +168,7 @@ class TscTaskProvider implements vscode.TaskProvider { return this.client.value.toPath(document.uri); } } - return null; + return undefined; } private async getTasksForProject(project: TSConfig): Promise<vscode.Task[]> { @@ -197,7 +199,7 @@ class TscTaskProvider implements vscode.TaskProvider { project.workspaceFolder || vscode.TaskScope.Workspace, localize('buildAndWatchTscLabel', 'watch - {0}', label), 'tsc', - new vscode.ShellExecution(command, ['--watch', ...args]), + new vscode.ShellExecution(command, [...args, '--watch']), '$tsc-watch'); watchTask.group = vscode.TaskGroup.Build; watchTask.isBackground = true; @@ -216,7 +218,7 @@ class TscTaskProvider implements vscode.TaskProvider { } try { - const tsconfig = JSON.parse(result.toString()); + const tsconfig = jsonc.parse(result.toString()); if (tsconfig.references) { return resolve(['-b', project.path]); } @@ -244,23 +246,24 @@ class TscTaskProvider implements vscode.TaskProvider { /** * Manages registrations of TypeScript task providers with VS Code. */ -export default class TypeScriptTaskProviderManager { +export default class TypeScriptTaskProviderManager extends Disposable { private taskProviderSub: vscode.Disposable | undefined = undefined; - private readonly disposables: vscode.Disposable[] = []; constructor( private readonly client: Lazy<ITypeScriptServiceClient> ) { - vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this.disposables); + super(); + vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this._disposables); this.onConfigurationChanged(); } dispose() { + super.dispose(); + if (this.taskProviderSub) { this.taskProviderSub.dispose(); this.taskProviderSub = undefined; } - this.disposables.forEach(x => x.dispose()); } private onConfigurationChanged() { @@ -272,4 +275,4 @@ export default class TypeScriptTaskProviderManager { this.taskProviderSub = vscode.workspace.registerTaskProvider('typescript', new TscTaskProvider(this.client)); } } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/features/tsconfig.ts b/extensions/typescript-language-features/src/features/tsconfig.ts index a7e89ea1332..2a47c0887bd 100644 --- a/extensions/typescript-language-features/src/features/tsconfig.ts +++ b/extensions/typescript-language-features/src/features/tsconfig.ts @@ -26,23 +26,30 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { } return [ - this.getExendsLink(document, root), + this.getExtendsLink(document, root), ...this.getFilesLinks(document, root), ...this.getReferencesLinks(document, root) ].filter(x => !!x) as vscode.DocumentLink[]; } - private getExendsLink(document: vscode.TextDocument, root: jsonc.Node): vscode.DocumentLink | undefined { + private getExtendsLink(document: vscode.TextDocument, root: jsonc.Node): vscode.DocumentLink | undefined { const extendsNode = jsonc.findNodeAtLocation(root, ['extends']); if (!this.isPathValue(extendsNode)) { return undefined; } + if (extendsNode.value.startsWith('.')) { + return new vscode.DocumentLink( + this.getRange(document, extendsNode), + vscode.Uri.file(join(dirname(document.uri.fsPath), extendsNode.value + (extendsNode.value.endsWith('.json') ? '' : '.json'))) + ); + } + + const workspaceFolderPath = vscode.workspace.getWorkspaceFolder(document.uri)!.uri.fsPath; 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'))); + vscode.Uri.file(join(workspaceFolderPath, 'node_modules', extendsNode.value + (extendsNode.value.endsWith('.json') ? '' : '.json'))) + ); } private getFilesLinks(document: vscode.TextDocument, root: jsonc.Node) { diff --git a/extensions/typescript-language-features/src/features/typeDefinitions.ts b/extensions/typescript-language-features/src/features/typeDefinitions.ts index 45d89aeaedd..86510f65189 100644 --- a/extensions/typescript-language-features/src/features/typeDefinitions.ts +++ b/extensions/typescript-language-features/src/features/typeDefinitions.ts @@ -10,6 +10,8 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import DefinitionProviderBase from './definitionProviderBase'; export default class TypeScriptTypeDefinitionProvider extends DefinitionProviderBase implements vscode.TypeDefinitionProvider { + public static readonly minVersion = API.v213; + public provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<vscode.Definition | undefined> { return this.getSymbolLocations('typeDefinition', document, position, token); } @@ -19,7 +21,7 @@ export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, ) { - return new VersionDependentRegistration(client, API.v213, () => { + return new VersionDependentRegistration(client, TypeScriptTypeDefinitionProvider.minVersion, () => { return vscode.languages.registerTypeDefinitionProvider(selector, new TypeScriptTypeDefinitionProvider(client)); }); diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 66425a7c222..c4c2e9ca135 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -10,52 +10,63 @@ import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; +import { nulToken } from '../utils/cancellation'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { Disposable } from '../utils/dispose'; import * as fileSchemes from '../utils/fileSchemes'; 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(); const updateImportsOnFileMoveName = 'updateImportsOnFileMove.enabled'; +function isDirectory(path: string): Promise<boolean> { + return new Promise<boolean>((resolve, reject) => { + fs.stat(path, (err, stat) => { + if (err) { + return reject(err); + } + return resolve(stat.isDirectory()); + }); + }); +} + enum UpdateImportsOnFileMoveSetting { Prompt = 'prompt', Always = 'always', Never = 'never', } -class UpdateImportsOnFileRenameHandler { - private readonly _onDidRenameSub: vscode.Disposable; +class UpdateImportsOnFileRenameHandler extends Disposable { + public static minVersion = API.v300; public constructor( private readonly client: ITypeScriptServiceClient, private readonly fileConfigurationManager: FileConfigurationManager, private readonly _handles: (uri: vscode.Uri) => Promise<boolean>, ) { - this._onDidRenameSub = vscode.workspace.onDidRenameFile(e => { - this.doRename(e.oldUri, e.newUri); - }); - } + super(); - public dispose() { - this._onDidRenameSub.dispose(); + this._register(vscode.workspace.onDidRenameFile(e => { + vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: localize('renameProgress.title', "Checking for update of JS/TS imports") + }, () => { + return this.doRename(e.oldUri, e.newUri); + }); + })); } private async doRename( oldResource: vscode.Uri, newResource: vscode.Uri, ): Promise<void> { - const targetResource = await this.getTargetResource(newResource); - if (!targetResource) { - return; - } - - const targetFile = this.client.toPath(targetResource); - if (!targetFile) { + // Try to get a js/ts file that is being moved + // For directory moves, this returns a js/ts file under the directory. + const jsTsFileThatIsBeingMoved = await this.getJsTsFileBeingMoved(newResource); + if (!jsTsFileThatIsBeingMoved || !this.client.toPath(jsTsFileThatIsBeingMoved)) { return; } @@ -69,7 +80,7 @@ class UpdateImportsOnFileRenameHandler { return; } - const document = await vscode.workspace.openTextDocument(targetResource); + const document = await vscode.workspace.openTextDocument(jsTsFileThatIsBeingMoved); const config = this.getConfiguration(document); const setting = config.get<UpdateImportsOnFileMoveSetting>(updateImportsOnFileMoveName); @@ -81,27 +92,7 @@ class UpdateImportsOnFileRenameHandler { this.client.bufferSyncSupport.closeResource(oldResource); this.client.bufferSyncSupport.openTextDocument(document); - if (this.client.apiVersion.lt(API.v300) && !fs.lstatSync(newResource.fsPath).isDirectory()) { - // 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 response = await this.client.execute('navtree', { file: newFile }, nulToken); - if (response.type !== 'response' || !response.body) { - return; - } - - const hasExport = (node: Proto.NavigationTree): boolean => { - return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport)); - }; - if (!hasExport(response.body)) { - return; - } - } catch { - // noop - } - } - - const edits = await this.getEditsForFileRename(targetFile, document, oldFile, newFile); + const edits = await this.getEditsForFileRename(document, oldFile, newFile); if (!edits || !edits.size) { return; } @@ -202,17 +193,12 @@ class UpdateImportsOnFileRenameHandler { return false; } - private async getTargetResource(resource: vscode.Uri): Promise<vscode.Uri | undefined> { + private async getJsTsFileBeingMoved(resource: vscode.Uri): Promise<vscode.Uri | undefined> { if (resource.scheme !== fileSchemes.file) { return undefined; } - const isDirectory = fs.lstatSync(resource.fsPath).isDirectory(); - if (isDirectory && this.client.apiVersion.gte(API.v300)) { - return resource; - } - - if (isDirectory && this.client.apiVersion.gte(API.v292)) { + if (await isDirectory(resource.fsPath)) { const files = await vscode.workspace.findFiles({ base: resource.fsPath, pattern: '**/*.{ts,tsx,js,jsx}', @@ -224,87 +210,23 @@ class UpdateImportsOnFileRenameHandler { } private async getEditsForFileRename( - targetResource: string, document: vscode.TextDocument, oldFile: string, newFile: string, - ) { - const isDirectoryRename = fs.lstatSync(newFile).isDirectory(); - 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, nulToken); - if (response.type !== 'response') { - return; - } - - if (!response.body) { - return; - } - - const edits: Proto.FileCodeEdits[] = []; - for (const edit of response.body) { - // Workaround for https://github.com/Microsoft/vscode/issues/52675 - if (this.client.apiVersion.lt(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)); - } - return typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, edits); - } - - private async fixEdit( - edit: Proto.FileCodeEdits, - isDirectoryRename: boolean, - oldFile: string, - newFile: string, - ): Promise<Proto.FileCodeEdits> { - if (!isDirectoryRename || this.client.apiVersion.gte(API.v300)) { - return edit; - } - - const document = await vscode.workspace.openTextDocument(edit.fileName); - const oldFileRe = new RegExp('/' + escapeRegExp(path.basename(oldFile)) + '/'); - - // Workaround for https://github.com/Microsoft/TypeScript/issues/24968 - const textChanges = edit.textChanges.map((change): Proto.CodeEdit => { - const existingText = document.getText(typeConverters.Range.fromTextSpan(change)); - const existingMatch = existingText.match(oldFileRe); - if (!existingMatch) { - return change; - } - - const match = new RegExp('/' + escapeRegExp(path.basename(newFile)) + '/(.+)$', 'g').exec(change.newText); - if (!match) { - return change; - } - - return { - newText: change.newText.slice(0, -match[1].length), - start: change.start, - end: { - line: change.end.line, - offset: change.end.offset - match[1].length, - }, + ): Promise<vscode.WorkspaceEdit | undefined> { + const response = await this.client.interruptGetErr(() => { + this.fileConfigurationManager.setGlobalConfigurationFromDocument(document, nulToken); + const args: Proto.GetEditsForFileRenameRequestArgs = { + oldFilePath: oldFile, + newFilePath: newFile, }; + return this.client.execute('getEditsForFileRename', args, nulToken); }); + if (response.type !== 'response' || !response.body) { + return; + } - return { - fileName: edit.fileName, - textChanges, - }; + return typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body); } } @@ -313,6 +235,6 @@ export function register( fileConfigurationManager: FileConfigurationManager, handles: (uri: vscode.Uri) => Promise<boolean>, ) { - return new VersionDependentRegistration(client, API.v290, () => + return new VersionDependentRegistration(client, UpdateImportsOnFileRenameHandler.minVersion, () => 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 ebbc4914326..a8aefa64db7 100644 --- a/extensions/typescript-language-features/src/features/workspaceSymbols.ts +++ b/extensions/typescript-language-features/src/features/workspaceSymbols.ts @@ -30,12 +30,12 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide search: string, token: vscode.CancellationToken ): Promise<vscode.SymbolInformation[]> { - const uri = this.getUri(); - if (!uri) { + const document = this.getDocument(); + if (!document) { return []; } - const filepath = this.client.toPath(uri); + const filepath = this.client.toOpenedFilePath(document); if (!filepath) { return []; } @@ -70,7 +70,7 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide return label; } - private getUri(): vscode.Uri | undefined { + private getDocument(): vscode.TextDocument | undefined { // typescript wants to have a resource even when asking // general questions so we check the active editor. If this // doesn't match we take the first TS document. @@ -79,14 +79,14 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide if (editor) { const document = editor.document; if (document && this.modeIds.indexOf(document.languageId) >= 0) { - return document.uri; + return document; } } const documents = vscode.workspace.textDocuments; for (const document of documents) { if (this.modeIds.indexOf(document.languageId) >= 0) { - return document.uri; + return document; } } return undefined; diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index e447baf09a2..4210ba2ba20 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -5,7 +5,7 @@ import { basename } from 'path'; import * as vscode from 'vscode'; -import { CachedResponse } from './features/baseCodeLensProvider'; +import { CachedResponse } from './tsServer/cachedResponse'; import { DiagnosticKind } from './features/diagnostics'; import FileConfigurationManager from './features/fileConfigurationManager'; import TypeScriptServiceClient from './typescriptServiceClient'; @@ -55,26 +55,30 @@ export default class LanguageProvider extends Disposable { const cachedResponse = new CachedResponse(); - this._register((await import('./features/completions')).register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.onCompletionAccepted)); - this._register((await import('./features/definitions')).register(selector, this.client)); - this._register((await import('./features/directiveCommentCompletions')).register(selector, this.client)); - this._register((await import('./features/documentHighlight')).register(selector, this.client)); - 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._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)); + await Promise.all([ + import('./features/completions').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.telemetryReporter, this.onCompletionAccepted))), + import('./features/definitions').then(provider => this._register(provider.register(selector, this.client))), + import('./features/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))), + import('./features/documentHighlight').then(provider => this._register(provider.register(selector, this.client))), + import('./features/documentSymbol').then(provider => this._register(provider.register(selector, this.client, cachedResponse))), + import('./features/folding').then(provider => this._register(provider.register(selector, this.client))), + import('./features/formatting').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))), + import('./features/hover').then(provider => this._register(provider.register(selector, this.client))), + import('./features/implementations').then(provider => this._register(provider.register(selector, this.client))), + import('./features/implementationsCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), + import('./features/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description.id, this.client))), + import('./features/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))), + import('./features/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))), + import('./features/fixAll').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager))), + import('./features/refactor').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter))), + import('./features/references').then(provider => this._register(provider.register(selector, this.client))), + import('./features/referencesCodeLens').then(provider => this._register(provider.register(selector, this.description.id, this.client, cachedResponse))), + import('./features/rename').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), + import('./features/smartSelect').then(provider => this._register(provider.register(selector, this.client))), + import('./features/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), + import('./features/tagClosing').then(provider => this._register(provider.register(selector, this.description.id, this.client))), + import('./features/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), + ]); } private configurationChanged(): void { @@ -89,7 +93,7 @@ export default class LanguageProvider extends Disposable { } const base = basename(resource.fsPath); - return !!base && base === this.description.configFile; + return !!base && (!!this.description.configFilePattern && this.description.configFilePattern.test(base)); } private get id(): string { diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts index dcb1b33e159..efded538f77 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -60,4 +60,13 @@ export class KindModifiers { KindModifiers.jsxFile, KindModifiers.jsonFile, ]; +} + +export class DisplayPartKind { + public static readonly functionName = 'functionName'; + public static readonly methodName = 'methodName'; + public static readonly parameterName = 'parameterName'; + public static readonly propertyName = 'propertyName'; + public static readonly punctuation = 'punctuation'; + public static readonly text = 'text'; } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/test/cachedResponse.test.ts b/extensions/typescript-language-features/src/test/cachedResponse.test.ts new file mode 100644 index 00000000000..919d5d4d03c --- /dev/null +++ b/extensions/typescript-language-features/src/test/cachedResponse.test.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { CachedResponse } from '../tsServer/cachedResponse'; +import { ServerResponse } from '../typescriptService'; + +suite('CachedResponse', () => { + test('should cache simple response for same document', async () => { + const doc = await createTextDocument(); + const response = new CachedResponse(); + + assertResult(await response.execute(doc, respondWith('test-0')), 'test-0'); + assertResult(await response.execute(doc, respondWith('test-1')), 'test-0'); + }); + + test('should invalidate cache for new document', async () => { + const doc1 = await createTextDocument(); + const doc2 = await createTextDocument(); + const response = new CachedResponse(); + + assertResult(await response.execute(doc1, respondWith('test-0')), 'test-0'); + assertResult(await response.execute(doc1, respondWith('test-1')), 'test-0'); + assertResult(await response.execute(doc2, respondWith('test-2')), 'test-2'); + assertResult(await response.execute(doc2, respondWith('test-3')), 'test-2'); + assertResult(await response.execute(doc1, respondWith('test-4')), 'test-4'); + assertResult(await response.execute(doc1, respondWith('test-5')), 'test-4'); + }); + + test('should not cache cancelled responses', async () => { + const doc = await createTextDocument(); + const response = new CachedResponse(); + + const cancelledResponder = createEventualResponder<ServerResponse.Cancelled>(); + const result1 = response.execute(doc, () => cancelledResponder.promise); + const result2 = response.execute(doc, respondWith('test-0')); + const result3 = response.execute(doc, respondWith('test-1')); + + cancelledResponder.resolve(new ServerResponse.Cancelled('cancelled')); + + assert.strictEqual((await result1).type, 'cancelled'); + assertResult(await result2, 'test-0'); + assertResult(await result3, 'test-0'); + }); + + test('should not care if subsequent requests are cancelled if first request is resolved ok', async () => { + const doc = await createTextDocument(); + const response = new CachedResponse(); + + const cancelledResponder = createEventualResponder<ServerResponse.Cancelled>(); + const result1 = response.execute(doc, respondWith('test-0')); + const result2 = response.execute(doc, () => cancelledResponder.promise); + const result3 = response.execute(doc, respondWith('test-1')); + + cancelledResponder.resolve(new ServerResponse.Cancelled('cancelled')); + + assertResult(await result1, 'test-0'); + assertResult(await result2, 'test-0'); + assertResult(await result3, 'test-0'); + }); + + test('should not cache cancelled responses with document changes', async () => { + const doc1 = await createTextDocument(); + const doc2 = await createTextDocument(); + const response = new CachedResponse(); + + const cancelledResponder = createEventualResponder<ServerResponse.Cancelled>(); + const cancelledResponder2 = createEventualResponder<ServerResponse.Cancelled>(); + + const result1 = response.execute(doc1, () => cancelledResponder.promise); + const result2 = response.execute(doc1, respondWith('test-0')); + const result3 = response.execute(doc1, respondWith('test-1')); + const result4 = response.execute(doc2, () => cancelledResponder2.promise); + const result5 = response.execute(doc2, respondWith('test-2')); + const result6 = response.execute(doc1, respondWith('test-3')); + + cancelledResponder.resolve(new ServerResponse.Cancelled('cancelled')); + cancelledResponder2.resolve(new ServerResponse.Cancelled('cancelled')); + + assert.strictEqual((await result1).type, 'cancelled'); + assertResult(await result2, 'test-0'); + assertResult(await result3, 'test-0'); + assert.strictEqual((await result4).type, 'cancelled'); + assertResult(await result5, 'test-2'); + assertResult(await result6, 'test-3'); + }); +}); + +function respondWith(command: string) { + return async () => createResponse(command); +} + +function createTextDocument() { + return vscode.workspace.openTextDocument({ language: 'javascript', content: '' }); +} + +function assertResult(result: ServerResponse.Response<Proto.Response>, command: string) { + if (result.type === 'response') { + assert.strictEqual(result.command, command); + } else { + assert.fail('Response failed'); + } +} + +function createResponse(command: string): Proto.Response { + return { + type: 'response', + body: {}, + command: command, + request_seq: 1, + success: true, + seq: 1 + }; +} + +function createEventualResponder<T>(): { promise: Promise<T>, resolve: (x: T) => void } { + let resolve: (value: T) => void; + const promise = new Promise<T>(r => { resolve = r; }); + return { promise, resolve: resolve! }; +} diff --git a/extensions/typescript-language-features/src/test/completions.test.ts b/extensions/typescript-language-features/src/test/completions.test.ts new file mode 100644 index 00000000000..563930d78d5 --- /dev/null +++ b/extensions/typescript-language-features/src/test/completions.test.ts @@ -0,0 +1,334 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils/dispose'; + +const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); + +type VsCodeConfiguration = { [key: string]: any }; + +async function updateConfig(newConfig: VsCodeConfiguration): Promise<VsCodeConfiguration> { + const oldConfig: VsCodeConfiguration = {}; + const config = vscode.workspace.getConfiguration(undefined, testDocumentUri); + for (const configKey of Object.keys(newConfig)) { + oldConfig[configKey] = config.get(configKey); + await new Promise((resolve, reject) => + config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global) + .then(() => resolve(), reject)); + } + return oldConfig; +} + +namespace Config { + export const suggestSelection = 'editor.suggestSelection'; + export const completeFunctionCalls = 'typescript.suggest.completeFunctionCalls'; +} + +suite('TypeScript Completions', () => { + const configDefaults: VsCodeConfiguration = Object.freeze({ + [Config.suggestSelection]: 'first', + [Config.completeFunctionCalls]: false, + }); + + const _disposables: vscode.Disposable[] = []; + let oldConfig: { [key: string]: any } = {}; + + setup(async () => { + await wait(100); + + // Save off config and apply defaults + oldConfig = await updateConfig(configDefaults); + }); + + teardown(async () => { + disposeAll(_disposables); + + // Restore config + await updateConfig(oldConfig); + + return vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Basic var completion', async () => { + await createTestEditor(testDocumentUri, + `const abcdef = 123;`, + `ab$0;` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `const abcdef = 123;`, + `abcdef;` + )); + }); + + test('Should treat period as commit character for var completions', async () => { + await createTestEditor(testDocumentUri, + `const abcdef = 123;`, + `ab$0;` + ); + + const document = await typeCommitCharacter(testDocumentUri, '.', _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `const abcdef = 123;`, + `abcdef.;` + )); + }); + + test('Should treat paren as commit character for function completions', async () => { + await createTestEditor(testDocumentUri, + `function abcdef() {};`, + `ab$0;` + ); + + const document = await typeCommitCharacter(testDocumentUri, '(', _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef() {};`, + `abcdef();` + )); + }); + + test('Should insert backets when completing dot properties with spaces in name', async () => { + await createTestEditor(testDocumentUri, + 'const x = { "hello world": 1 };', + 'x.$0' + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + 'const x = { "hello world": 1 };', + 'x["hello world"]' + )); + }); + + test('Should allow commit characters for backet completions', async () => { + for (const { char, insert } of [ + { char: '.', insert: '.' }, + { char: '(', insert: '()' }, + ]) { + await createTestEditor(testDocumentUri, + 'const x = { "hello world2": 1 };', + 'x.$0' + ); + + const document = await typeCommitCharacter(testDocumentUri, char, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + 'const x = { "hello world2": 1 };', + `x["hello world2"]${insert}` + )); + } + }); + + test('Should not prioritize bracket accessor completions. #63100', async () => { + // 'a' should be first entry in completion list + await createTestEditor(testDocumentUri, + 'const x = { "z-z": 1, a: 1 };', + 'x.$0' + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + 'const x = { "z-z": 1, a: 1 };', + 'x.a' + )); + }); + + test('Accepting a string completion should replace the entire string. #53962', async () => { + await createTestEditor(testDocumentUri, + 'interface TFunction {', + ` (_: 'abc.abc2', __ ?: {}): string;`, + ` (_: 'abc.abc', __?: {}): string;`, + `}`, + 'const f: TFunction = (() => { }) as any;', + `f('abc.abc$0')` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + 'interface TFunction {', + ` (_: 'abc.abc2', __ ?: {}): string;`, + ` (_: 'abc.abc', __?: {}): string;`, + `}`, + 'const f: TFunction = (() => { }) as any;', + `f('abc.abc')` + )); + }); + + test.skip('Accepting a member completion should result in valid code. #58597', async () => { + await createTestEditor(testDocumentUri, + `const abc = 123;`, + `ab$0c` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `const abc = 123;`, + `abc` + )); + }); + + test('completeFunctionCalls should complete function parameters when at end of word', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + // Complete with-in word + await createTestEditor(testDocumentUri, + `function abcdef(x, y, z) { }`, + `abcdef$0` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef(x, y, z) { }`, + `abcdef(x, y, z)` + )); + }); + + test.skip('completeFunctionCalls should complete function parameters when within word', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + await createTestEditor(testDocumentUri, + `function abcdef(x, y, z) { }`, + `abcd$0ef` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef(x, y, z) { }`, + `abcdef(x, y, z)` + )); + }); + + test('completeFunctionCalls should not complete function parameters at end of word if we are already in something that looks like a function call, #18131', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + await createTestEditor(testDocumentUri, + `function abcdef(x, y, z) { }`, + `abcdef$0(1, 2, 3)` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef(x, y, z) { }`, + `abcdef(1, 2, 3)` + )); + }); + + test.skip('completeFunctionCalls should not complete function parameters within word if we are already in something that looks like a function call, #18131', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + await createTestEditor(testDocumentUri, + `function abcdef(x, y, z) { }`, + `abcd$0ef(1, 2, 3)` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef(x, y, z) { }`, + `abcdef(1, 2, 3)` + )); + }); +}); + +const joinLines = (...args: string[]) => args.join('\n'); + +const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +async function acceptFirstSuggestion(uri: vscode.Uri, _disposables: vscode.Disposable[]) { + const didChangeDocument = onChangedDocument(uri, _disposables); + const didSuggest = onDidSuggest(_disposables); + await vscode.commands.executeCommand('editor.action.triggerSuggest'); + await didSuggest; + // TODO: depends on reverting fix for https://github.com/Microsoft/vscode/issues/64257 + // Make sure we have time to resolve the suggestion because `acceptSelectedSuggestion` doesn't + await wait(40); + await vscode.commands.executeCommand('acceptSelectedSuggestion'); + return await didChangeDocument; +} + +async function typeCommitCharacter(uri: vscode.Uri, character: string, _disposables: vscode.Disposable[]) { + const didChangeDocument = onChangedDocument(uri, _disposables); + const didSuggest = onDidSuggest(_disposables); + await vscode.commands.executeCommand('editor.action.triggerSuggest'); + await didSuggest; + await vscode.commands.executeCommand('type', { text: character }); + return await didChangeDocument; +} + +function onChangedDocument(documentUri: vscode.Uri, disposables: vscode.Disposable[]) { + return new Promise<vscode.TextDocument>(resolve => vscode.workspace.onDidChangeTextDocument(e => { + if (e.document.uri.toString() === documentUri.toString()) { + resolve(e.document); + } + }, undefined, disposables)); +} + +async function createTestEditor(uri: vscode.Uri, ...lines: string[]) { + const document = await vscode.workspace.openTextDocument(uri); + await vscode.window.showTextDocument(document); + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) { + throw new Error('no active editor'); + } + + await activeEditor.insertSnippet(new vscode.SnippetString(joinLines(...lines)), new vscode.Range(0, 0, 1000, 0)); +} + +function onDidSuggest(disposables: vscode.Disposable[]) { + return new Promise(resolve => + disposables.push(vscode.languages.registerCompletionItemProvider('typescript', new class implements vscode.CompletionItemProvider { + provideCompletionItems(doc: vscode.TextDocument, position: vscode.Position): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> { + // Return a fake item that will come first + const range = new vscode.Range(new vscode.Position(position.line, 0), position); + return [{ + label: '🦄', + insertText: doc.getText(range), + filterText: doc.getText(range), + preselect: true, + sortText: '\0', + range: range + }]; + } + async resolveCompletionItem(item: vscode.CompletionItem) { + await vscode.commands.executeCommand('selectNextSuggestion'); + resolve(); + return item; + } + }))); +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts new file mode 100644 index 00000000000..e053719f3d4 --- /dev/null +++ b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { snippetForFunctionCall } from '../utils/snippetForFunctionCall'; + +suite('typescript function call snippets', () => { + test('Should use label as function name', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'abc', }, + [] + ).snippet.value, + 'abc()$0'); + }); + + test('Should use insertText string to override function name', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'abc', insertText: 'def' }, + [] + ).snippet.value, + 'def()$0'); + }); + + test('Should return insertText as-is if it is already a snippet', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'abc', insertText: new vscode.SnippetString('bla()$0') }, + [] + ).snippet.value, + 'bla()$0'); + }); + + test('Should return insertText as-is if it is already a snippet', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'abc', insertText: new vscode.SnippetString('bla()$0') }, + [] + ).snippet.value, + 'bla()$0'); + }); + + test('Should extract parameter from display parts', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'activate' }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "activate", "kind": "text" }, { "text": "(", "kind": "punctuation" }, { "text": "context", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "vscode", "kind": "aliasName" }, { "text": ".", "kind": "punctuation" }, { "text": "ExtensionContext", "kind": "interfaceName" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'activate(${1:context})$0'); + }); + + test('Should extract all parameters from display parts', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foo' }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foo", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "a", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": ",", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "b", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }, { "text": ",", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "c", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "boolean", "kind": "keyword" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'foo(${1:a}, ${2:b}, ${3:c})$0'); + }); + + test('Should create empty placeholder at rest parameter', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foo' }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foo", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "a", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": ",", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "...", "kind": "punctuation" }, { "text": "rest", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "any", "kind": "keyword" }, { "text": "[", "kind": "punctuation" }, { "text": "]", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'foo(${1:a}$2)$0'); + }); + + test('Should skip over inline function and object types when extracting parameters', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foo' }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foo", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "a", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "(", "kind": "punctuation" }, { "text": "x", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }, { "text": ")", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "=>", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "{", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": " ", "kind": "space" }, { "text": "f", "kind": "propertyName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "(", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "=>", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }, { "text": ";", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": "}", "kind": "punctuation" }, { "text": ",", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "b", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "{", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": " ", "kind": "space" }, { "text": "f", "kind": "propertyName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "(", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "=>", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }, { "text": ";", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": "}", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'foo(${1:a}, ${2:b})$0'); + }); + + test('Should skip over return type while extracting parameters', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foo' }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foo", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "a", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "{", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": " ", "kind": "space" }, { "text": "f", "kind": "propertyName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "(", "kind": "punctuation" }, { "text": "b", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }, { "text": ")", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "=>", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }, { "text": ";", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": "}", "kind": "punctuation" }] + ).snippet.value, + 'foo(${1:a})$0'); + }); + + test('Should skip over prefix type while extracting parameters', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foo' }, + [{ "text": "(", "kind": "punctuation" }, { "text": "method", "kind": "text" }, { "text": ")", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "Array", "kind": "localName" }, { "text": "<", "kind": "punctuation" }, { "text": "{", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "dispose", "kind": "methodName" }, { "text": "(", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "any", "kind": "keyword" }, { "text": ";", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "}", "kind": "punctuation" }, { "text": ">", "kind": "punctuation" }, { "text": ".", "kind": "punctuation" }, { "text": "foo", "kind": "methodName" }, { "text": "(", "kind": "punctuation" }, { "text": "searchElement", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "{", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": " ", "kind": "space" }, { "text": "dispose", "kind": "methodName" }, { "text": "(", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "any", "kind": "keyword" }, { "text": ";", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": "}", "kind": "punctuation" }, { "text": ",", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "fromIndex", "kind": "parameterName" }, { "text": "?", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }] + ).snippet.value, + 'foo(${1:searchElement}$2)$0'); + }); + + test('Should complete property names', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'methoda' }, + [{ "text": "(", "kind": "punctuation" }, { "text": "method", "kind": "text" }, { "text": ")", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "methoda", "kind": "propertyName" }, { "text": "(", "kind": "punctuation" }, { "text": "x", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "number", "kind": "keyword" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'methoda(${1:x})$0'); + }); + + test('Should escape snippet syntax in method name', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: '$abc', }, + [] + ).snippet.value, + '\\$abc()$0'); + }); + + test('Should not include object key signature in completion, #66297', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foobar', }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foobar", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "param", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "{", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": " ", "kind": "space" }, { "text": "[", "kind": "punctuation" }, { "text": "key", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": "]", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": ";", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": "}", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'foobar(${1:param})$0'); + }); +}); diff --git a/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts index 7446a150016..a982e46221f 100644 --- a/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts @@ -7,6 +7,8 @@ import * as assert from 'assert'; import 'mocha'; import { templateToSnippet } from '../features/jsDocCompletions'; +const joinLines = (...args: string[]) => args.join('\n'); + suite('typescript.jsDocSnippet', () => { test('Should do nothing for single line input', async () => { const input = `/** */`; @@ -15,63 +17,63 @@ suite('typescript.jsDocSnippet', () => { test('Should put cursor inside multiline line input', async () => { assert.strictEqual( - templateToSnippet([ + templateToSnippet(joinLines( '/**', ' * ', ' */' - ].join('\n')).value, - [ + )).value, + joinLines( '/**', ' * $0', ' */' - ].join('\n')); + )); }); test('Should add placeholders after each parameter', async () => { assert.strictEqual( - templateToSnippet([ + templateToSnippet(joinLines( '/**', ' * @param a', ' * @param b', ' */' - ].join('\n')).value, - [ + )).value, + joinLines( '/**', ' * @param a ${1}', ' * @param b ${2}', ' */' - ].join('\n')); + )); }); test('Should add placeholders for types', async () => { assert.strictEqual( - templateToSnippet([ + templateToSnippet(joinLines( '/**', ' * @param {*} a', ' * @param {*} b', ' */' - ].join('\n')).value, - [ + )).value, + joinLines( '/**', ' * @param {${1:*}} a ${2}', ' * @param {${3:*}} b ${4}', ' */' - ].join('\n')); + )); }); test('Should properly escape dollars in parameter names', async () => { assert.strictEqual( - templateToSnippet([ + templateToSnippet(joinLines( '/**', ' * ', ' * @param $arg', ' */' - ].join('\n')).value, - [ + )).value, + joinLines( '/**', ' * $0', ' * @param \\$arg ${1}', ' */' - ].join('\n')); + )); }); }); diff --git a/extensions/typescript-language-features/src/test/onEnter.test.ts b/extensions/typescript-language-features/src/test/onEnter.test.ts new file mode 100644 index 00000000000..c7d6c3a45dd --- /dev/null +++ b/extensions/typescript-language-features/src/test/onEnter.test.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { CURSOR, withRandomFileEditor } from './testUtils'; + +const onDocumentChange = (doc: vscode.TextDocument): Promise<vscode.TextDocument> => { + return new Promise<vscode.TextDocument>(resolve => { + const sub = vscode.workspace.onDidChangeTextDocument(e => { + if (e.document !== doc) { + return; + } + sub.dispose(); + resolve(e.document); + }); + }); +}; + +const type = async (document: vscode.TextDocument, text: string): Promise<vscode.TextDocument> => { + const onChange = onDocumentChange(document); + await vscode.commands.executeCommand('type', { text }); + await onChange; + return document; +}; + +suite('OnEnter', () => { + test('should indent after if block with braces', () => { + return withRandomFileEditor(`if (true) {${CURSOR}`, 'js', async (_editor, document) => { + await type(document, '\nx'); + assert.strictEqual(document.getText(), `if (true) {\n x`); + }); + }); + + test('should indent within empty object literal', () => { + return withRandomFileEditor(`({${CURSOR}})`, 'js', async (_editor, document) => { + await type(document, '\nx'); + assert.strictEqual(document.getText(), `({\n x\n})`); + }); + }); + + test('should indent after simple jsx tag with attributes', () => { + return withRandomFileEditor(`const a = <div onclick={bla}>${CURSOR}`, 'jsx', async (_editor, document) => { + await type(document, '\nx'); + assert.strictEqual(document.getText(), `const a = <div onclick={bla}>\n x`); + }); + }); + + test('should indent after simple jsx tag with attributes', () => { + return withRandomFileEditor(`const a = <div onclick={bla}>${CURSOR}`, 'jsx', async (_editor, document) => { + await type(document, '\nx'); + assert.strictEqual(document.getText(), `const a = <div onclick={bla}>\n x`); + }); + }); +}); \ No newline at end of file diff --git a/extensions/typescript-language-features/src/test/testUtils.ts b/extensions/typescript-language-features/src/test/testUtils.ts new file mode 100644 index 00000000000..d8efdc3f9c4 --- /dev/null +++ b/extensions/typescript-language-features/src/test/testUtils.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as os from 'os'; +import { join } from 'path'; + +function rndName() { + return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); +} + +export function createRandomFile(contents = '', fileExtension = 'txt'): Thenable<vscode.Uri> { + return new Promise((resolve, reject) => { + const tmpFile = join(os.tmpdir(), rndName() + '.' + fileExtension); + fs.writeFile(tmpFile, contents, (error) => { + if (error) { + return reject(error); + } + + resolve(vscode.Uri.file(tmpFile)); + }); + }); +} + + +export function deleteFile(file: vscode.Uri): Thenable<boolean> { + return new Promise((resolve, reject) => { + fs.unlink(file.fsPath, (err) => { + if (err) { + reject(err); + } else { + resolve(true); + } + }); + }); +} + +export const CURSOR = '$$CURSOR$$'; + +export function withRandomFileEditor( + contents: string, + fileExtension: string, + run: (editor: vscode.TextEditor, doc: vscode.TextDocument) => Thenable<void> +): Thenable<boolean> { + const cursorIndex = contents.indexOf(CURSOR); + return createRandomFile(contents.replace(CURSOR, ''), fileExtension).then(file => { + return vscode.workspace.openTextDocument(file).then(doc => { + return vscode.window.showTextDocument(doc).then((editor) => { + if (cursorIndex >= 0) { + const pos = doc.positionAt(cursorIndex); + editor.selection = new vscode.Selection(pos, pos); + } + return run(editor, doc).then(_ => { + if (doc.isDirty) { + return doc.save().then(() => { + return deleteFile(file); + }); + } else { + return deleteFile(file); + } + }); + }); + }); + }); +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/tsServer/cachedResponse.ts b/extensions/typescript-language-features/src/tsServer/cachedResponse.ts new file mode 100644 index 00000000000..2fb9b542281 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/cachedResponse.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import { ServerResponse } from '../typescriptService'; + +type Resolve<T extends Proto.Response> = () => Promise<ServerResponse.Response<T>>; + +/** + * Caches a class of TS Server request based on document. + */ +export class CachedResponse<T extends Proto.Response> { + private response?: Promise<ServerResponse.Response<T>>; + private version: number = -1; + private document: string = ''; + + /** + * Execute a request. May return cached value or resolve the new value + * + * Caller must ensure that all input `resolve` functions return equivilent results (keyed only off of document). + */ + public execute( + document: vscode.TextDocument, + resolve: Resolve<T> + ): Promise<ServerResponse.Response<T>> { + if (this.response && this.matches(document)) { + // Chain so that on cancellation we fall back to the next resolve + return this.response = this.response.then(result => result.type === 'cancelled' ? resolve() : result); + } + return this.reset(document, resolve); + } + + private matches(document: vscode.TextDocument): boolean { + return this.version === document.version && this.document === document.uri.toString(); + } + + private async reset( + document: vscode.TextDocument, + resolve: Resolve<T> + ): Promise<ServerResponse.Response<T>> { + this.version = document.version; + this.document = document.uri.toString(); + return this.response = resolve(); + } +} diff --git a/extensions/typescript-language-features/src/tsServer/callbackMap.ts b/extensions/typescript-language-features/src/tsServer/callbackMap.ts index 5c187ec1485..7f96de57159 100644 --- a/extensions/typescript-language-features/src/tsServer/callbackMap.ts +++ b/extensions/typescript-language-features/src/tsServer/callbackMap.ts @@ -4,21 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as Proto from '../protocol'; -import { CancelledResponse, ServerResponse } from '../typescriptService'; +import { ServerResponse } from '../typescriptService'; export interface CallbackItem<R> { readonly onSuccess: (value: R) => void; - readonly onError: (err: any) => void; + readonly onError: (err: Error) => void; readonly startTime: number; readonly isAsync: boolean; } export class CallbackMap<R extends Proto.Response> { - private readonly _callbacks = new Map<number, CallbackItem<ServerResponse<R> | undefined>>(); - private readonly _asyncCallbacks = new Map<number, CallbackItem<ServerResponse<R> | undefined>>(); + private readonly _callbacks = new Map<number, CallbackItem<ServerResponse.Response<R> | undefined>>(); + private readonly _asyncCallbacks = new Map<number, CallbackItem<ServerResponse.Response<R> | undefined>>(); public destroy(cause: string): void { - const cancellation = new CancelledResponse(cause); + const cancellation = new ServerResponse.Cancelled(cause); for (const callback of this._callbacks.values()) { callback.onSuccess(cancellation); } @@ -29,7 +29,7 @@ export class CallbackMap<R extends Proto.Response> { this._asyncCallbacks.clear(); } - public add(seq: number, callback: CallbackItem<ServerResponse<R> | undefined>, isAsync: boolean) { + public add(seq: number, callback: CallbackItem<ServerResponse.Response<R> | undefined>, isAsync: boolean) { if (isAsync) { this._asyncCallbacks.set(seq, callback); } else { @@ -37,7 +37,7 @@ export class CallbackMap<R extends Proto.Response> { } } - public fetch(seq: number): CallbackItem<ServerResponse<R> | undefined> | undefined { + public fetch(seq: number): CallbackItem<ServerResponse.Response<R> | undefined> | undefined { const callback = this._callbacks.get(seq) || this._asyncCallbacks.get(seq); this.delete(seq); return callback; diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index cde2df2c2b7..1bfb290fa0a 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as Proto from '../protocol'; -import { CancelledResponse, NoContentResponse } from '../typescriptService'; +import { ServerResponse } from '../typescriptService'; import API from '../utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; import { Disposable } from '../utils/dispose'; @@ -17,6 +17,7 @@ import LogDirectoryProvider from '../utils/logDirectoryProvider'; import Logger from '../utils/logger'; import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; import { PluginManager } from '../utils/plugins'; +import { escapeRegExp } from '../utils/regexp'; import TelemetryReporter from '../utils/telemetry'; import Tracer from '../utils/tracer'; import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider'; @@ -24,6 +25,75 @@ import { Reader } from '../utils/wireProtocol'; import { CallbackMap } from './callbackMap'; import { RequestItem, RequestQueue, RequestQueueingType } from './requestQueue'; +class TypeScriptServerError extends Error { + + public static create( + version: TypeScriptVersion, + response: Proto.Response, + ): TypeScriptServerError { + const parsedResult = TypeScriptServerError.parseErrorText(version, response); + return new TypeScriptServerError(version, response, + parsedResult ? parsedResult.message : undefined, + parsedResult ? parsedResult.stack : undefined); + } + + constructor( + version: TypeScriptVersion, + private readonly response: Proto.Response, + public readonly serverMessage: string | undefined, + public readonly serverStack: string | undefined, + ) { + super(`TypeScript Server Error (${version.versionString})\n${serverMessage}\n${serverStack}`); + } + + public get serverErrorText() { + return this.response.message; + } + + public get serverCommand() { + return this.response.command; + } + + /** + * Given a `errorText` from a tsserver request indicating failure in handling a request, + * prepares a payload for telemetry-logging. + */ + private static parseErrorText( + version: TypeScriptVersion, + response: Proto.Response, + ) { + const errorText = response.message; + if (errorText) { + const errorPrefix = 'Error processing request. '; + if (errorText.startsWith(errorPrefix)) { + const prefixFreeErrorText = errorText.substr(errorPrefix.length); + const newlineIndex = prefixFreeErrorText.indexOf('\n'); + if (newlineIndex >= 0) { + // Newline expected between message and stack. + return { + message: prefixFreeErrorText.substring(0, newlineIndex), + stack: TypeScriptServerError.normalizeMessageStack(version, prefixFreeErrorText.substring(newlineIndex + 1)) + }; + } + } + } + return undefined; + } + + /** + * Try to replace full TS Server paths with 'tsserver.js' so that we don't have to post process the data as much + */ + private static normalizeMessageStack( + version: TypeScriptVersion, + message: string | undefined, + ) { + if (!message) { + return ''; + } + return message.replace(new RegExp(`${escapeRegExp(version.path)}[/\\\\]tsserver.js:`, 'gi'), 'tsserver.js:'); + } +} + export class TypeScriptServerSpawner { public constructor( private readonly _versionProvider: TypeScriptVersionProvider, @@ -41,7 +111,7 @@ export class TypeScriptServerSpawner { ): TypeScriptServer { const apiVersion = version.version || API.defaultVersion; - const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(configuration, version, pluginManager); + const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(configuration, version, apiVersion, pluginManager); if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) { if (tsServerLogFile) { @@ -55,7 +125,7 @@ export class TypeScriptServerSpawner { const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions()); this._logger.info('Started TSServer'); - return new TypeScriptServer(childProcess, tsServerLogFile, cancellationPipeName, this._logger, this._telemetryReporter, this._tracer); + return new TypeScriptServer(childProcess, tsServerLogFile, cancellationPipeName, version, this._telemetryReporter, this._tracer); } private getForkOptions() { @@ -69,14 +139,13 @@ export class TypeScriptServerSpawner { private getTsServerArgs( configuration: TypeScriptServiceConfiguration, currentVersion: TypeScriptVersion, + apiVersion: API, pluginManager: PluginManager, ): { args: string[], cancellationPipeName: string | undefined, tsServerLogFile: string | undefined } { const args: string[] = []; let cancellationPipeName: string | undefined; let tsServerLogFile: string | undefined; - const apiVersion = currentVersion.version || API.defaultVersion; - if (apiVersion.gte(API.v206)) { if (apiVersion.gte(API.v250)) { args.push('--useInferredProjectPerProjectRoot'); @@ -113,8 +182,11 @@ export class TypeScriptServerSpawner { if (pluginManager.plugins.length) { args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); - if (currentVersion.path === this._versionProvider.defaultVersion.path) { - pluginPaths.push(...pluginManager.plugins.map(x => x.path)); + const isUsingBundledTypeScriptVersion = currentVersion.path === this._versionProvider.defaultVersion.path; + for (const plugin of pluginManager.plugins) { + if (isUsingBundledTypeScriptVersion || plugin.enableForWorkspaceTypeScriptVersions) { + pluginPaths.push(plugin.path); + } } } @@ -137,6 +209,10 @@ export class TypeScriptServerSpawner { args.push('--noGetErrOnBackgroundUpdate'); } + if (apiVersion.gte(API.v345)) { + args.push('--validateDefaultNpmLocation'); + } + return { args, cancellationPipeName, tsServerLogFile }; } @@ -173,12 +249,12 @@ export class TypeScriptServer extends Disposable { private readonly _childProcess: cp.ChildProcess, private readonly _tsServerLogFile: string | undefined, private readonly _cancellationPipeName: string | undefined, - private readonly _logger: Logger, + private readonly _version: TypeScriptVersion, private readonly _telemetryReporter: TelemetryReporter, private readonly _tracer: Tracer, ) { super(); - this._reader = this._register(new Reader<Proto.Response>(this._childProcess.stdout)); + this._reader = this._register(new Reader<Proto.Response>(this._childProcess.stdout!)); this._reader.onData(msg => this.dispatchMessage(msg)); this._childProcess.on('exit', code => this.handleExit(code)); this._childProcess.on('error', error => this.handleError(error)); @@ -198,7 +274,7 @@ export class TypeScriptServer extends Disposable { public get tsServerLogFile() { return this._tsServerLogFile; } public write(serverRequest: Proto.Request) { - this._childProcess.stdin.write(JSON.stringify(serverRequest) + '\r\n', 'utf8'); + this._childProcess.stdin!.write(JSON.stringify(serverRequest) + '\r\n', 'utf8'); } public dispose() { @@ -273,7 +349,7 @@ export class TypeScriptServer extends Disposable { } finally { const callback = this.fetchCallback(seq); if (callback) { - callback.onSuccess(new CancelledResponse(`Cancelled request ${seq} - ${command}`)); + callback.onSuccess(new ServerResponse.Cancelled(`Cancelled request ${seq} - ${command}`)); } } } @@ -289,13 +365,15 @@ export class TypeScriptServer extends Disposable { callback.onSuccess(response); } else if (response.message === 'No content available.') { // Special case where response itself is successful but there is not any data to return. - callback.onSuccess(new NoContentResponse()); + callback.onSuccess(ServerResponse.NoContent); } else { - callback.onError(response); + callback.onError(TypeScriptServerError.create(this._version, response)); } } - public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<any> { + public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; + public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>; + public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined { const request = this._requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request, @@ -303,70 +381,49 @@ export class TypeScriptServer extends Disposable { isAsync: executeInfo.isAsync, queueingType: getQueueingType(command, executeInfo.lowPriority) }; - let result: Promise<any>; + let result: Promise<ServerResponse.Response<Proto.Response>> | undefined; if (executeInfo.expectsResult) { - let wasCancelled = false; - result = new Promise<any>((resolve, reject) => { + result = new Promise<ServerResponse.Response<Proto.Response>>((resolve, reject) => { this._callbacks.add(request.seq, { onSuccess: resolve, onError: reject, startTime: Date.now(), isAsync: executeInfo.isAsync }, executeInfo.isAsync); if (executeInfo.token) { executeInfo.token.onCancellationRequested(() => { - wasCancelled = true; this.tryCancelRequest(request.seq, command); }); } - }).catch((err: any) => { - if (!wasCancelled) { - this._logger.error(`'${command}' request failed with error.`, err); - const properties = this.parseErrorText(err && err.message, command); - /* __GDPR__ - "languageServiceErrorResponse" : { - "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this._telemetryReporter.logTelemetry('languageServiceErrorResponse', properties); + }).catch((err: Error) => { + if (err instanceof TypeScriptServerError) { + if (!executeInfo.token || !executeInfo.token.isCancellationRequested) { + /* __GDPR__ + "languageServiceErrorResponse" : { + "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, + "stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, + "errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this._telemetryReporter.logTelemetry('languageServiceErrorResponse', { + command: err.serverCommand, + message: err.serverMessage || '', + stack: err.serverStack || '', + errortext: err.serverErrorText || '', + }); + } } + throw err; }); - } else { - result = Promise.resolve(null); } + this._requestQueue.enqueue(requestInfo); this.sendNextRequests(); return result; } - /** - * Given a `errorText` from a tsserver request indicating failure in handling a request, - * prepares a payload for telemetry-logging. - */ - private parseErrorText(errorText: string | undefined, command: string) { - const properties: ObjectMap<string> = Object.create(null); - properties['command'] = command; - if (errorText) { - properties['errorText'] = errorText; - - const errorPrefix = 'Error processing request. '; - if (errorText.startsWith(errorPrefix)) { - const prefixFreeErrorText = errorText.substr(errorPrefix.length); - const newlineIndex = prefixFreeErrorText.indexOf('\n'); - if (newlineIndex >= 0) { - // Newline expected between message and stack. - properties['message'] = prefixFreeErrorText.substring(0, newlineIndex); - properties['stack'] = prefixFreeErrorText.substring(newlineIndex + 1); - } - } - } - return properties; - } - private sendNextRequests(): void { while (this._pendingResponses.size === 0 && this._requestQueue.length > 0) { const item = this._requestQueue.dequeue(); @@ -405,7 +462,7 @@ export class TypeScriptServer extends Disposable { } } -const fenceCommands = new Set(['change', 'close', 'open']); +const fenceCommands = new Set(['change', 'close', 'open', 'updateOpen']); function getQueueingType( command: string, diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 9c76d5e50ab..b11259f22ea 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -29,6 +29,7 @@ import VersionStatus from './utils/versionStatus'; const styleCheckDiagnostics = [ 6133, // variable is declared but never used 6138, // property is declared but its value is never read + 6192, // All imports are unused 7027, // unreachable code detected 7028, // unused label 7029, // fall through case in switch @@ -55,7 +56,6 @@ export default class TypeScriptServiceClientHost extends Disposable { ) { super(); const handleProjectCreateOrDelete = () => { - this.client.executeWithoutWaitingForResponse('reloadProjects', null); this.triggerAllDiagnostics(); }; const handleProjectChange = () => { @@ -218,47 +218,17 @@ export default class TypeScriptServiceClientHost extends Disposable { return; } - (this.findLanguage(this.client.toResource(body.configFile))).then(language => { + this.findLanguage(this.client.toResource(body.configFile)).then(language => { if (!language) { return; } - if (body.diagnostics.length === 0) { - language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), []); - } else if (body.diagnostics.length >= 1) { - 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: vscode.Diagnostic; - for (let index = 0; index < document.lineCount; index++) { - const line = document.lineAt(index); - const text = line.text; - const firstNonWhitespaceCharacterIndex = line.firstNonWhitespaceCharacterIndex; - if (firstNonWhitespaceCharacterIndex < text.length) { - if (text.charAt(firstNonWhitespaceCharacterIndex) === '{') { - curly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + 1]; - break; - } else { - const matches = /\s*([^\s]*)(?:\s*|$)/.exec(text.substr(firstNonWhitespaceCharacterIndex)); - if (matches && matches.length >= 1) { - nonCurly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + matches[1].length]; - } - } - } - } - const match = curly || nonCurly; - if (match) { - diagnostic = new vscode.Diagnostic(new vscode.Range(match[0], match[1], match[0], match[2]), body.diagnostics[0].text); - } else { - 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 vscode.Diagnostic(new vscode.Range(0, 0, 0, 0), body.diagnostics[0].text)]); - }); - } + + language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), body.diagnostics.map(tsDiag => { + const range = tsDiag.start && tsDiag.end ? typeConverters.Range.fromTextSpan(tsDiag) : new vscode.Range(0, 0, 0, 1); + const diagnostic = new vscode.Diagnostic(range, body.diagnostics[0].text, this.getDiagnosticSeverity(tsDiag)); + diagnostic.source = language.diagnosticSource; + return diagnostic; + })); }); } @@ -272,8 +242,7 @@ export default class TypeScriptServiceClientHost extends Disposable { private tsDiagnosticToVsDiagnostic(diagnostic: Proto.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any } { const { start, end, text } = diagnostic; 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); + const converted = new vscode.Diagnostic(range, text, this.getDiagnosticSeverity(diagnostic)); converted.source = diagnostic.source || source; if (diagnostic.code) { converted.code = diagnostic.code; diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index ef6b7d31c32..1156b79d268 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -11,21 +11,22 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import Logger from './utils/logger'; import { PluginManager } from './utils/plugins'; -export class CancelledResponse { - public readonly type: 'cancelled' = 'cancelled'; +export namespace ServerResponse { - constructor( - public readonly reason: string - ) { } + export class Cancelled { + public readonly type = 'cancelled'; + + constructor( + public readonly reason: string + ) { } + } + + export const NoContent = new class { readonly type = 'noContent'; }; + + export type Response<T extends Proto.Response> = T | Cancelled | typeof NoContent; } -export class NoContentResponse { - public readonly type: 'noContent' = 'noContent'; -} - -export type ServerResponse<T extends Proto.Response> = T | CancelledResponse | NoContentResponse; - -interface TypeScriptRequestTypes { +export interface TypeScriptRequestTypes { 'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse]; 'completionEntryDetails': [Proto.CompletionDetailsRequestArgs, Proto.CompletionDetailsResponse]; 'completionInfo': [Proto.CompletionsRequestArgs, Proto.CompletionInfoResponse]; @@ -34,10 +35,11 @@ interface TypeScriptRequestTypes { 'definition': [Proto.FileLocationRequestArgs, Proto.DefinitionResponse]; 'definitionAndBoundSpan': [Proto.FileLocationRequestArgs, Proto.DefinitionInfoAndBoundSpanReponse]; 'docCommentTemplate': [Proto.FileLocationRequestArgs, Proto.DocCommandTemplateResponse]; + 'documentHighlights': [Proto.DocumentHighlightsRequestArgs, Proto.DocumentHighlightsResponse]; 'format': [Proto.FormatRequestArgs, Proto.FormatResponse]; 'formatonkey': [Proto.FormatOnKeyRequestArgs, Proto.FormatResponse]; 'getApplicableRefactors': [Proto.GetApplicableRefactorsRequestArgs, Proto.GetApplicableRefactorsResponse]; - 'getCodeFixes': [Proto.CodeFixRequestArgs, Proto.GetCodeFixesResponse]; + 'getCodeFixes': [Proto.CodeFixRequestArgs, Proto.CodeFixResponse]; 'getCombinedCodeFix': [Proto.GetCombinedCodeFixRequestArgs, Proto.GetCombinedCodeFixResponse]; 'getEditsForFileRename': [Proto.GetEditsForFileRenameRequestArgs, Proto.GetEditsForFileRenameResponse]; 'getEditsForRefactor': [Proto.GetEditsForRefactorRequestArgs, Proto.GetEditsForRefactorResponse]; @@ -47,12 +49,12 @@ interface TypeScriptRequestTypes { '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]; + 'selectionRange': [Proto.SelectionRangeRequestArgs, Proto.SelectionRangeResponse]; 'signatureHelp': [Proto.SignatureHelpRequestArgs, Proto.SignatureHelpResponse]; 'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse]; } @@ -63,20 +65,27 @@ export interface ITypeScriptServiceClient { * * Does not try handling case insensitivity. */ - normalizedPath(resource: vscode.Uri): string | null; + normalizedPath(resource: vscode.Uri): string | undefined; /** * Map a resource to a normalized path * * This will attempt to handle case insensitivity. */ - toPath(resource: vscode.Uri): string | null; + toPath(resource: vscode.Uri): string | undefined; /** * Convert a path to a resource. */ toResource(filepath: string): vscode.Uri; + /** + * Tries to ensure that a vscode document is open on the TS server. + * + * Returns the normalized path. + */ + toOpenedFilePath(document: vscode.TextDocument): string | undefined; + getWorkspaceRootForResource(resource: vscode.Uri): string | undefined; readonly onTsServerStarted: vscode.Event<API>; @@ -96,18 +105,19 @@ export interface ITypeScriptServiceClient { args: TypeScriptRequestTypes[K][0], token: vscode.CancellationToken, lowPriority?: boolean - ): Promise<ServerResponse<TypeScriptRequestTypes[K][1]>>; + ): Promise<ServerResponse.Response<TypeScriptRequestTypes[K][1]>>; executeWithoutWaitingForResponse(command: 'open', args: Proto.OpenRequestArgs): void; executeWithoutWaitingForResponse(command: 'close', args: Proto.FileRequestArgs): void; executeWithoutWaitingForResponse(command: 'change', args: Proto.ChangeRequestArgs): void; + executeWithoutWaitingForResponse(command: 'updateOpen', args: Proto.UpdateOpenRequestArgs): 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>; + executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>>; /** * Cancel on going geterr requests and re-queue them after `f` has been evaluated. */ - interuptGetErr<R>(f: () => R): R; + interruptGetErr<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 4e275b7dbc5..8f8f45aaa34 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -11,7 +11,7 @@ import BufferSyncSupport from './features/bufferSyncSupport'; import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; import * as Proto from './protocol'; import { TypeScriptServer, TypeScriptServerSpawner } from './tsServer/server'; -import { ITypeScriptServiceClient } from './typescriptService'; +import { ITypeScriptServiceClient, ServerResponse } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; import { Disposable } from './utils/dispose'; @@ -34,6 +34,43 @@ export interface TsDiagnostics { readonly diagnostics: Proto.Diagnostic[]; } +namespace ServerState { + export const enum Type { + None, + Running, + Errored + } + + export const None = new class { readonly type = Type.None; }; + + export class Running { + readonly type = Type.Running; + constructor( + public readonly server: TypeScriptServer, + + /** + * API version obtained from the version picker after checking the corresponding path exists. + */ + public readonly apiVersion: API, + + /** + * Version reported by currently-running tsserver. + */ + public tsserverVersion: string | undefined, + public langaugeServiceEnabled: boolean, + ) { } + } + + export class Errored { + readonly type = Type.Errored; + constructor( + public readonly error: Error, + ) { } + } + + export type State = typeof None | Running | Errored; +} + export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient { private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`; @@ -49,23 +86,13 @@ export default class TypeScriptServiceClient extends Disposable implements IType public readonly logger: Logger = new Logger(); private readonly typescriptServerSpawner: TypeScriptServerSpawner; - private forkedTsServer: TypeScriptServer | null; - private lastError: Error | null; + private serverState: ServerState.State = ServerState.None; private lastStart: number; private numberRestarts: number; private isRestarting: boolean = false; private loadingIndicator = new ServerInitializingIndicator(); public readonly telemetryReporter: TelemetryReporter; - /** - * API version obtained from the version picker after checking the corresponding path exists. - */ - private _apiVersion: API; - - /** - * Version reported by currently-running tsserver. - */ - private _tsserverVersion: string | undefined; public readonly bufferSyncSupport: BufferSyncSupport; public readonly diagnosticsManager: DiagnosticsManager; @@ -81,13 +108,12 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.pathSeparator = path.sep; this.lastStart = Date.now(); + // tslint:disable-next-line: no-var-keyword var p = new Promise<void>((resolve, reject) => { this._onReady = { promise: p, resolve, reject }; }); this._onReady!.promise = p; - this.forkedTsServer = null; - this.lastError = null; this.numberRestarts = 0; this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); @@ -95,8 +121,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.pluginPathsProvider = new TypeScriptPluginPathsProvider(this._configuration); this.versionPicker = new TypeScriptVersionPicker(this.versionProvider, this.workspaceState); - this._apiVersion = API.defaultVersion; - this._tsserverVersion = undefined; this.tracer = new Tracer(this.logger); this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds); @@ -115,7 +139,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.pluginPathsProvider.updateConfiguration(this._configuration); this.tracer.updateConfiguration(); - if (this.forkedTsServer) { + if (this.serverState.type === ServerState.Type.Running) { if (this._configuration.checkJs !== oldConfiguration.checkJs || this._configuration.experimentalDecorators !== oldConfiguration.experimentalDecorators ) { @@ -128,13 +152,24 @@ export default class TypeScriptServiceClient extends Disposable implements IType } }, this, this._disposables); - this.telemetryReporter = this._register(new TelemetryReporter(() => this._tsserverVersion || this._apiVersion.versionString)); + this.telemetryReporter = this._register(new TelemetryReporter(() => { + if (this.serverState.type === ServerState.Type.Running) { + if (this.serverState.tsserverVersion) { + return this.serverState.tsserverVersion; + } + } + return this.apiVersion.versionString; + })); this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer); this._register(this.pluginManager.onDidUpdateConfig(update => { this.configurePlugin(update.pluginId, update.config); })); + + this._register(this.pluginManager.onDidChangePlugins(() => { + this.restartTsServer(); + })); } public get configuration() { @@ -146,22 +181,21 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.bufferSyncSupport.dispose(); - if (this.forkedTsServer) { - this.forkedTsServer.kill(); + if (this.serverState.type === ServerState.Type.Running) { + this.serverState.server.kill(); } this.loadingIndicator.reset(); } public restartTsServer(): void { - if (this.forkedTsServer) { + if (this.serverState.type === ServerState.Type.Running) { this.info('Killing TS Server'); this.isRestarting = true; - this.forkedTsServer.kill(); - this.resetClientVersion(); + this.serverState.server.kill(); } - this.forkedTsServer = this.startService(true); + this.serverState = this.startService(true); } private readonly _onTsServerStarted = this._register(new vscode.EventEmitter<API>()); @@ -192,7 +226,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType public readonly onSurveyReady = this._onSurveyReady.event; public get apiVersion(): API { - return this._apiVersion; + if (this.serverState.type === ServerState.Type.Running) { + return this.serverState.apiVersion; + } + return API.defaultVersion; } public onReady(f: () => void): Promise<void> { @@ -211,30 +248,30 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.telemetryReporter.logTelemetry(eventName, properties); } - private service(): TypeScriptServer | null { - if (this.forkedTsServer) { - return this.forkedTsServer; + private service(): ServerState.Running { + if (this.serverState.type === ServerState.Type.Running) { + return this.serverState; } - if (this.lastError) { - throw this.lastError; + if (this.serverState.type === ServerState.Type.Errored) { + throw this.serverState.error; } - this.startService(); - if (this.forkedTsServer) { - return this.forkedTsServer; + const newState = this.startService(); + if (newState.type === ServerState.Type.Running) { + return newState; } throw new Error('Could not create TS service'); } public ensureServiceStarted() { - if (!this.forkedTsServer) { + if (this.serverState.type !== ServerState.Type.Running) { this.startService(); } } private token: number = 0; - private startService(resendModels: boolean = false): TypeScriptServer | null { + private startService(resendModels: boolean = false): ServerState.State { if (this.isDisposed) { - return null; + return ServerState.None; } let currentVersion = this.versionPicker.currentVersion; @@ -247,13 +284,13 @@ export default class TypeScriptServiceClient extends Disposable implements IType currentVersion = this.versionPicker.currentVersion; } - this._apiVersion = this.versionPicker.currentVersion.version || API.defaultVersion; + const apiVersion = this.versionPicker.currentVersion.version || API.defaultVersion; this.onDidChangeTypeScriptVersion(currentVersion); - this.lastError = null; let mytoken = ++this.token; const handle = this.typescriptServerSpawner.spawn(currentVersion, this.configuration, this.pluginManager); + this.serverState = new ServerState.Running(handle, apiVersion, undefined, true); this.lastStart = Date.now(); handle.onError((err: Error) => { @@ -266,7 +303,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType vscode.window.showErrorMessage(localize('serverExitedWithError', 'TypeScript language server exited with error. Error message is: {0}', err.message || err.name)); } - this.lastError = err; + this.serverState = new ServerState.Errored(err); this.error('TSServer errored with error.', err); if (handle.tsServerLogFile) { this.error(`TSServer log file: ${handle.tsServerLogFile}`); @@ -281,7 +318,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType */ this.logTelemetry('tsserver.error'); this.serviceExited(false); - this.resetClientVersion(); }); handle.onExit((code: any) => { @@ -316,16 +352,15 @@ export default class TypeScriptServiceClient extends Disposable implements IType handle.onEvent(event => this.dispatchEvent(event)); this._onReady!.resolve(); - this.forkedTsServer = handle; this._onTsServerStarted.fire(currentVersion.version); - if (this._apiVersion.gte(API.v300)) { + if (apiVersion.gte(API.v300)) { this.loadingIndicator.startedLoadingProject(undefined /* projectName */); } this.serviceStarted(resendModels); - return handle; + return this.serverState; } public onVersionStatusClicked(): Thenable<void> { @@ -371,7 +406,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType return false; } - if (!this.forkedTsServer || !this.forkedTsServer.tsServerLogFile) { + if (this.serverState.type !== ServerState.Type.Running || !this.serverState.server.tsServerLogFile) { vscode.window.showWarningMessage(localize( 'typescript.openTsServerLog.noLogFile', 'TS Server has not started logging.')); @@ -379,7 +414,15 @@ export default class TypeScriptServiceClient extends Disposable implements IType } try { - await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(this.forkedTsServer.tsServerLogFile)); + const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(this.serverState.server.tsServerLogFile)); + await vscode.window.showTextDocument(doc); + return true; + } catch { + // noop + } + + try { + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(this.serverState.server.tsServerLogFile)); return true; } catch { vscode.window.showWarningMessage(localize( @@ -392,6 +435,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType private serviceStarted(resendModels: boolean): void { const configureOptions: Proto.ConfigureRequestArguments = { hostInfo: 'vscode', + preferences: { + providePrefixAndSuffixTextForRename: true, + allowRenameOfImportPath: true, + } }; this.executeWithoutWaitingForResponse('configure', configureOptions); this.setCompilerOptionsForInferredProjects(this._configuration); @@ -436,10 +483,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType id: MessageAction; } - this.forkedTsServer = null; - if (!restart) { - this.resetClientVersion(); - } else { + this.serverState = ServerState.None; + if (restart) { const diff = Date.now() - this.lastStart; this.numberRestarts++; let startService = true; @@ -463,7 +508,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType } */ this.logTelemetry('serviceExited'); - this.resetClientVersion(); } else if (diff < 60 * 1000 /* 1 Minutes */) { this.lastStart = Date.now(); prompt = vscode.window.showWarningMessage<MyMessageItem>( @@ -488,8 +532,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType } } - public normalizedPath(resource: vscode.Uri): string | null { - if (this._apiVersion.gte(API.v213)) { + public normalizedPath(resource: vscode.Uri): string | undefined { + if (this.apiVersion.gte(API.v213)) { if (resource.scheme === fileSchemes.walkThroughSnippet || resource.scheme === fileSchemes.untitled) { const dirName = path.dirname(resource.path); const fileName = this.inMemoryResourcePrefix + path.basename(resource.path); @@ -498,28 +542,36 @@ export default class TypeScriptServiceClient extends Disposable implements IType } if (resource.scheme !== fileSchemes.file) { - return null; + return undefined; } const result = resource.fsPath; if (!result) { - return null; + return undefined; } // Both \ and / must be escaped in regular expressions return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); } - public toPath(resource: vscode.Uri): string | null { + public toPath(resource: vscode.Uri): string | undefined { return this.normalizedPath(resource); } + public toOpenedFilePath(document: vscode.TextDocument): string | undefined { + if (!this.bufferSyncSupport.handles(document.uri)) { + console.error(`Unexpected resource ${document.uri}`); + return undefined; + } + return this.toPath(document.uri) || undefined; + } + private get inMemoryResourcePrefix(): string { - return this._apiVersion.gte(API.v270) ? '^' : ''; + return this.apiVersion.gte(API.v270) ? '^' : ''; } public toResource(filepath: string): vscode.Uri { - if (this._apiVersion.gte(API.v213)) { + if (this.apiVersion.gte(API.v213)) { if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':')) ) { let resource = vscode.Uri.parse(filepath); @@ -554,7 +606,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType return undefined; } - public execute(command: string, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise<any> { + public execute(command: string, args: any, token: vscode.CancellationToken, lowPriority?: boolean): Promise<ServerResponse.Response<Proto.Response>> { return this.executeImpl(command, args, { isAsync: false, token, @@ -571,7 +623,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType }); } - public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<any> { + public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise<ServerResponse.Response<Proto.Response>> { return this.executeImpl(command, args, { isAsync: true, token, @@ -579,15 +631,15 @@ export default class TypeScriptServiceClient extends Disposable implements IType }); } - private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<any> { - const server = this.service(); - if (!server) { - return Promise.reject(new Error('Could not load TS Server')); - } - return server.executeImpl(command, args, executeInfo); + private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean }): undefined; + private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>>; + private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise<ServerResponse.Response<Proto.Response>> | undefined { + this.bufferSyncSupport.beforeCommand(command); + const runningServerState = this.service(); + return runningServerState.server.executeImpl(command, args, executeInfo); } - public interuptGetErr<R>(f: () => R): R { + public interruptGetErr<R>(f: () => R): R { return this.bufferSyncSupport.interuptGetErr(f); } @@ -596,8 +648,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType case 'syntaxDiag': case 'semanticDiag': case 'suggestionDiag': - // This event also roughly signals that the global project has been loaded successfully - this.loadingIndicator.finishedLoadingProject(undefined /* projectName */); + // This event also roughly signals that projects have been loaded successfully (since the TS server is synchronous) + this.loadingIndicator.reset(); const diagnosticEvent = event as Proto.DiagnosticEvent; if (diagnosticEvent.body && diagnosticEvent.body.diagnostics) { @@ -614,14 +666,23 @@ export default class TypeScriptServiceClient extends Disposable implements IType break; case 'telemetry': - const telemetryData = (event as Proto.TelemetryEvent).body; - this.dispatchTelemetryEvent(telemetryData); - break; - + { + const body = (event as Proto.TelemetryEvent).body; + this.dispatchTelemetryEvent(body); + break; + } case 'projectLanguageServiceState': - this._onProjectLanguageServiceStateChanged.fire((event as Proto.ProjectLanguageServiceStateEvent).body); - break; - + { + const body = (event as Proto.ProjectLanguageServiceStateEvent).body!; + if (this.serverState.type === ServerState.Type.Running) { + this.serverState = { + ...this.serverState, + langaugeServiceEnabled: body.languageServiceEnabled, + }; + } + this._onProjectLanguageServiceStateChanged.fire(body); + break; + } case 'projectsUpdatedInBackground': const body = (event as Proto.ProjectsUpdatedInBackgroundEvent).body; const resources = body.openFiles.map(vscode.Uri.file); @@ -685,7 +746,12 @@ export default class TypeScriptServiceClient extends Disposable implements IType break; } if (telemetryData.telemetryEventName === 'projectInfo') { - this._tsserverVersion = properties['version']; + if (this.serverState.type === ServerState.Type.Running) { + this.serverState = { + ...this.serverState, + tsserverVersion: properties['version'] + }; + } } /* __GDPR__ @@ -702,13 +768,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.logTelemetry(telemetryData.telemetryEventName, properties); } - private resetClientVersion() { - this._apiVersion = API.defaultVersion; - this._tsserverVersion = undefined; - } private configurePlugin(pluginName: string, configuration: {}): any { - if (this._apiVersion.gte(API.v314)) { + if (this.apiVersion.gte(API.v314)) { this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration }); } } diff --git a/extensions/typescript-language-features/src/typings/ref.d.ts b/extensions/typescript-language-features/src/typings/ref.d.ts index 954bab971e3..c9849d48e08 100644 --- a/extensions/typescript-language-features/src/typings/ref.d.ts +++ b/extensions/typescript-language-features/src/typings/ref.d.ts @@ -5,4 +5,3 @@ /// <reference path='../../../../src/vs/vscode.d.ts'/> /// <reference path='../../../../src/vs/vscode.proposed.d.ts'/> -/// <reference types='@types/node'/> diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 0c7f5b9ff69..01f3ac6e6a5 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -33,6 +33,11 @@ export default class API { public static readonly v310 = API.fromSimpleString('3.1.0'); public static readonly v314 = API.fromSimpleString('3.1.4'); public static readonly v320 = API.fromSimpleString('3.2.0'); + public static readonly v330 = API.fromSimpleString('3.3.0'); + public static readonly v333 = API.fromSimpleString('3.3.3'); + public static readonly v340 = API.fromSimpleString('3.4.0'); + public static readonly v345 = API.fromSimpleString('3.4.5'); + public static readonly v350 = API.fromSimpleString('3.5.0'); public static fromVersionString(versionString: string): API { diff --git a/extensions/typescript-language-features/src/utils/arrays.ts b/extensions/typescript-language-features/src/utils/arrays.ts index e8aa98a7582..bf403c3afeb 100644 --- a/extensions/typescript-language-features/src/utils/arrays.ts +++ b/extensions/typescript-language-features/src/utils/arrays.ts @@ -16,6 +16,6 @@ export function equals<T>(one: ReadonlyArray<T>, other: ReadonlyArray<T>, itemEq return true; } -export function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): T[] { +export function flatten<T>(arr: ReadonlyArray<T>[]): T[] { return ([] as T[]).concat.apply([], arr); } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/async.ts b/extensions/typescript-language-features/src/utils/async.ts index db4ca99ed8f..069b0bffd57 100644 --- a/extensions/typescript-language-features/src/utils/async.ts +++ b/extensions/typescript-language-features/src/utils/async.ts @@ -35,7 +35,7 @@ export class Delayer<T> { }).then(() => { this.completionPromise = null; this.onSuccess = null; - var result = this.task && this.task(); + const result = this.task && this.task(); this.task = null; return result; }); diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index adeb071062e..3babc53c91b 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; import * as arrays from './arrays'; +import * as os from 'os'; +import * as path from 'path'; export enum TsServerLogLevel { Off, @@ -83,18 +85,28 @@ export class TypeScriptServiceConfiguration { && arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths); } + private static fixPathPrefixes(inspectValue: string): string { + const pathPrefixes = ['~' + path.sep]; + for (const pathPrefix of pathPrefixes) { + if (inspectValue.startsWith(pathPrefix)) { + return path.join(os.homedir(), inspectValue.slice(pathPrefix.length)); + } + } + return inspectValue; + } + 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; + if (inspect && typeof inspect.globalValue === 'string') { + return this.fixPathPrefixes(inspect.globalValue); } return 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; + if (inspect && typeof inspect.workspaceValue === 'string') { + return this.fixPathPrefixes(inspect.workspaceValue); } return null; } diff --git a/extensions/typescript-language-features/src/utils/dispose.ts b/extensions/typescript-language-features/src/utils/dispose.ts index ba629191cf5..548094c28e5 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'; -function disposeAll(disposables: vscode.Disposable[]) { +export function disposeAll(disposables: vscode.Disposable[]) { while (disposables.length) { const item = disposables.pop(); if (item) { diff --git a/extensions/typescript-language-features/src/utils/electron.ts b/extensions/typescript-language-features/src/utils/electron.ts index 88fc9a99934..3a1ece8f726 100644 --- a/extensions/typescript-language-features/src/utils/electron.ts +++ b/extensions/typescript-language-features/src/utils/electron.ts @@ -14,16 +14,29 @@ const getRootTempDir = (() => { return () => { if (!dir) { dir = temp.getTempFile(`vscode-typescript`); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + return dir; + }; +})(); + +export const getInstanceDir = (() => { + let dir: string | undefined; + return () => { + if (!dir) { + dir = path.join(getRootTempDir(), temp.makeRandomHexString(20)); + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); } return dir; }; })(); export function getTempFile(prefix: string): string { - return path.join(getRootTempDir(), `${prefix}-${temp.makeRandomHexString(20)}.tmp`); + return path.join(getInstanceDir(), `${prefix}-${temp.makeRandomHexString(20)}.tmp`); } function generatePatchedEnv(env: any, modulePath: string): any { diff --git a/extensions/typescript-language-features/src/utils/languageDescription.ts b/extensions/typescript-language-features/src/utils/languageDescription.ts index 32cec300ebd..79f9ce9f47b 100644 --- a/extensions/typescript-language-features/src/utils/languageDescription.ts +++ b/extensions/typescript-language-features/src/utils/languageDescription.ts @@ -9,7 +9,7 @@ export const enum DiagnosticLanguage { TypeScript } -export const allDiagnosticLangauges = [DiagnosticLanguage.JavaScript, DiagnosticLanguage.TypeScript]; +export const allDiagnosticLanguages = [DiagnosticLanguage.JavaScript, DiagnosticLanguage.TypeScript]; export interface LanguageDescription { readonly id: string; @@ -17,7 +17,7 @@ export interface LanguageDescription { readonly diagnosticSource: string; readonly diagnosticLanguage: DiagnosticLanguage; readonly modeIds: string[]; - readonly configFile?: string; + readonly configFilePattern?: RegExp; readonly isExternal?: boolean; } @@ -28,13 +28,13 @@ export const standardLanguageDescriptions: LanguageDescription[] = [ diagnosticSource: 'ts', diagnosticLanguage: DiagnosticLanguage.TypeScript, modeIds: [languageModeIds.typescript, languageModeIds.typescriptreact], - configFile: 'tsconfig.json' + configFilePattern: /^tsconfig(\..*)?\.json$/gi }, { id: 'javascript', diagnosticOwner: 'typescript', diagnosticSource: 'ts', diagnosticLanguage: DiagnosticLanguage.JavaScript, modeIds: [languageModeIds.javascript, languageModeIds.javascriptreact], - configFile: 'jsconfig.json' + configFilePattern: /^jsconfig(\..*)?\.json$/gi } ]; diff --git a/extensions/typescript-language-features/src/utils/managedFileContext.ts b/extensions/typescript-language-features/src/utils/managedFileContext.ts index f88e90e19e0..81acf9ad505 100644 --- a/extensions/typescript-language-features/src/utils/managedFileContext.ts +++ b/extensions/typescript-language-features/src/utils/managedFileContext.ts @@ -5,29 +5,25 @@ import * as vscode from 'vscode'; import { isSupportedLanguageMode } from './languageModeIds'; +import { Disposable } from './dispose'; /** * When clause context set when the current file is managed by vscode's built-in typescript extension. */ -export default class ManagedFileContextManager { +export default class ManagedFileContextManager extends Disposable { private static readonly contextName = 'typescript.isManagedFile'; private isInManagedFileContext: boolean = false; - private readonly onDidChangeActiveTextEditorSub: vscode.Disposable; - public constructor( - private readonly normalizePath: (resource: vscode.Uri) => string | null + private readonly normalizePath: (resource: vscode.Uri) => string | undefined ) { - this.onDidChangeActiveTextEditorSub = vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this); + super(); + vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, this._disposables); this.onDidChangeActiveTextEditor(vscode.window.activeTextEditor); } - public dispose() { - this.onDidChangeActiveTextEditorSub.dispose(); - } - private onDidChangeActiveTextEditor(editor?: vscode.TextEditor): any { if (editor) { const isManagedFile = isSupportedLanguageMode(editor.document) && this.normalizePath(editor.document.uri) !== null; diff --git a/extensions/typescript-language-features/src/utils/plugins.ts b/extensions/typescript-language-features/src/utils/plugins.ts index 9b398d96393..71ce2430cb8 100644 --- a/extensions/typescript-language-features/src/utils/plugins.ts +++ b/extensions/typescript-language-features/src/utils/plugins.ts @@ -4,36 +4,55 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as arrays from './arrays'; import { Disposable } from './dispose'; -import { memoize } from './memoize'; export interface TypeScriptServerPlugin { readonly path: string; readonly name: string; + readonly enableForWorkspaceTypeScriptVersions: boolean; readonly languages: ReadonlyArray<string>; } +namespace TypeScriptServerPlugin { + export function equals(a: TypeScriptServerPlugin, b: TypeScriptServerPlugin): boolean { + return a.path === b.path + && a.name === b.name + && a.enableForWorkspaceTypeScriptVersions === b.enableForWorkspaceTypeScriptVersions + && arrays.equals(a.languages, b.languages); + } +} + export class PluginManager extends Disposable { private readonly _pluginConfigurations = new Map<string, {}>(); - @memoize - public get plugins(): ReadonlyArray<TypeScriptServerPlugin> { - const plugins: TypeScriptServerPlugin[] = []; - for (const extension of vscode.extensions.all) { - const pack = extension.packageJSON; - if (pack.contributes && Array.isArray(pack.contributes.typescriptServerPlugins)) { - for (const plugin of pack.contributes.typescriptServerPlugins) { - plugins.push({ - name: plugin.name, - path: extension.extensionPath, - languages: Array.isArray(plugin.languages) ? plugin.languages : [], - }); - } + private _plugins: Map<string, ReadonlyArray<TypeScriptServerPlugin>> | undefined; + + constructor() { + super(); + + vscode.extensions.onDidChange(() => { + if (!this._plugins) { + return; } - } - return plugins; + const newPlugins = this.readPlugins(); + if (!arrays.equals(arrays.flatten(Array.from(this._plugins.values())), arrays.flatten(Array.from(newPlugins.values())), TypeScriptServerPlugin.equals)) { + this._plugins = newPlugins; + this._onDidUpdatePlugins.fire(this); + } + }, undefined, this._disposables); } + public get plugins(): ReadonlyArray<TypeScriptServerPlugin> { + if (!this._plugins) { + this._plugins = this.readPlugins(); + } + return arrays.flatten(Array.from(this._plugins.values())); + } + + private readonly _onDidUpdatePlugins = this._register(new vscode.EventEmitter<this>()); + public readonly onDidChangePlugins = this._onDidUpdatePlugins.event; + private readonly _onDidUpdateConfig = this._register(new vscode.EventEmitter<{ pluginId: string, config: {} }>()); public readonly onDidUpdateConfig = this._onDidUpdateConfig.event; @@ -45,4 +64,26 @@ export class PluginManager extends Disposable { public configurations(): IterableIterator<[string, {}]> { return this._pluginConfigurations.entries(); } + + private readPlugins() { + const pluginMap = new Map<string, ReadonlyArray<TypeScriptServerPlugin>>(); + for (const extension of vscode.extensions.all) { + const pack = extension.packageJSON; + if (pack.contributes && Array.isArray(pack.contributes.typescriptServerPlugins)) { + const plugins: TypeScriptServerPlugin[] = []; + for (const plugin of pack.contributes.typescriptServerPlugins) { + plugins.push({ + name: plugin.name, + enableForWorkspaceTypeScriptVersions: !!plugin.enableForWorkspaceTypeScriptVersions, + path: extension.extensionPath, + languages: Array.isArray(plugin.languages) ? plugin.languages : [], + }); + } + if (plugins.length) { + pluginMap.set(extension.id, plugins); + } + } + } + return pluginMap; + } } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/previewer.ts b/extensions/typescript-language-features/src/utils/previewer.ts index aa698b2c163..f4a17274960 100644 --- a/extensions/typescript-language-features/src/utils/previewer.ts +++ b/extensions/typescript-language-features/src/utils/previewer.ts @@ -14,7 +14,7 @@ function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined { switch (tag.name) { case 'example': case 'default': - // Convert to markdown code block if it not already one + // Convert to markdown code block if it is not already one if (tag.text.match(/^\s*[~`]{3}/g)) { return tag.text; } diff --git a/extensions/typescript-language-features/src/utils/resourceMap.ts b/extensions/typescript-language-features/src/utils/resourceMap.ts index 33d6f00b4be..a10fbd91636 100644 --- a/extensions/typescript-language-features/src/utils/resourceMap.ts +++ b/extensions/typescript-language-features/src/utils/resourceMap.ts @@ -18,7 +18,7 @@ export class ResourceMap<T> { private readonly _map = new Map<string, { resource: vscode.Uri, value: T }>(); constructor( - private readonly _normalizePath: (resource: vscode.Uri) => string | null = (resource) => resource.fsPath + private readonly _normalizePath: (resource: vscode.Uri) => string | undefined = (resource) => resource.fsPath ) { } public get size() { @@ -71,7 +71,7 @@ export class ResourceMap<T> { return this._map.values(); } - private toKey(resource: vscode.Uri): string | null { + private toKey(resource: vscode.Uri): string | undefined { const key = this._normalizePath(resource); if (!key) { return key; diff --git a/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts b/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts new file mode 100644 index 00000000000..fbf2868d101 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as Proto from '../protocol'; +import * as PConst from '../protocol.const'; + +export function snippetForFunctionCall( + item: { insertText?: string | vscode.SnippetString; label: string; }, + displayParts: ReadonlyArray<Proto.SymbolDisplayPart> +): { snippet: vscode.SnippetString, parameterCount: number } { + if (item.insertText && typeof item.insertText !== 'string') { + return { snippet: item.insertText, parameterCount: 0 }; + } + + const parameterListParts = getParameterListParts(displayParts); + const snippet = new vscode.SnippetString(); + snippet.appendText(`${item.insertText || item.label}(`); + appendJoinedPlaceholders(snippet, parameterListParts.parts, ', '); + if (parameterListParts.hasOptionalParameters) { + snippet.appendTabstop(); + } + snippet.appendText(')'); + snippet.appendTabstop(0); + return { snippet, parameterCount: parameterListParts.parts.length + (parameterListParts.hasOptionalParameters ? 1 : 0) }; +} + +function appendJoinedPlaceholders( + snippet: vscode.SnippetString, + parts: ReadonlyArray<Proto.SymbolDisplayPart>, + joiner: string +) { + for (let i = 0; i < parts.length; ++i) { + const paramterPart = parts[i]; + snippet.appendPlaceholder(paramterPart.text); + if (i !== parts.length - 1) { + snippet.appendText(joiner); + } + } +} + +interface ParamterListParts { + readonly parts: ReadonlyArray<Proto.SymbolDisplayPart>; + readonly hasOptionalParameters: boolean; +} + +function getParameterListParts( + displayParts: ReadonlyArray<Proto.SymbolDisplayPart> +): ParamterListParts { + const parts: Proto.SymbolDisplayPart[] = []; + let isInMethod = false; + let hasOptionalParameters = false; + let parenCount = 0; + let braceCount = 0; + + outer: for (let i = 0; i < displayParts.length; ++i) { + const part = displayParts[i]; + switch (part.kind) { + case PConst.DisplayPartKind.methodName: + case PConst.DisplayPartKind.functionName: + case PConst.DisplayPartKind.text: + case PConst.DisplayPartKind.propertyName: + if (parenCount === 0 && braceCount === 0) { + isInMethod = true; + } + break; + + case PConst.DisplayPartKind.parameterName: + if (parenCount === 1 && braceCount === 0 && isInMethod) { + // Only take top level paren names + const next = displayParts[i + 1]; + // Skip optional parameters + const nameIsFollowedByOptionalIndicator = next && next.text === '?'; + if (!nameIsFollowedByOptionalIndicator) { + parts.push(part); + } + hasOptionalParameters = hasOptionalParameters || nameIsFollowedByOptionalIndicator; + } + break; + + case PConst.DisplayPartKind.punctuation: + if (part.text === '(') { + ++parenCount; + } else if (part.text === ')') { + --parenCount; + if (parenCount <= 0 && isInMethod) { + break outer; + } + } else if (part.text === '...' && parenCount === 1) { + // Found rest parmeter. Do not fill in any further arguments + hasOptionalParameters = true; + break outer; + } else if (part.text === '{') { + ++braceCount; + } else if (part.text === '}') { + --braceCount; + } + break; + } + } + + return { hasOptionalParameters, parts }; +} diff --git a/extensions/typescript-language-features/src/utils/surveyor.ts b/extensions/typescript-language-features/src/utils/surveyor.ts index bf3ba4ad601..2af183be12e 100644 --- a/extensions/typescript-language-features/src/utils/surveyor.ts +++ b/extensions/typescript-language-features/src/utils/surveyor.ts @@ -103,7 +103,7 @@ class Survey { } private get triggerCount(): number { - const count = this.memento.get(this.triggerCountMementoKey); + const count = this.memento.get<number>(this.triggerCountMementoKey); return !count || isNaN(+count) ? 0 : +count; } diff --git a/extensions/typescript-language-features/src/utils/temp.ts b/extensions/typescript-language-features/src/utils/temp.ts index 79c5cf751d0..2af5f1732b0 100644 --- a/extensions/typescript-language-features/src/utils/temp.ts +++ b/extensions/typescript-language-features/src/utils/temp.ts @@ -7,7 +7,7 @@ import path = require('path'); import os = require('os'); export function makeRandomHexString(length: number): string { - let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; + const 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++) { const idx = Math.floor(chars.length * Math.random()); diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts index 36a20544fee..eaa6a96c860 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -38,6 +38,11 @@ export namespace Position { export const fromLocation = (tslocation: Proto.Location): vscode.Position => new vscode.Position(tslocation.line - 1, tslocation.offset - 1); + export const toLocation = (vsPosition: vscode.Position): Proto.Location => ({ + line: vsPosition.line + 1, + offset: vsPosition.character + 1, + }); + export const toFileLocationRequestArgs = (file: string, position: vscode.Position): Proto.FileLocationRequestArgs => ({ file, line: position.line + 1, @@ -63,16 +68,17 @@ export namespace WorkspaceEdit { edits: Iterable<Proto.FileCodeEdits> ): vscode.WorkspaceEdit { return withFileCodeEdits(new vscode.WorkspaceEdit(), client, edits); - } + export function withFileCodeEdits( workspaceEdit: vscode.WorkspaceEdit, client: ITypeScriptServiceClient, edits: Iterable<Proto.FileCodeEdits> ): vscode.WorkspaceEdit { for (const edit of edits) { + const resource = client.toResource(edit.fileName); for (const textChange of edit.textChanges) { - workspaceEdit.replace(client.toResource(edit.fileName), + workspaceEdit.replace(resource, Range.fromTextSpan(textChange), textChange.newText); } diff --git a/extensions/typescript-language-features/src/utils/typingsStatus.ts b/extensions/typescript-language-features/src/utils/typingsStatus.ts index 78f303f0f39..066fda960df 100644 --- a/extensions/typescript-language-features/src/utils/typingsStatus.ts +++ b/extensions/typescript-language-features/src/utils/typingsStatus.ts @@ -6,18 +6,19 @@ import * as vscode from 'vscode'; import { loadMessageBundle } from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; +import { Disposable } from './dispose'; const localize = loadMessageBundle(); const typingsInstallTimeout = 30 * 1000; -export default class TypingsStatus extends vscode.Disposable { +export default class TypingsStatus extends Disposable { private _acquiringTypings: { [eventId: string]: NodeJS.Timer } = Object.create({}); private _client: ITypeScriptServiceClient; private _subscriptions: vscode.Disposable[] = []; constructor(client: ITypeScriptServiceClient) { - super(() => this.dispose()); + super(); this._client = client; this._subscriptions.push( @@ -28,6 +29,7 @@ export default class TypingsStatus extends vscode.Disposable { } public dispose(): void { + super.dispose(); this._subscriptions.forEach(x => x.dispose()); for (const eventId of Object.keys(this._acquiringTypings)) { diff --git a/extensions/typescript-language-features/src/utils/versionStatus.ts b/extensions/typescript-language-features/src/utils/versionStatus.ts index 2b6901ee3d1..0b3e3412617 100644 --- a/extensions/typescript-language-features/src/utils/versionStatus.ts +++ b/extensions/typescript-language-features/src/utils/versionStatus.ts @@ -6,21 +6,17 @@ import * as vscode from 'vscode'; import * as languageModeIds from './languageModeIds'; import { TypeScriptVersion } from './versionProvider'; +import { Disposable } from './dispose'; -export default class VersionStatus { - private readonly _onChangeEditorSub: vscode.Disposable; +export default class VersionStatus extends Disposable { private readonly _versionBarEntry: vscode.StatusBarItem; constructor( - private readonly _normalizePath: (resource: vscode.Uri) => string | null + private readonly _normalizePath: (resource: vscode.Uri) => string | undefined ) { - this._versionBarEntry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99 /* to the right of editor status (100) */); - this._onChangeEditorSub = vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this); - } - - public dispose() { - this._versionBarEntry.dispose(); - this._onChangeEditorSub.dispose(); + super(); + this._versionBarEntry = this._register(vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 99 /* to the right of editor status (100) */)); + vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this, this._disposables); } public onDidChangeTypeScriptVersion(version: TypeScriptVersion) { diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index ff4f999787c..968cb9074cd 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -2,10 +2,37 @@ # yarn lockfile v1 -"@types/node@8.0.33": - version "8.0.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" - integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/glob@*": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/node@*", "@types/node@^12.0.2": + version "12.0.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40" + integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA== + +"@types/rimraf@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e" + integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ== + dependencies: + "@types/glob" "*" + "@types/node" "*" "@types/semver@^5.5.0": version "5.5.0" @@ -58,10 +85,10 @@ ansi-wrap@0.1.0: resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -630,6 +657,18 @@ glob@^5.0.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glogg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" @@ -1490,6 +1529,13 @@ rimraf@2: dependencies: glob "^7.0.5" +rimraf@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + 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" @@ -1810,12 +1856,12 @@ vinyl@^2.0.1, vinyl@^2.0.2: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/vb/cgmanifest.json b/extensions/vb/cgmanifest.json index 4cb7d33e04b..23523f36abb 100644 --- a/extensions/vb/cgmanifest.json +++ b/extensions/vb/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 4ee121e1b1b..e1716b8555a 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@types/mocha": "2.2.43", - "@types/node": "^8.10.25", + "@types/node": "^10.12.21", "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/commands.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts index 2629f516aac..a3d5690b9b9 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts @@ -74,23 +74,6 @@ suite('commands namespace tests', () => { }); - test('api-command: vscode.previewHtml', async function () { - - let registration = workspace.registerTextDocumentContentProvider('speciale', { - provideTextDocumentContent(uri) { - return `content of URI <b>${uri.toString()}</b>`; - } - }); - - let virtualDocumentUri = Uri.parse('speciale://authority/path'); - let title = 'A title'; - - const success = await commands.executeCommand('vscode.previewHtml', virtualDocumentUri, ViewColumn.Three, title); - assert.ok(success); - registration.dispose(); - - }); - test('api-command: vscode.diff', function () { let registration = workspace.registerTextDocumentContentProvider('sc', { @@ -101,17 +84,17 @@ suite('commands namespace tests', () => { let a = commands.executeCommand('vscode.diff', Uri.parse('sc:a'), Uri.parse('sc:b'), 'DIFF').then(value => { - assert.ok(value === void 0); + assert.ok(value === undefined); registration.dispose(); }); let b = commands.executeCommand('vscode.diff', Uri.parse('sc:a'), Uri.parse('sc:b')).then(value => { - assert.ok(value === void 0); + assert.ok(value === undefined); registration.dispose(); }); let c = commands.executeCommand('vscode.diff', Uri.parse('sc:a'), Uri.parse('sc:b'), 'Title', { selection: new Range(new Position(1, 1), new Position(1, 2)) }).then(value => { - assert.ok(value === void 0); + assert.ok(value === undefined); registration.dispose(); }); @@ -122,7 +105,7 @@ suite('commands namespace tests', () => { }); test('api-command: vscode.open', function () { - let uri = Uri.file(join(workspace.rootPath || '', './image.png')); + let uri = Uri.parse(workspace.workspaceFolders![0].uri.toString() + '/image.png'); let a = commands.executeCommand('vscode.open', uri).then(() => assert.ok(true), () => assert.ok(false)); let b = commands.executeCommand('vscode.open', uri, ViewColumn.Two).then(() => assert.ok(true), () => assert.ok(false)); let c = commands.executeCommand('vscode.open').then(() => assert.ok(false), () => assert.ok(true)); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts index d3c5c788d08..31d6d4ce312 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/env.test.ts @@ -17,11 +17,11 @@ suite('env-namespace', () => { }); test('env is readonly', function () { - assert.throws(() => env.language = '234'); - assert.throws(() => env.appRoot = '234'); - assert.throws(() => env.appName = '234'); - assert.throws(() => env.machineId = '234'); - assert.throws(() => env.sessionId = '234'); + assert.throws(() => (env as any).language = '234'); + assert.throws(() => (env as any).appRoot = '234'); + assert.throws(() => (env as any).appName = '234'); + assert.throws(() => (env as any).machineId = '234'); + assert.throws(() => (env as any).sessionId = '234'); }); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts index 9983c542be3..81c9c2eeadf 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/languages.test.ts @@ -10,6 +10,20 @@ import { createRandomFile } from '../utils'; suite('languages namespace tests', () => { + const isWindows = process.platform === 'win32'; + + function positionToString(p: vscode.Position) { + return `[${p.character}/${p.line}]`; + } + + function rangeToString(r: vscode.Range) { + return `[${positionToString(r.start)}/${positionToString(r.end)}]`; + } + + function assertEqualRange(actual: vscode.Range, expected: vscode.Range, message?: string) { + assert.equal(rangeToString(actual), rangeToString(expected), message); + } + test('setTextDocumentLanguage -> close/open event', async function () { const file = await createRandomFile('foo\nbar\nbar'); const doc = await vscode.workspace.openTextDocument(file); @@ -77,6 +91,31 @@ suite('languages namespace tests', () => { assert.ok(found); }); + test('link detector', async function () { + const uri = await createRandomFile('class A { // http://a.com }', undefined, '.java'); + const doc = await vscode.workspace.openTextDocument(uri); + + const target = vscode.Uri.file(isWindows ? 'c:\\foo\\bar' : '/foo/bar'); + const range = new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 5)); + + const linkProvider: vscode.DocumentLinkProvider = { + provideDocumentLinks: _doc => { + return [new vscode.DocumentLink(range, target)]; + } + }; + vscode.languages.registerDocumentLinkProvider({ language: 'java', scheme: 'file' }, linkProvider); + + const links = await vscode.commands.executeCommand<vscode.DocumentLink[]>('vscode.executeLinkProvider', doc.uri); + assert.equal(2, links && links.length); + let [link1, link2] = links!.sort((l1, l2) => l1.range.start.compareTo(l2.range.start)); + + assert.equal(target.toString(), link1.target && link1.target.toString()); + assertEqualRange(range, link1.range); + + assert.equal('http://a.com/', link2.target && link2.target.toString()); + assertEqualRange(new vscode.Range(new vscode.Position(0, 13), new vscode.Position(0, 25)), link2.range); + }); + test('diagnostics & CodeActionProvider', async function () { class D2 extends vscode.Diagnostic { @@ -141,7 +180,8 @@ suite('languages namespace tests', () => { await vscode.workspace.openTextDocument(uri); const result = await vscode.commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', uri, new vscode.Position(1, 0)); r1.dispose(); - assert.ok(ran); - assert.equal(result!.items[0].label, 'foo'); + assert.ok(ran, 'Provider has not been invoked'); + assert.ok(result!.items.some(i => i.label === 'foo'), 'Results do not include "foo"'); }); + }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts new file mode 100644 index 00000000000..b2ad43d30b0 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/types.test.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'mocha'; +import * as assert from 'assert'; +import * as vscode from 'vscode'; + + +suite('types', () => { + + test('static properties, es5 compat class', function () { + + + assert.ok(vscode.ThemeIcon.File instanceof vscode.ThemeIcon); + assert.ok(vscode.ThemeIcon.Folder instanceof vscode.ThemeIcon); + assert.ok(vscode.CodeActionKind.Empty instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.QuickFix instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.Refactor instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.RefactorExtract instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.RefactorInline instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.RefactorRewrite instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.Source instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.SourceOrganizeImports instanceof vscode.CodeActionKind); + assert.ok(vscode.CodeActionKind.SourceFixAll instanceof vscode.CodeActionKind); + // assert.ok(vscode.QuickInputButtons.Back instanceof vscode.QuickInputButtons); never was an instance + + }); +}); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index d211db9565b..5e28b247b07 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -7,7 +7,7 @@ import 'mocha'; import * as assert from 'assert'; import * as vscode from 'vscode'; import { join } from 'path'; -import { closeAllEditors, disposeAll } from '../utils'; +import { closeAllEditors, disposeAll, conditionalTest } from '../utils'; const webviewId = 'myWebview'; @@ -82,7 +82,7 @@ suite('Webview tests', () => { } }); - test('webviews should preserve vscode API state when they are hidden', async () => { + conditionalTest('webviews should preserve vscode API state when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -125,7 +125,7 @@ suite('Webview tests', () => { assert.strictEqual(secondResponse.value, 1); }); - test('webviews should preserve their context when they are moved between view columns', async () => { + conditionalTest('webviews should preserve their context when they are moved between view columns', async () => { const doc = await vscode.workspace.openTextDocument(testDocument); await vscode.window.showTextDocument(doc, vscode.ViewColumn.One); @@ -163,7 +163,7 @@ suite('Webview tests', () => { assert.strictEqual(secondResponse.value, 1); }); - test('webviews with retainContextWhenHidden should preserve their context when they are hidden', async () => { + conditionalTest('webviews with retainContextWhenHidden should preserve their context when they are hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); @@ -203,7 +203,7 @@ suite('Webview tests', () => { assert.strictEqual(secondResponse.value, 1); }); - test('webviews with retainContextWhenHidden should preserve their page position when hidden', async () => { + conditionalTest('webviews with retainContextWhenHidden should preserve their page position when hidden', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true })); const ready = getMesssage(webview); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -244,7 +244,7 @@ suite('Webview tests', () => { assert.strictEqual(secondResponse.value, 100); }); - test('webviews should only be able to load resources from workspace by default', async () => { + conditionalTest('webviews should only be able to load resources from workspace by default', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true })); webview.webview.html = createHtmlDocumentWithBody(/*html*/` @@ -278,7 +278,7 @@ suite('Webview tests', () => { } }); - test('webviews should allow overriding allowed resource paths using localResourceRoots', async () => { + conditionalTest('webviews should allow overriding allowed resource paths using localResourceRoots', async () => { const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, localResourceRoots: [vscode.Uri.file(join(vscode.workspace.rootPath!, 'sub'))] @@ -307,6 +307,38 @@ suite('Webview tests', () => { assert.strictEqual((await response).value, false); } }); + + test('webviews should have real view column after they are created, #56097', async () => { + const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.Active }, { enableScripts: true })); + + // Since we used a symbolic column, we don't know what view column the webview will actually show in at first + assert.strictEqual(webview.viewColumn, undefined); + + let changed = false; + const viewStateChanged = new Promise<vscode.WebviewPanelOnDidChangeViewStateEvent>((resolve) => { + webview.onDidChangeViewState(e => { + if (changed) { + throw new Error('Only expected a single view state change'); + } + changed = true; + resolve(e); + }, undefined, disposables); + }); + + assert.strictEqual((await viewStateChanged).webviewPanel.viewColumn, vscode.ViewColumn.One); + + const firstResponse = getMesssage(webview); + webview.webview.html = createHtmlDocumentWithBody(/*html*/` + <script> + const vscode = acquireVsCodeApi(); + vscode.postMessage({ }); + </script>`); + + webview.webview.postMessage({ value: 1 }); + await firstResponse; + assert.strictEqual(webview.viewColumn, vscode.ViewColumn.One); + + }); }); function createHtmlDocumentWithBody(body: string): string { 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 1ed84272d39..f6844162017 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, Terminal } from 'vscode'; +import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, Terminal, TerminalDimensionsChangeEvent } from 'vscode'; import { join } from 'path'; import { closeAllEditors, pathEquals, createRandomFile } from '../utils'; @@ -136,8 +136,7 @@ suite('window namespace tests', () => { }).then(() => { assert.equal(actualEvents.length, 2); - for (let i = 0; i < actualEvents.length; i++) { - const event = actualEvents[i]; + for (const event of actualEvents) { assert.equal(event.viewColumn, event.textEditor.viewColumn); } @@ -381,13 +380,13 @@ suite('window namespace tests', () => { assert.equal(await two, 'notempty'); }); - - test('showQuickPick, accept first', async function () { - const pick = window.showQuickPick(['eins', 'zwei', 'drei']); - await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - assert.equal(await pick, 'eins'); - }); + // TODO@chrmarti Disabled due to flaky behaviour (https://github.com/Microsoft/vscode/issues/70887) + // test('showQuickPick, accept first', async function () { + // const pick = window.showQuickPick(['eins', 'zwei', 'drei']); + // await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. + // await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + // assert.equal(await pick, 'eins'); + // }); test('showQuickPick, accept second', async function () { const resolves: ((value: string) => void)[] = []; @@ -436,18 +435,19 @@ suite('window namespace tests', () => { return unexpected; }); - test('showQuickPick, keep selection (Microsoft/vscode-azure-account#67)', async function () { - const picks = window.showQuickPick([ - { label: 'eins' }, - { label: 'zwei', picked: true }, - { label: 'drei', picked: true } - ], { - canPickMany: true - }); - await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - assert.deepStrictEqual((await picks)!.map(pick => pick.label), ['zwei', 'drei']); - }); + // TODO@chrmarti Disabled due to flaky behaviour (https://github.com/Microsoft/vscode/issues/70887) + // test('showQuickPick, keep selection (Microsoft/vscode-azure-account#67)', async function () { + // const picks = window.showQuickPick([ + // { label: 'eins' }, + // { label: 'zwei', picked: true }, + // { label: 'drei', picked: true } + // ], { + // canPickMany: true + // }); + // await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. + // await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + // assert.deepStrictEqual((await picks)!.map(pick => pick.label), ['zwei', 'drei']); + // }); test('showQuickPick, undefined on cancel', function () { const source = new CancellationTokenSource(); @@ -518,19 +518,20 @@ suite('window namespace tests', () => { return Promise.all([a, b]); }); - test('showWorkspaceFolderPick', async function () { - const p = window.showWorkspaceFolderPick(undefined); + // TODO@chrmarti Disabled due to flaky behaviour (https://github.com/Microsoft/vscode/issues/70887) + // test('showWorkspaceFolderPick', async function () { + // const p = window.showWorkspaceFolderPick(undefined); - await timeout(10); - await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); - try { - await p; - assert.ok(true); - } - catch (_error) { - assert.ok(false); - } - }); + // await timeout(10); + // await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + // try { + // await p; + // assert.ok(true); + // } + // catch (_error) { + // assert.ok(false); + // } + // }); test('Default value for showInput Box not accepted when it fails validateInput, reversing #33691', async function () { const result = window.showInputBox({ @@ -696,6 +697,46 @@ suite('window namespace tests', () => { const terminal = window.createTerminal(); terminal.show(); }); + + test('onDidChangeTerminalDimensions should fire when new terminals are created', (done) => { + const reg1 = window.onDidChangeTerminalDimensions(async (event: TerminalDimensionsChangeEvent) => { + assert.equal(event.terminal, terminal1); + assert.equal(typeof event.dimensions.columns, 'number'); + assert.equal(typeof event.dimensions.rows, 'number'); + assert.ok(event.dimensions.columns > 0); + assert.ok(event.dimensions.rows > 0); + reg1.dispose(); + let terminal2: Terminal; + const reg2 = window.onDidOpenTerminal((newTerminal) => { + // This is guarantees to fire before dimensions change event + if (newTerminal !== terminal1) { + terminal2 = newTerminal; + reg2.dispose(); + } + }); + let firstCalled = false; + let secondCalled = false; + const reg3 = window.onDidChangeTerminalDimensions((event: TerminalDimensionsChangeEvent) => { + if (event.terminal === terminal1) { + // The original terminal should fire dimension change after a split + firstCalled = true; + } else if (event.terminal !== terminal1) { + // The new split terminal should fire dimension change + secondCalled = true; + } + if (firstCalled && secondCalled) { + terminal1.dispose(); + terminal2.dispose(); + reg3.dispose(); + done(); + } + }); + await timeout(500); + commands.executeCommand('workbench.action.terminal.split'); + }); + const terminal1 = window.createTerminal({ name: 'test' }); + terminal1.show(); + }); }); }); 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 8921b4aee46..dc700abdf1b 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -36,10 +36,12 @@ suite('workspace-namespace', () => { }); test('rootPath', () => { - if (vscode.workspace.rootPath) { - assert.ok(pathEquals(vscode.workspace.rootPath, join(__dirname, '../../testWorkspace'))); - } - assert.throws(() => vscode.workspace.rootPath = 'farboo'); + assert.ok(pathEquals(vscode.workspace.rootPath!, join(__dirname, '../../testWorkspace'))); + assert.throws(() => (vscode.workspace as any).rootPath = 'farboo'); + }); + + test('workspaceFile', () => { + assert.ok(!vscode.workspace.workspaceFile); }); test('workspaceFolders', () => { @@ -75,7 +77,7 @@ suite('workspace-namespace', () => { }); test('openTextDocument, untitled is dirty', function () { - return vscode.workspace.openTextDocument(vscode.Uri.parse('untitled:' + join(vscode.workspace.rootPath || '', './newfile.txt'))).then(doc => { + return vscode.workspace.openTextDocument(vscode.Uri.parse('untitled:' + join(vscode.workspace.workspaceFolders![0].uri.toString() || '', './newfile.txt'))).then(doc => { assert.equal(doc.uri.scheme, 'untitled'); assert.ok(doc.isDirty); }); @@ -285,6 +287,30 @@ suite('workspace-namespace', () => { }); }); + test('events: onDidSaveTextDocument fires even for non dirty file when saved', () => { + return createRandomFile().then(file => { + let disposables: vscode.Disposable[] = []; + + let onDidSaveTextDocument = false; + disposables.push(vscode.workspace.onDidSaveTextDocument(e => { + assert.ok(pathEquals(e.uri.fsPath, file.fsPath)); + onDidSaveTextDocument = true; + })); + + return vscode.workspace.openTextDocument(file).then(doc => { + return vscode.window.showTextDocument(doc).then(() => { + return vscode.commands.executeCommand('workbench.action.files.save').then(() => { + assert.ok(onDidSaveTextDocument); + + disposeAll(disposables); + + return deleteFile(file); + }); + }); + }); + }); + }); + test('openTextDocument, with selection', function () { return createRandomFile('foo\nbar\nbar').then(file => { return vscode.workspace.openTextDocument(file).then(doc => { @@ -488,9 +514,23 @@ suite('workspace-namespace', () => { }); test('findFiles', () => { - return vscode.workspace.findFiles('*.js').then((res) => { + return vscode.workspace.findFiles('**/*.png').then((res) => { + assert.equal(res.length, 2); + assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); + }); + }); + + test('findFiles - exclude', () => { + return vscode.workspace.findFiles('**/*.png').then((res) => { + assert.equal(res.length, 2); + assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); + }); + }); + + test('findFiles, exclude', () => { + return vscode.workspace.findFiles('**/*.png', '**/sub/**').then((res) => { assert.equal(res.length, 1); - assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'far.js'); + assert.equal(basename(vscode.workspace.asRelativePath(res[0])), 'image.png'); }); }); @@ -568,7 +608,7 @@ suite('workspace-namespace', () => { test('applyEdit should fail when editing renamed from resource', async () => { const resource = await createRandomFile(); - const newResource = vscode.Uri.parse(resource.fsPath + '.1'); + const newResource = vscode.Uri.file(resource.fsPath + '.1'); const edit = new vscode.WorkspaceEdit(); edit.renameFile(resource, newResource); edit.insert(resource, new vscode.Position(0, 0), ''); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index 79db77f0d0c..d9fda3bb76d 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -12,9 +12,9 @@ export function rndName() { return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); } -export function createRandomFile(contents = '', dir: string = os.tmpdir()): Thenable<vscode.Uri> { +export function createRandomFile(contents = '', dir: string = os.tmpdir(), ext = ''): Thenable<vscode.Uri> { return new Promise((resolve, reject) => { - const tmpFile = join(dir, rndName()); + const tmpFile = join(dir, rndName() + ext); fs.writeFile(tmpFile, contents, (error) => { if (error) { return reject(error); @@ -58,4 +58,19 @@ export function disposeAll(disposables: vscode.Disposable[]) { item.dispose(); } } +} + +export function conditionalTest(name: string, testCallback: (done: MochaDone) => void | Thenable<any>) { + if (isTestTypeActive()) { + const async = !!testCallback.length; + if (async) { + test(name, (done) => testCallback(done)); + } else { + test(name, () => (<() => void | Thenable<any>>testCallback)()); + } + } +} + +function isTestTypeActive(): boolean { + return !!vscode.extensions.getExtension('vscode-resolver-test'); } \ No newline at end of file diff --git a/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts b/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts index f28967468a5..f018f581c42 100644 --- a/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/workspace-tests/workspace.test.ts @@ -13,18 +13,18 @@ suite('workspace-namespace', () => { teardown(closeAllEditors); test('rootPath', () => { - if (vscode.workspace.rootPath) { - assert.ok(pathEquals(vscode.workspace.rootPath, join(__dirname, '../../testWorkspace'))); - } + assert.ok(pathEquals(vscode.workspace.rootPath!, join(__dirname, '../../testWorkspace'))); + }); + + test('workspaceFile', () => { + assert.ok(pathEquals(vscode.workspace.workspaceFile!.fsPath, join(__dirname, '../../testworkspace.code-workspace'))); }); test('workspaceFolders', () => { - if (vscode.workspace.workspaceFolders) { - assert.equal(vscode.workspace.workspaceFolders.length, 2); - assert.ok(pathEquals(vscode.workspace.workspaceFolders[0].uri.fsPath, join(__dirname, '../../testWorkspace'))); - assert.ok(pathEquals(vscode.workspace.workspaceFolders[1].uri.fsPath, join(__dirname, '../../testWorkspace2'))); - assert.ok(pathEquals(vscode.workspace.workspaceFolders[1].name, 'Test Workspace 2')); - } + assert.equal(vscode.workspace.workspaceFolders!.length, 2); + assert.ok(pathEquals(vscode.workspace.workspaceFolders![0].uri.fsPath, join(__dirname, '../../testWorkspace'))); + assert.ok(pathEquals(vscode.workspace.workspaceFolders![1].uri.fsPath, join(__dirname, '../../testWorkspace2'))); + assert.ok(pathEquals(vscode.workspace.workspaceFolders![1].name, 'Test Workspace 2')); }); test('getWorkspaceFolder', () => { diff --git a/extensions/vscode-api-tests/yarn.lock b/extensions/vscode-api-tests/yarn.lock index 74a177b2aea..c7841be1397 100644 --- a/extensions/vscode-api-tests/yarn.lock +++ b/extensions/vscode-api-tests/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27" integrity sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw== -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== 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 9b43cfa2a6c..517429bb6d7 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -8,11 +8,10 @@ "vscode": "*" }, "scripts": { - "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json", - "postinstall": "node ./node_modules/vscode/bin/install" + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json" }, "devDependencies": { - "@types/node": "^8.10.25", + "@types/node": "^10.12.21", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "vscode": "1.1.5" diff --git a/extensions/vscode-colorize-tests/src/typings/ref.d.ts b/extensions/vscode-colorize-tests/src/typings/ref.d.ts index 8ea9f802a47..a45a0c6353f 100644 --- a/extensions/vscode-colorize-tests/src/typings/ref.d.ts +++ b/extensions/vscode-colorize-tests/src/typings/ref.d.ts @@ -4,3 +4,4 @@ *--------------------------------------------------------------------------------------------*/ /// <reference types='@types/node'/> +/// <reference path='../../../../src/vs/vscode.d.ts'/> diff --git a/extensions/vscode-colorize-tests/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index b17b8b1954b..46684d06b5e 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^8.10.25": - version "8.10.25" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" - integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== +"@types/node@^10.12.21": + version "10.12.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" + integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== ajv@^5.1.0: version "5.3.0" diff --git a/extensions/xml/cgmanifest.json b/extensions/xml/cgmanifest.json index b209abe6e8c..a291bba7156 100644 --- a/extensions/xml/cgmanifest.json +++ b/extensions/xml/cgmanifest.json @@ -11,8 +11,8 @@ }, "license": "MIT", "description": "The files syntaxes/xml.json and syntaxes/xsl.json were derived from the Atom package https://github.com/atom/language-xml which were originally converted from the TextMate bundle https://github.com/textmate/xml.tmbundle.", - "version": "0.0.0" + "version": "0.35.2" } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/xml/package.json b/extensions/xml/package.json index f04aed5a2d3..985266edf90 100644 --- a/extensions/xml/package.json +++ b/extensions/xml/package.json @@ -15,7 +15,6 @@ ".atom", ".axml", ".bpmn", - ".config", ".cpt", ".csl", ".csproj", @@ -36,7 +35,7 @@ ".opml", ".owl", ".proj", - ".props", + ".props", ".pt", ".publishsettings", ".pubxml", diff --git a/extensions/yaml/cgmanifest.json b/extensions/yaml/cgmanifest.json index 80ba8ae522c..e6c3ca158b5 100644 --- a/extensions/yaml/cgmanifest.json +++ b/extensions/yaml/cgmanifest.json @@ -34,4 +34,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 7dfb337bfd6..38c68f6e8f7 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.2.0-rc: - version "3.2.0-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.0-rc.tgz#7c3816f1c761b096f4f1712382e872f4da8f263e" - integrity sha512-RgKDOpEdbU9dAkB4TzxWy46wiyNUKQo0NM0bB7WfvEFw50yu046ldQXpOUYMUTLIAHnToOCtHsu39MYE8o6NCw== +typescript@3.5.0-dev.20190517: + version "3.5.0-dev.20190517" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.0-dev.20190517.tgz#5a85f1091cf33fde39b04f898c5730e30edd3e39" + integrity sha512-KoBHq6ytEApXKTDtmTu4Sp/tC5SPe4FpvwutLEANhwdMPblqZdh7APuH7I/ceMlgfHSa7B00JgF7NokUJQi0/g== diff --git a/gulpfile.js b/gulpfile.js index fe899377d79..1d13cff608c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,31 +10,27 @@ require('events').EventEmitter.defaultMaxListeners = 100; const gulp = require('gulp'); const util = require('./build/lib/util'); +const task = require('./build/lib/task'); const path = require('path'); const compilation = require('./build/lib/compilation'); +const { monacoTypecheckTask/* , monacoTypecheckWatchTask */ } = require('./build/gulpfile.editor'); +const { compileExtensionsTask, watchExtensionsTask } = require('./build/gulpfile.extensions'); // Fast compile for development time -gulp.task('clean-client', util.rimraf('out')); -gulp.task('compile-client', ['clean-client'], compilation.compileTask('src', 'out', false)); -gulp.task('watch-client', ['clean-client'], compilation.watchTask('out', false)); +const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), compilation.compileTask('src', 'out', false))); +gulp.task(compileClientTask); -// Full compile, including nls and inline sources in sourcemaps, for build -gulp.task('clean-client-build', util.rimraf('out-build')); -gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('src', 'out-build', true)); -gulp.task('watch-client-build', ['clean-client-build'], compilation.watchTask('out-build', true)); - -// Default -gulp.task('default', ['compile']); +const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), compilation.watchTask('out', false))); +gulp.task(watchClientTask); // All -gulp.task('clean', ['clean-client', 'clean-extensions']); -gulp.task('compile', ['monaco-typecheck', 'compile-client', 'compile-extensions']); -gulp.task('watch', [/* 'monaco-typecheck-watch', */ 'watch-client', 'watch-extensions']); +const compileTask = task.define('compile', task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask)); +gulp.task(compileTask); -// All Build -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']); +gulp.task(task.define('watch', task.parallel(/* monacoTypecheckWatchTask, */ watchClientTask, watchExtensionsTask))); + +// Default +gulp.task('default', compileTask); process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); diff --git a/package.json b/package.json index f31cdba4157..aee41e81b21 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.30.0", - "distro": "cedfc514fbce202dad3ec5f86033796d70369b2a", + "version": "1.35.0", + "distro": "a80429a5120c955df4bd2dd95b1cff71b72ab556", "author": { "name": "Microsoft Corporation" }, @@ -21,15 +21,14 @@ "update-grammars": "node build/npm/update-all-grammars.js", "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", - "strict-null-check": "tsc -p src/tsconfig.strictNullChecks.json", - "strict-null-check-watch": "tsc -p src/tsconfig.strictNullChecks.json --watch" + "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", + "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization", + "web": "node scripts/code-web.js" }, "dependencies": { - "applicationinsights": "1.0.6", - "fast-plist": "0.1.2", - "gc-signals": "^0.0.1", + "applicationinsights": "1.0.8", + "gc-signals": "^0.0.2", "getmac": "1.4.1", "graceful-fs": "4.1.11", "http-proxy-agent": "^2.1.0", @@ -41,20 +40,19 @@ "native-is-elevated": "^0.2.1", "native-keymap": "1.2.5", "native-watchdog": "1.0.0", - "node-pty": "0.7.8", + "node-pty": "0.8.1", "semver": "^5.5.0", - "spdlog": "0.7.2", + "spdlog": "0.8.1", "sudo-prompt": "8.2.0", - "v8-inspect-profiler": "^0.0.13", + "v8-inspect-profiler": "^0.0.20", "vscode-chokidar": "1.6.5", - "vscode-debugprotocol": "1.33.0-pre.0", + "vscode-debugprotocol": "1.34.0", "vscode-nsfw": "1.1.1", - "vscode-proxy-agent": "0.1.1", - "vscode-ripgrep": "^1.2.4", - "vscode-sqlite3": "4.0.3", + "vscode-proxy-agent": "0.4.0", + "vscode-ripgrep": "^1.2.5", + "vscode-sqlite3": "4.0.7", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.9.0-beta13", - "winreg": "^1.2.4", + "vscode-xterm": "3.14.0-beta2", "yauzl": "^2.9.1", "yazl": "^2.4.3" }, @@ -63,11 +61,12 @@ "@types/keytar": "^4.0.1", "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", - "@types/node": "^8.9.1", + "@types/node": "^10.12.12", "@types/semver": "^5.5.0", "@types/sinon": "^1.16.36", "@types/webpack": "^4.4.10", "@types/winreg": "^1.2.30", + "ansi-colors": "^3.2.3", "asar": "^0.14.0", "chromium-pickle-js": "^0.2.0", "clean-css": "3.4.6", @@ -78,30 +77,33 @@ "documentdb": "^1.5.1", "electron-mksnapshot": "~2.0.0", "eslint": "^3.4.0", - "event-stream": "^3.1.7", + "event-stream": "3.3.4", "express": "^4.13.1", + "fancy-log": "^1.3.3", + "fast-plist": "0.1.2", "glob": "^5.0.13", - "gulp": "^3.8.9", - "gulp-atom-electron": "^1.19.0", - "gulp-azure-storage": "^0.7.0", + "gulp": "^4.0.0", + "gulp-atom-electron": "^1.20.0", + "gulp-azure-storage": "^0.10.0", "gulp-buffer": "0.0.2", - "gulp-concat": "^2.6.0", - "gulp-cssnano": "^2.1.0", - "gulp-eslint": "^3.0.1", - "gulp-filter": "^3.0.0", - "gulp-flatmap": "^1.0.0", - "gulp-json-editor": "^2.2.1", - "gulp-mocha": "^2.1.3", + "gulp-concat": "^2.6.1", + "gulp-cssnano": "^2.1.3", + "gulp-eslint": "^5.0.0", + "gulp-filter": "^5.1.0", + "gulp-flatmap": "^1.0.2", + "gulp-gunzip": "^1.0.0", + "gulp-json-editor": "^2.5.0", "gulp-plumber": "^1.2.0", - "gulp-remote-src": "^0.4.0", + "gulp-remote-src": "^0.4.4", "gulp-rename": "^1.2.0", "gulp-replace": "^0.5.4", - "gulp-shell": "^0.5.2", - "gulp-tsb": "2.0.5", - "gulp-tslint": "^8.1.2", + "gulp-shell": "^0.6.5", + "gulp-tsb": "2.0.7", + "gulp-tslint": "^8.1.3", "gulp-uglify": "^3.0.0", - "gulp-util": "^3.0.6", - "gulp-vinyl-zip": "^1.2.2", + "gulp-untar": "^0.0.7", + "gulp-vinyl-zip": "^2.1.2", + "http-server": "^0.11.1", "husky": "^0.13.1", "innosetup-compiler": "^5.5.60", "is": "^3.1.0", @@ -114,26 +116,27 @@ "mkdirp": "^0.5.0", "mocha": "^2.2.5", "mocha-junit-reporter": "^1.17.0", + "opn": "^5.4.0", "optimist": "0.3.5", "p-all": "^1.0.0", "pump": "^1.0.1", "queue": "3.0.6", "rcedit": "^1.1.0", - "remap-istanbul": "^0.6.4", + "remap-istanbul": "^0.13.0", "rimraf": "^2.2.8", "sinon": "^1.17.2", "source-map": "^0.4.4", "ts-loader": "^4.4.2", - "tslint": "^5.11.0", - "typescript": "3.1.4", + "tslint": "^5.16.0", + "typescript": "3.4.5", "typescript-formatter": "7.1.0", "typescript-tslint-plugin": "^0.0.7", "uglify-es": "^3.0.18", "underscore": "^1.8.2", - "vinyl": "^0.4.5", - "vinyl-fs": "^2.4.3", + "vinyl": "^2.0.0", + "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-nls-dev": "3.2.2", + "vscode-nls-dev": "3.2.5", "webpack": "^4.16.5", "webpack-cli": "^3.1.0", "webpack-stream": "^5.1.1" @@ -146,6 +149,8 @@ "url": "https://github.com/Microsoft/vscode/issues" }, "optionalDependencies": { + "vscode-windows-ca-certs": "0.1.0", + "vscode-windows-registry": "1.0.1", "windows-foreground-love": "0.1.0", "windows-mutex": "0.2.1", "windows-process-tree": "0.2.3" diff --git a/product.json b/product.json index eb6526c2d8f..4103352af85 100644 --- a/product.json +++ b/product.json @@ -16,9 +16,11 @@ "win32AppUserModelId": "Microsoft.CodeOSS", "win32ShellNameShort": "C&ode - OSS", "darwinBundleIdentifier": "com.visualstudio.code.oss", + "linuxIconName": "com.visualstudio.code.oss", + "licenseFileName": "LICENSE.txt", "reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new", "urlProtocol": "code-oss", "extensionAllowedProposedApi": [ "ms-vscode.references-view" ] -} +} \ No newline at end of file diff --git a/resources/completions/bash/code b/resources/completions/bash/code new file mode 100644 index 00000000000..e377c5d24e2 --- /dev/null +++ b/resources/completions/bash/code @@ -0,0 +1,61 @@ +_code() +{ + local cur prev words cword split + _init_completion -s || return + + _expand || return + + case $prev in + -d|--diff) + _filedir + return + ;; + -a|--add|--user-data-dir|--extensions-dir) + _filedir -d + return + ;; + -g|--goto) + compopt -o nospace + _filedir + return + ;; + --locale) + COMPREPLY=( $( compgen -W 'de en en-US es fr it ja ko ru zh-CN zh-TW bg hu pt-br tr' ) ) + return + ;; + --install-extension|--uninstall-extension) + _filedir vsix + return + ;; + --log) + COMPREPLY=( $( compgen -W 'critical error warn info debug trace off' ) ) + return + ;; + --folder-uri|--disable-extension|--max-memory) + # argument required but no completions available + return 0 + ;; + --enable-proposed-api) + # argument optional but no completions available + ;; + esac + + $split && return + + if [[ $cur == -* ]]; then + COMPREPLY=( $( compgen -W '-d --diff --folder-uri -a --add -g + --goto -n --new-window -r --reuse-window -w --wait --locale= + --user-data-dir -v --version -h --help --extensions-dir + --list-extensions --show-versions --install-extension + --uninstall-extension --enable-proposed-api --verbose --log -s + --status -p --performance --prof-startup --disable-extensions + --disable-extension --inspect-extensions + --inspect-brk-extensions --disable-gpu --upload-logs + --max-memory=' -- "$cur") ) + [[ $COMPREPLY == *= ]] && compopt -o nospace + return + fi + + _filedir +} && +complete -F _code code diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code new file mode 100644 index 00000000000..9579cffb2f6 --- /dev/null +++ b/resources/completions/zsh/_code @@ -0,0 +1,38 @@ +#compdef code + +local arguments + +arguments=( + '(-d --diff)'{-d,--diff}'[compare two files with each other]:file to compare:_files:file to compare with:_files' + \*'--folder-uri[open a window with given folder uri(s)]:folder uri: ' + \*{-a,--add}'[add folder(s) to the last active window]:directory:_directories' + '(-g --goto)'{-g,--goto}'[open a file at the path on the specified line and column position]:file\:line[\:column]:_files -r \:' + '(-n --new-window -r --reuse-window)'{-n,--new-window}'[force to open a new window]' + '(-n --new-window -r --reuse-window)'{-r,--reuse-window}'[force to open a file or folder in an already opened window]' + '(-w --wait)'{-w,--wait}'[wait for the files to be closed before returning]' + '--locale=[the locale to use (e.g. en-US or zh-TW)]:locale (e.g. en-US or zh-TW):(de en en-US es fr it ja ko ru zh-CN zh-TW bg hu pt-br tr)' + '--user-data-dir[specify the directory that user data is kept in]:directory:_directories' + '(- *)'{-v,--version}'[print version]' + '(- *)'{-h,--help}'[print usage]' + '--extensions-dir[set the root path for extensions]:root path:_directories' + '--list-extensions[list the installed extensions]' + '--show-versions[show versions of installed extensions, when using --list-extension]' + '--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"' + '--uninstall-extension[uninstall an extension]:id or path:_files -g "*.vsix(-.)"' + '--enable-proposed-api[enables proposed API features for extensions]::extension id: ' + '--verbose[print verbose output (implies --wait)]' + '--log[log level to use]:level [info]:(critical error warn info debug trace off)' + '(-s --status)'{-s,--status}'[print process usage and diagnostics information]' + '(-p --performance)'{-p,--performance}'[start with the "Developer: Startup Performance" command enabled]' + '--prof-startup[run CPU profiler during startup]' + '(--disable-extension --disable-extensions)--disable-extensions[disable all installed extensions]' + \*'--disable-extension[disable an extension]:extension id: ' + '--inspect-extensions[allow debugging and profiling of extensions]' + '--inspect-brk-extensions[allow debugging and profiling of extensions with the extension host being paused after start]' + '--disable-gpu[disable GPU hardware acceleration]' + '--upload-logs[upload logs from current session to a secure endpoint]:confirm:(iConfirmLogsUpload)' + '--max-memory=[max memory size for a window (in Mbytes)]:size (Mbytes)' + '*:file or directory:_files' +) + +_arguments -s -S $arguments diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 55f50b6f1c1..64109f1fa12 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -3,6 +3,15 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. +# test that VSCode wasn't installed inside WSL +if grep -qi Microsoft /proc/version; then + echo "To use VS Code with the Windows Subsystem for Linux, please install VS Code in Windows and uninstall the Linux version in WSL. You can then use the '@@PRODNAME@@' command in a WSL terminal just as you would in a normal command prompt." 1>&2 + read -e -p "Do you want to continue anyways ? [y/N] " YN + + [[ $YN == "n" || $YN == "N" || $YN == "" ]] && exit 1 +fi + + # If root, ensure that --user-data-dir or --file-write is specified if [ "$(id -u)" = "0" ]; then for i in $@ diff --git a/resources/linux/code-url-handler.desktop b/resources/linux/code-url-handler.desktop index 1dcc422e72c..09e39c57c51 100644 --- a/resources/linux/code-url-handler.desktop +++ b/resources/linux/code-url-handler.desktop @@ -7,7 +7,6 @@ Icon=@@ICON@@ Type=Application NoDisplay=true StartupNotify=true -StartupWMClass=@@NAME_SHORT@@ Categories=Utility;TextEditor;Development;IDE; MimeType=x-scheme-handler/@@URLPROTOCOL@@; Keywords=vscode; diff --git a/resources/linux/code.desktop b/resources/linux/code.desktop index f93060d565b..dbc7818cecf 100644 --- a/resources/linux/code.desktop +++ b/resources/linux/code.desktop @@ -5,7 +5,7 @@ GenericName=Text Editor Exec=/usr/share/@@NAME@@/@@NAME@@ --unity-launch %F Icon=@@ICON@@ Type=Application -StartupNotify=true +StartupNotify=false StartupWMClass=@@NAME_SHORT@@ Categories=Utility;TextEditor;Development;IDE; MimeType=text/plain;inode/directory; diff --git a/resources/linux/debian/control.template b/resources/linux/debian/control.template index 5e7617824cd..27d36308c88 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 (>= 2:3.26), gnupg, apt, libxkbfile1, libgconf-2-4, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1 +Depends: libnotify4, libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1 Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation <vscode-linux@microsoft.com> diff --git a/resources/linux/rpm/code.spec.template b/resources/linux/rpm/code.spec.template index 71e016614e2..c942d65a745 100644 --- a/resources/linux/rpm/code.spec.template +++ b/resources/linux/rpm/code.spec.template @@ -18,10 +18,14 @@ Visual Studio Code is a new choice of tool that combines the simplicity of a cod mkdir -p %{buildroot}/usr/share/@@NAME@@ mkdir -p %{buildroot}/usr/share/applications mkdir -p %{buildroot}/usr/share/pixmaps +#mkdir -p %{buildroot}/usr/share/bash-completion/completions +#mkdir -p %{buildroot}/usr/share/zsh/site-functions cp -r usr/share/@@NAME@@/* %{buildroot}/usr/share/@@NAME@@ cp -r usr/share/applications/@@NAME@@.desktop %{buildroot}/usr/share/applications cp -r usr/share/applications/@@NAME@@-url-handler.desktop %{buildroot}/usr/share/applications -cp -r usr/share/pixmaps/@@NAME@@.png %{buildroot}/usr/share/pixmaps +cp -r usr/share/pixmaps/@@ICON@@.png %{buildroot}/usr/share/pixmaps +#cp usr/share/bash-completion/completions/code %{buildroot}/usr/share/bash-completion/completions/code +#cp usr/share/zsh/site-functions/_code %{buildroot}/usr/share/zsh/site-functions/_code %post # Remove the legacy bin command if this is the stable build @@ -53,4 +57,6 @@ fi /usr/share/@@NAME@@/ /usr/share/applications/@@NAME@@.desktop /usr/share/applications/@@NAME@@-url-handler.desktop -/usr/share/pixmaps/@@NAME@@.png +/usr/share/pixmaps/@@ICON@@.png +#/usr/share/bash-completion/completions/code +#/usr/share/zsh/site-functions/_code diff --git a/resources/linux/rpm/dependencies.json b/resources/linux/rpm/dependencies.json index c2ae8b8fe31..d0b64c4fa68 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -27,7 +27,6 @@ "libX11.so.6()(64bit)", "libXss.so.1()(64bit)", "libXtst.so.6()(64bit)", - "libgconf-2.so.4()(64bit)", "libgmodule-2.0.so.0()(64bit)", "librt.so.1()(64bit)", "libglib-2.0.so.0()(64bit)", @@ -107,7 +106,6 @@ "libgcc_s.so.1", "libgcc_s.so.1(GCC_4.0.0)", "libgcc_s.so.1(GLIBC_2.0)", - "libgconf-2.so.4", "libgdk-x11-2.0.so.0", "libgdk_pixbuf-2.0.so.0", "libgio-2.0.so.0", diff --git a/resources/linux/snap/electron-launch b/resources/linux/snap/electron-launch index 6fdd68a34cf..2a1c4395187 100755 --- a/resources/linux/snap/electron-launch +++ b/resources/linux/snap/electron-launch @@ -1,6 +1,34 @@ -#!/bin/sh +#!/usr/bin/env bash -# Create $XDG_RUNTIME_DIR if it doesn't exist +# On Fedora $SNAP is under /var and there is some magic to map it to /snap. +# We need to handle that case and reset $SNAP +SNAP=$(echo $SNAP | sed -e "s|/var/lib/snapd||g") + +if [ "$SNAP_ARCH" == "amd64" ]; then + ARCH="x86_64-linux-gnu" +elif [ "$SNAP_ARCH" == "armhf" ]; then + ARCH="arm-linux-gnueabihf" +elif [ "$SNAP_ARCH" == "arm64" ]; then + ARCH="aarch64-linux-gnu" +else + ARCH="$SNAP_ARCH-linux-gnu" +fi + +export XDG_CACHE_HOME=$SNAP_USER_COMMON/.cache +if [[ -d $SNAP_USER_DATA/.cache && ! -e $XDG_CACHE_HOME ]]; then + # the .cache directory used to be stored under $SNAP_USER_DATA, migrate it + mv $SNAP_USER_DATA/.cache $SNAP_USER_COMMON/ +fi +mkdir -p $XDG_CACHE_HOME + +# Gdk-pixbuf loaders +export GDK_PIXBUF_MODULE_FILE=$XDG_CACHE_HOME/gdk-pixbuf-loaders.cache +export GDK_PIXBUF_MODULEDIR=$SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/2.10.0/loaders +if [ -f $SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders ]; then + $SNAP/usr/lib/$ARCH/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders > $GDK_PIXBUF_MODULE_FILE +fi + +# Create $XDG_RUNTIME_DIR if not exists (to be removed when https://pad.lv/1656340 is fixed) [ -n "$XDG_RUNTIME_DIR" ] && mkdir -p $XDG_RUNTIME_DIR -m 700 exec "$@" diff --git a/resources/linux/snap/snapUpdate.sh b/resources/linux/snap/snapUpdate.sh deleted file mode 100755 index d9299ca4d58..00000000000 --- a/resources/linux/snap/snapUpdate.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -sleep 2 -@@NAME@@ \ No newline at end of file diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index e0772308684..0a78d41e8fd 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -10,35 +10,54 @@ grade: stable confinement: classic parts: + gnome: + plugin: nil + build-packages: + - software-properties-common + override-pull: | + add-apt-repository -y ppa:ubuntu-desktop/gnome-3-26 + apt -y update + code: + after: + - gnome plugin: dump source: . stage-packages: + - fcitx-frontend-gtk3 + - gvfs-libs - libasound2 - - libc++1 - - libgconf2-4 + - libgconf-2-4 + - libglib2.0-bin + - libgnome-keyring0 + - libgtk-3-0 - libnotify4 - libnspr4 - libnss3 - libpcre3 - libpulse0 + - libsecret-1-0 - libxss1 - libxtst6 - - libxkbcommon0 - - libgtk-3-0 - - libgdk-pixbuf2.0-0 - - libglib2.0-bin - - unity-gtk2-module - - libappindicator1 - - xdg-user-dirs - - libsecret-1-0 - # TODO@joao @Tyriar check these deps - # - libatomic1 - # - libgtk2.0-bin + - zlib1g prime: - - -usr/share/dh-python + - -usr/share/doc + - -usr/share/fonts + - -usr/share/icons + - -usr/share/lintian + - -usr/share/man apps: @@NAME@@: - command: electron-launch ${SNAP}/usr/share/@@NAME@@/bin/@@NAME@@ - desktop: usr/share/applications/@@NAME@@.desktop \ No newline at end of file + command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ + desktop: usr/share/applications/@@NAME@@.desktop + environment: + DISABLE_WAYLAND: 1 + GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas + + url-handler: + command: electron-launch $SNAP/usr/share/@@NAME@@/bin/@@NAME@@ --open-url + desktop: usr/share/applications/@@NAME@@-url-handler.desktop + environment: + DISABLE_WAYLAND: 1 + GSETTINGS_SCHEMA_DIR: $SNAP/usr/share/glib-2.0/schemas \ No newline at end of file diff --git a/resources/win32/bin/code.cmd b/resources/win32/bin/code.cmd index 4f31b474695..33c640f5dd1 100644 --- a/resources/win32/bin/code.cmd +++ b/resources/win32/bin/code.cmd @@ -2,5 +2,5 @@ setlocal set VSCODE_DEV= set ELECTRON_RUN_AS_NODE=1 -call "%~dp0..\@@NAME@@.exe" "%~dp0..\resources\app\out\cli.js" %* +"%~dp0..\@@NAME@@.exe" "%~dp0..\resources\app\out\cli.js" %* endlocal \ No newline at end of file diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index eaef0759a87..23e8a522007 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -2,23 +2,48 @@ # # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. - +COMMIT="@@COMMIT@@" +APP_NAME="@@APPNAME@@" +QUALITY="@@QUALITY@@" NAME="@@NAME@@" VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" ELECTRON="$VSCODE_PATH/$NAME.exe" -if grep -q Microsoft /proc/version; then - if [ -x /bin/wslpath ]; then - # On recent WSL builds, we just need to set WSLENV so that - # ELECTRON_RUN_AS_NODE is visible to the win32 process - export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV - CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js") - else +if grep -qi Microsoft /proc/version; then + # in a wsl shell + fallback() { # If running under older WSL, don't pass cli.js to Electron as # environment vars cannot be transferred from WSL to Windows # See: https://github.com/Microsoft/BashOnWindows/issues/1363 # https://github.com/Microsoft/BashOnWindows/issues/1494 "$ELECTRON" "$@" exit $? + } + WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-Microsoft/\1/') + # wslpath is not available prior to WSL build 17046 + # See: https://docs.microsoft.com/en-us/windows/wsl/release-notes#build-17046 + if [ -x /bin/wslpath ]; then + WIN_CODE_CMD=$(wslpath -w "$(dirname "$(realpath "$0")")/$APP_NAME.cmd") + # make sure the cwd is in the windows fs, otherwise there will be a warning from cmd + pushd "$(dirname "$0")" > /dev/null + WSL_EXT_ID="ms-vscode-remote.remote-wsl" + WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CMD" --locate-extension $WSL_EXT_ID) + popd > /dev/null + if ! [ -z "$WSL_EXT_WLOC" ]; then + # replace \r\n with \n in WSL_EXT_WLOC, get linux path for + WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh + "$WSL_CODE" $COMMIT $QUALITY "$WIN_CODE_CMD" "$APP_NAME" "$@" + exit $? + elif [ $WSL_BUILD -ge 17063 ] 2> /dev/null; then + # Since WSL build 17063, we just need to set WSLENV so that + # ELECTRON_RUN_AS_NODE is visible to the win32 process + # See: https://docs.microsoft.com/en-us/windows/wsl/release-notes#build-17063 + export WSLENV=ELECTRON_RUN_AS_NODE/w:$WSLENV + CLI=$(wslpath -m "$VSCODE_PATH/resources/app/out/cli.js") + else # $WSL_BUILD ∈ [17046, 17063) OR $WSL_BUILD is indeterminate + fallback "$@" + fi + else + fallback "$@" fi elif [ -x "$(command -v cygpath)" ]; then CLI=$(cygpath -m "$VSCODE_PATH/resources/app/out/cli.js") @@ -26,4 +51,4 @@ else CLI="$VSCODE_PATH/resources/app/out/cli.js" fi ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" "$@" -exit $? +exit $? \ No newline at end of file diff --git a/scripts/code-cli.bat b/scripts/code-cli.bat index 927e3e00d22..6fbf5edd626 100644 --- a/scripts/code-cli.bat +++ b/scripts/code-cli.bat @@ -17,7 +17,7 @@ set CODE=".build\electron\%NAMESHORT%" node build\lib\electron.js if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron -:: Manage build-in extensions +:: Manage built-in extensions if "%1"=="--builtin" goto builtin :: Sync built-in extensions diff --git a/scripts/code-web.js b/scripts/code-web.js new file mode 100644 index 00000000000..da3afe33e47 --- /dev/null +++ b/scripts/code-web.js @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const httpServer = require('http-server'); +const opn = require('opn'); + +const url = 'http://127.0.0.1:8080/out/vs/code/browser/workbench/workbench.html'; + +httpServer.createServer({ root: '.', cache: 5 }).listen(8080); +console.log(`Open ${url} in your browser`); + +opn(url); \ No newline at end of file diff --git a/scripts/code.bat b/scripts/code.bat index 6789e54fdda..8c058365dca 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -36,9 +36,6 @@ set ELECTRON_ENABLE_STACK_DUMPING=1 :: Launch Code -:: Use the following to get v8 tracing: -:: %CODE% --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" . %* - %CODE% . %* goto end diff --git a/scripts/code.sh b/scripts/code.sh index 26332faea6c..1f7cda590a1 100755 --- a/scripts/code.sh +++ b/scripts/code.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -e + if [[ "$OSTYPE" == "darwin"* ]]; then realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"; } ROOT=$(dirname "$(dirname "$(realpath "$0")")") @@ -9,6 +11,9 @@ if [[ "$OSTYPE" == "darwin"* ]]; then export ELECTRON_ENABLE_LOGGING=1 else ROOT=$(dirname "$(dirname "$(readlink -f $0)")") + if grep -qi Microsoft /proc/version; then + IN_WSL=true + fi fi function code() { @@ -50,7 +55,23 @@ function code() { exec "$CODE" . "$@" } -# Use the following to get v8 tracing: -# code --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" "$@" +function code-wsl() +{ + # in a wsl shell + local WIN_CODE_CLI_CMD=$(wslpath -w "$ROOT/scripts/code-cli.bat") + if ! [ -z "$WIN_CODE_CLI_CMD" ]; then + local WSL_EXT_ID="ms-vscode-remote.remote-wsl" + local WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CLI_CMD" --locate-extension $WSL_EXT_ID) + if ! [ -z "$WSL_EXT_WLOC" ]; then + # replace \r\n with \n in WSL_EXT_WLOC + local WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode-dev.sh + $WSL_CODE "$ROOT" "$@" + exit $? + fi + fi +} -code "$@" +if ! [ -z ${IN_WSL+x} ]; then + code-wsl "$@" +fi +code "$@" \ No newline at end of file diff --git a/scripts/generate-definitelytyped.sh b/scripts/generate-definitelytyped.sh new file mode 100755 index 00000000000..82de9124a07 --- /dev/null +++ b/scripts/generate-definitelytyped.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +if [ $# -eq 0 ]; then + echo "Pass in a version like ./scripts/generate-vscode-dts.sh 1.30." + echo "Failed to generate index.d.ts." + exit 1 +fi + +header="// Type definitions for Visual Studio Code ${1} +// Project: https://github.com/microsoft/vscode +// Definitions by: Visual Studio Code Team, Microsoft <https://github.com/Microsoft> +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Type Definition for Visual Studio Code ${1} Extension API + * See https://code.visualstudio.com/api for more information + */" + +if [ -f ./src/vs/vscode.d.ts ]; then + echo "$header" > index.d.ts + sed "1,4d" ./src/vs/vscode.d.ts >> index.d.ts + echo "Generated index.d.ts for version ${1}." +else + echo "Can't find ./src/vs/vscode.d.ts. Run this script at vscode root." +fi \ No newline at end of file diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index fc13a2d99b4..d6ef0295e7d 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -10,19 +10,19 @@ call .\scripts\test.bat --runGlob **\*.integrationTest.js %* if %errorlevel% neq 0 exit /b %errorlevel% :: Tests in the extension host -REM call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% -REM if %errorlevel% neq 0 exit /b %errorlevel% - -REM call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% -REM if %errorlevel% neq 0 exit /b %errorlevel% - -call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% +call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% . +call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testworkspace.code-workspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\workspace-tests --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% if %errorlevel% neq 0 exit /b %errorlevel% -# Tests in commonJS (HTML, CSS, JSON language server tests...) +call .\scripts\code.bat %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% +if %errorlevel% neq 0 exit /b %errorlevel% + +call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disable-extensions --user-data-dir=%VSCODEUSERDATADIR% . +if %errorlevel% neq 0 exit /b %errorlevel% + +:: Tests in commonJS (HTML, CSS, JSON language server tests...) call .\scripts\node-electron.bat .\node_modules\mocha\bin\_mocha .\extensions\*\server\out\test\**\*.test.js if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index a51f3ee172c..d95026b945d 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -16,14 +16,14 @@ cd $ROOT ./scripts/test.sh --runGlob **/*.integrationTest.js "$@" # Tests in the extension host -./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started -./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/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started +./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-extensions --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 --disable-extensions --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 --disable-extensions --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 . -rm -r $ROOT/extensions/emmet/test-fixtures +mkdir -p $ROOT/extensions/emmet/test-fixtures +./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-extensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started +rm -rf $ROOT/extensions/emmet/test-fixtures # Tests in commonJS cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index c8442633b5f..a8144075dd5 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -18,8 +18,7 @@ loader.config({ catchError: true, nodeRequire: require, nodeMain: __filename, - 'vs/nls': nlsConfig, - nodeCachedDataDir: process.env['VSCODE_NODE_CACHED_DATA_DIR'] + 'vs/nls': nlsConfig }); // Running in Electron @@ -41,6 +40,16 @@ exports.load = function (entrypoint, onLoad, onError) { return; } + // cached data config + if (process.env['VSCODE_NODE_CACHED_DATA_DIR']) { + loader.config({ + nodeCachedData: { + path: process.env['VSCODE_NODE_CACHED_DATA_DIR'], + seed: entrypoint + } + }); + } + onLoad = onLoad || function () { }; onError = onError || function (err) { console.error(err); }; diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index e30cadb0fca..c49274c99a7 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -11,6 +11,10 @@ const bootstrap = require('./bootstrap'); // Enable ASAR in our forked processes bootstrap.enableASARSupport(); +if (process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']) { + bootstrap.injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']); +} + // Configure: pipe logging to parent process if (!!process.send && process.env.PIPE_LOGGING === 'true') { pipeLoggingToParent(); diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 77c606a6774..85548f51965 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -30,8 +30,24 @@ exports.load = function (modulePaths, resultCallback, options) { const path = require('path'); const args = parseURLQueryArgs(); + /** + * // configuration: IWindowConfiguration + * @type {{ + * zoomLevel?: number, + * extensionDevelopmentPath?: string | string[], + * extensionTestsPath?: string, + * userEnv?: { [key: string]: string | undefined }, + * appRoot?: string, + * nodeCachedDataDir?: string + * }} */ const configuration = JSON.parse(args['config'] || '{}') || {}; + // Apply zoom level early to avoid glitches + const zoomLevel = configuration.zoomLevel; + if (typeof zoomLevel === 'number' && zoomLevel !== 0) { + webFrame.setZoomLevel(zoomLevel); + } + // Error handler // @ts-ignore process.on('uncaughtException', function (error) { @@ -51,13 +67,6 @@ exports.load = function (modulePaths, resultCallback, options) { // Enable ASAR support bootstrap.enableASARSupport(path.join(configuration.appRoot, 'node_modules')); - // 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); - } - if (options && typeof options.canModifyDOM === 'function') { options.canModifyDOM(configuration); } @@ -91,10 +100,17 @@ exports.load = function (modulePaths, resultCallback, options) { const loaderConfig = { baseUrl: bootstrap.uriFromPath(configuration.appRoot) + '/out', 'vs/nls': nlsConfig, - nodeCachedDataDir: configuration.nodeCachedDataDir, nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] }; + // cached data config + if (configuration.nodeCachedDataDir) { + loaderConfig.nodeCachedData = { + path: configuration.nodeCachedDataDir, + seed: modulePaths.join('') + }; + } + if (options && typeof options.beforeLoaderConfig === 'function') { options.beforeLoaderConfig(configuration, loaderConfig); } @@ -162,11 +178,12 @@ function registerDeveloperKeybindings() { // Devtools & reload support const TOGGLE_DEV_TOOLS_KB = (process.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I + const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12 const RELOAD_KB = (process.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R let listener = function (e) { const key = extractKey(e); - if (key === TOGGLE_DEV_TOOLS_KB) { + if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) { ipc.send('vscode:toggleDevTools'); } else if (key === RELOAD_KB) { ipc.send('vscode:reloadWindow'); @@ -178,7 +195,7 @@ function registerDeveloperKeybindings() { return function () { if (listener) { window.removeEventListener('keydown', listener); - listener = void 0; + listener = undefined; } }; } @@ -197,4 +214,4 @@ function onUnexpectedError(error, enableDeveloperTools) { if (error.stack) { console.error(error.stack); } -} \ No newline at end of file +} diff --git a/src/bootstrap.js b/src/bootstrap.js index 5f73a09dc7e..bb29caa6e82 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -20,6 +20,38 @@ process.on('SIGPIPE', () => { //#endregion +//#region Add support for redirecting the loading of node modules +exports.injectNodeModuleLookupPath = function (injectPath) { + if (!injectPath) { + throw new Error('Missing injectPath'); + } + + // @ts-ignore + const Module = require('module'); + const path = require('path'); + + const nodeModulesPath = path.join(__dirname, '../node_modules'); + + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; + + // @ts-ignore + Module._resolveLookupPaths = function (moduleName, parent, newReturn) { + const result = originalResolveLookupPaths(moduleName, parent, newReturn); + + const paths = newReturn ? result : result[1]; + for (let i = 0, len = paths.length; i < len; i++) { + if (paths[i] === nodeModulesPath) { + paths.splice(i, 0, injectPath); + break; + } + } + + return result; + }; +}; +//#endregion + //#region Add support for using node_modules.asar /** * @param {string=} nodeModulesPath @@ -69,7 +101,15 @@ exports.uriFromPath = function (_path) { pathName = '/' + pathName; } - return encodeURI('file://' + pathName).replace(/#/g, '%23'); + /** @type {string} */ + let uri; + if (process.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths + uri = encodeURI('file:' + pathName); + } else { + uri = encodeURI('file://' + pathName); + } + + return uri.replace(/#/g, '%23'); }; //#endregion @@ -110,6 +150,36 @@ exports.writeFile = function (file, content) { }); }); }; + +/** + * @param {string} dir + * @returns {Promise<string>} + */ +function mkdir(dir) { + const fs = require('fs'); + + return new Promise((c, e) => fs.mkdir(dir, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); +} + +/** + * @param {string} dir + * @returns {Promise<string>} + */ +exports.mkdirp = function mkdirp(dir) { + const path = require('path'); + + return mkdir(dir).then(null, err => { + if (err && err.code === 'ENOENT') { + const parent = path.dirname(dir); + + if (parent !== dir) { // if not arrived at root + return mkdirp(parent).then(() => mkdir(dir)); + } + } + + throw err; + }); +}; //#endregion //#region NLS helpers diff --git a/src/buildfile.js b/src/buildfile.js index bd05c387ae0..a7c0376985e 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -5,13 +5,15 @@ exports.base = [{ name: 'vs/base/common/worker/simpleWorker', - include: [ 'vs/editor/common/services/editorSimpleWorker' ], - prepend: [ 'vs/loader.js' ], - append: [ 'vs/base/worker/workerMain' ], + include: ['vs/editor/common/services/editorSimpleWorker'], + prepend: ['vs/loader.js'], + append: ['vs/base/worker/workerMain'], dest: 'vs/base/worker/workerMain.js' }]; -//@ts-ignore review + exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.main']); +exports.workbenchWeb = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.web.main']); + exports.code = require('./vs/code/buildfile').collectModules(); exports.entrypoint = function (name) { diff --git a/src/main.js b/src/main.js index a551417c8e8..186be5a805c 100644 --- a/src/main.js +++ b/src/main.js @@ -7,9 +7,10 @@ 'use strict'; const perf = require('./vs/base/common/performance'); +const lp = require('./vs/base/node/languagePacks'); + perf.mark('main:started'); -const fs = require('fs'); const path = require('path'); const bootstrap = require('./bootstrap'); const paths = require('./paths'); @@ -42,9 +43,11 @@ registerListeners(); */ let nlsConfiguration = undefined; const userDefinedLocale = getUserDefinedLocale(); -userDefinedLocale.then((locale) => { +const metaDataFile = path.join(__dirname, 'nls.metadata.json'); + +userDefinedLocale.then(locale => { if (locale && !nlsConfiguration) { - nlsConfiguration = getNLSConfiguration(locale); + nlsConfiguration = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); } }); @@ -74,7 +77,7 @@ function onReady() { Promise.all([nodeCachedDataDir.ensureExists(), userDefinedLocale]).then(([cachedDataDir, locale]) => { if (locale && !nlsConfiguration) { - nlsConfiguration = getNLSConfiguration(locale); + nlsConfiguration = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale); } if (!nlsConfiguration) { @@ -83,7 +86,7 @@ function onReady() { // First, we need to test a user defined locale. If it fails we try the app locale. // If that fails we fall back to English. - nlsConfiguration.then((nlsConfig) => { + nlsConfiguration.then(nlsConfig => { const startup = nlsConfig => { nlsConfig._languagePackSupport = true; @@ -91,7 +94,10 @@ function onReady() { process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || ''; // Load main in AMD - require('./bootstrap-amd').load('vs/code/electron-main/main'); + perf.mark('willLoadMainBundle'); + require('./bootstrap-amd').load('vs/code/electron-main/main', () => { + perf.mark('didLoadMainBundle'); + }); }; // We recevied a valid nlsConfig from a user defined locale @@ -111,7 +117,7 @@ function onReady() { // See above the comment about the loader and case sensitiviness appLocale = appLocale.toLowerCase(); - getNLSConfiguration(appLocale).then((nlsConfig) => { + lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale).then(nlsConfig => { if (!nlsConfig) { nlsConfig = { locale: appLocale, availableLanguages: {} }; } @@ -132,17 +138,19 @@ function onReady() { */ function configureCommandlineSwitches(cliArgs, nodeCachedDataDir) { - // 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'); + app.commandLine.appendSwitch('disable-color-correct-rendering'); // Support JS Flags const jsFlags = resolveJSFlags(cliArgs, nodeCachedDataDir.jsFlags()); if (jsFlags) { app.commandLine.appendSwitch('--js-flags', jsFlags); } + + // Disable smooth scrolling for Webviews + if (cliArgs['disable-smooth-scrolling']) { + app.commandLine.appendSwitch('disable-smooth-scrolling'); + } } /** @@ -151,10 +159,13 @@ function configureCommandlineSwitches(cliArgs, nodeCachedDataDir) { * @returns {string} */ function resolveJSFlags(cliArgs, ...jsFlags) { + + // Add any existing JS flags we already got from the command line if (cliArgs['js-flags']) { jsFlags.push(cliArgs['js-flags']); } + // Support max-memory flag if (cliArgs['max-memory'] && !/max_old_space_size=(\d+)/g.exec(cliArgs['js-flags'])) { jsFlags.push(`--max_old_space_size=${cliArgs['max-memory']}`); } @@ -252,11 +263,12 @@ function getNodeCachedDir() { } jsFlags() { - return this.value ? '--nolazy' : undefined; + // return this.value ? '--nolazy' : undefined; + return undefined; } ensureExists() { - return mkdirp(this.value).then(() => this.value, () => { /*ignore*/ }); + return bootstrap.mkdirp(this.value).then(() => this.value, () => { /*ignore*/ }); } _compute() { @@ -286,7 +298,7 @@ function getNodeCachedDir() { * @returns {string} */ function stripComments(content) { - const regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + const regexp = /("(?:[^\\"]*(?:\\.)?)*")|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; return content.replace(regexp, function (match, m1, m2, m3, m4) { // Only one of m1, m2, m3, m4 matches @@ -309,101 +321,6 @@ function stripComments(content) { }); } -/** - * @param {string} dir - * @returns {Promise<string>} - */ -function mkdir(dir) { - return new Promise((c, e) => fs.mkdir(dir, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); -} - -/** - * @param {string} file - * @returns {Promise<boolean>} - */ -function exists(file) { - return new Promise(c => fs.exists(file, c)); -} - -/** - * @param {string} file - * @returns {Promise<void>} - */ -function touch(file) { - return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); -} - -/** - * @param {string} file - * @returns {Promise<object>} - */ -function lstat(file) { - return new Promise((c, e) => fs.lstat(file, (err, stats) => err ? e(err) : c(stats))); -} - -/** - * @param {string} dir - * @returns {Promise<string[]>} - */ -function readdir(dir) { - return new Promise((c, e) => fs.readdir(dir, (err, files) => err ? e(err) : c(files))); -} - -/** - * @param {string} dir - * @returns {Promise<void>} - */ -function rmdir(dir) { - return new Promise((c, e) => fs.rmdir(dir, err => err ? e(err) : c(undefined))); -} - -/** - * @param {string} file - * @returns {Promise<void>} - */ -function unlink(file) { - return new Promise((c, e) => fs.unlink(file, err => err ? e(err) : c(undefined))); -} - -/** - * @param {string} dir - * @returns {Promise<string>} - */ -function mkdirp(dir) { - return mkdir(dir).then(null, err => { - if (err && err.code === 'ENOENT') { - const parent = path.dirname(dir); - - if (parent !== dir) { // if not arrived at root - return mkdirp(parent).then(() => mkdir(dir)); - } - } - - throw err; - }); -} - -/** - * @param {string} location - * @returns {Promise<void>} - */ -function rimraf(location) { - return lstat(location).then(stat => { - if (stat.isDirectory() && !stat.isSymbolicLink()) { - return readdir(location) - .then(children => Promise.all(children.map(child => rimraf(path.join(location, child))))) - .then(() => rmdir(location)); - } else { - return unlink(location); - } - }, err => { - if (err.code === 'ENOENT') { - return void 0; - } - throw err; - }); -} - // Language tags are case insensitive however an amd loader is case sensitive // To make this work on case preserving & insensitive FS we do the following: // the language bundles have lower case language tags and we always lower case @@ -418,191 +335,16 @@ function getUserDefinedLocale() { } const localeConfig = path.join(userDataPath, 'User', 'locale.json'); - return exists(localeConfig).then((result) => { - if (result) { - return bootstrap.readFile(localeConfig).then((content) => { - content = stripComments(content); - try { - const value = JSON.parse(content).locale; - return value && typeof value === 'string' ? value.toLowerCase() : undefined; - } catch (e) { - return undefined; - } - }); - } else { + return bootstrap.readFile(localeConfig).then(content => { + content = stripComments(content); + try { + const value = JSON.parse(content).locale; + return value && typeof value === 'string' ? value.toLowerCase() : undefined; + } catch (e) { return undefined; } + }, () => { + return undefined; }); } - -/** - * @returns {object} - */ -function getLanguagePackConfigurations() { - const configFile = path.join(userDataPath, 'languagepacks.json'); - try { - return require(configFile); - } catch (err) { - // Do nothing. If we can't read the file we have no - // language pack config. - } - return undefined; -} - -/** - * @param {object} config - * @param {string} locale - */ -function resolveLanguagePackLocale(config, locale) { - try { - while (locale) { - if (config[locale]) { - return locale; - } else { - const index = locale.lastIndexOf('-'); - if (index > 0) { - locale = locale.substring(0, index); - } else { - return undefined; - } - } - } - } catch (err) { - console.error('Resolving language pack configuration failed.', err); - } - return undefined; -} - -/** - * @param {string} locale - */ -function getNLSConfiguration(locale) { - if (locale === 'pseudo') { - return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true }); - } - - if (process.env['VSCODE_DEV']) { - return Promise.resolve({ locale: locale, availableLanguages: {} }); - } - - // 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 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 === 'en-us')) { - return Promise.resolve({ locale: locale, availableLanguages: {} }); - } - - const initialLocale = locale; - - perf.mark('nlsGeneration:start'); - - const defaultResult = function (locale) { - perf.mark('nlsGeneration:end'); - return Promise.resolve({ locale: locale, availableLanguages: {} }); - }; - try { - const commit = product.commit; - if (!commit) { - return defaultResult(initialLocale); - } - const configs = getLanguagePackConfigurations(); - if (!configs) { - return defaultResult(initialLocale); - } - locale = resolveLanguagePackLocale(configs, locale); - if (!locale) { - return defaultResult(initialLocale); - } - const packConfig = configs[locale]; - let mainPack; - if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { - return defaultResult(initialLocale); - } - return exists(mainPack).then((fileExists) => { - if (!fileExists) { - return defaultResult(initialLocale); - } - const packId = packConfig.hash + '.' + locale; - const cacheRoot = path.join(userDataPath, 'clp', packId); - const coreLocation = path.join(cacheRoot, commit); - const translationsConfigFile = path.join(cacheRoot, 'tcf.json'); - const corruptedFile = path.join(cacheRoot, 'corrupted.info'); - const result = { - locale: initialLocale, - availableLanguages: { '*': locale }, - _languagePackId: packId, - _translationsConfigFile: translationsConfigFile, - _cacheRoot: cacheRoot, - _resolvedLanguagePackCoreLocation: coreLocation, - _corruptedFile: corruptedFile - }; - return exists(corruptedFile).then((corrupted) => { - // The nls cache directory is corrupted. - let toDelete; - if (corrupted) { - toDelete = rimraf(cacheRoot); - } else { - toDelete = Promise.resolve(undefined); - } - return toDelete.then(() => { - return exists(coreLocation).then((fileExists) => { - if (fileExists) { - // We don't wait for this. No big harm if we can't touch - touch(coreLocation).catch(() => { }); - perf.mark('nlsGeneration:end'); - return result; - } - return mkdirp(coreLocation).then(() => { - return Promise.all([bootstrap.readFile(path.join(__dirname, 'nls.metadata.json')), bootstrap.readFile(mainPack)]); - }).then((values) => { - const metadata = JSON.parse(values[0]); - const packData = JSON.parse(values[1]).contents; - const bundles = Object.keys(metadata.bundles); - const writes = []; - for (let bundle of bundles) { - const modules = metadata.bundles[bundle]; - const target = Object.create(null); - for (let module of modules) { - const keys = metadata.keys[module]; - const defaultMessages = metadata.messages[module]; - const translations = packData[module]; - let targetStrings; - if (translations) { - targetStrings = []; - for (let i = 0; i < keys.length; i++) { - const elem = keys[i]; - const key = typeof elem === 'string' ? elem : elem.key; - let translatedMessage = translations[key]; - if (translatedMessage === undefined) { - translatedMessage = defaultMessages[i]; - } - targetStrings.push(translatedMessage); - } - } else { - targetStrings = defaultMessages; - } - target[module] = targetStrings; - } - writes.push(bootstrap.writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target))); - } - writes.push(bootstrap.writeFile(translationsConfigFile, JSON.stringify(packConfig.translations))); - return Promise.all(writes); - }).then(() => { - perf.mark('nlsGeneration:end'); - return result; - }).catch((err) => { - console.error('Generating translation files failed.', err); - return defaultResult(locale); - }); - }); - }); - }); - }); - } catch (err) { - console.error('Generating translation files failed.', err); - return defaultResult(locale); - } -} -//#endregion +//#endregion \ No newline at end of file diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index 2235d2eefa5..627491de597 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -2,13 +2,17 @@ "compilerOptions": { "module": "amd", "moduleResolution": "node", - "noImplicitAny": false, + "noImplicitAny": true, + "suppressImplicitAnyIndexErrors": true, "target": "es5", "experimentalDecorators": true, "noImplicitReturns": true, "noUnusedLocals": true, "noImplicitThis": true, "alwaysStrict": true, + "strictBindCallApply": true, + "strictNullChecks": true, + "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { "vs/*": [ diff --git a/src/tsconfig.json b/src/tsconfig.json index 4b9f4170455..25a1f449cc9 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -4,7 +4,13 @@ "removeComments": false, "preserveConstEnums": true, "sourceMap": false, - "outDir": "../out" + "outDir": "../out", + "target": "es6", + "lib": [ + "dom", + "es5", + "es2015.iterable" + ] }, "include": [ "./typings", diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 1bf4732a4a0..32983503724 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -1,24 +1,23 @@ { + "extends": "./tsconfig.base.json", "compilerOptions": { "noEmit": true, + "types": [], + "paths": {}, "module": "amd", "moduleResolution": "classic", - "noImplicitAny": false, "removeComments": false, "preserveConstEnums": true, "target": "es5", "sourceMap": false, - "experimentalDecorators": true, - "declaration": true, - "noImplicitReturns": true, - "baseUrl": ".", - "types": [] + "declaration": true }, "include": [ "typings/require.d.ts", "./typings/require-monaco.d.ts", "typings/thenable.d.ts", "typings/es6-promise.d.ts", + "typings/lib.es2018.promise.d.ts", "typings/lib.array-ext.d.ts", "typings/lib.ie11_safe_es6.d.ts", "vs/css.d.ts", @@ -35,4 +34,4 @@ "exclude": [ "node_modules/*" ] -} \ No newline at end of file +} diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json deleted file mode 100644 index d537b38d2b1..00000000000 --- a/src/tsconfig.strictNullChecks.json +++ /dev/null @@ -1,742 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "noEmit": true, - "strictNullChecks": true - }, - "include": [ - "./typings", - "./vs/base/common/**/*.ts" - ], - "files": [ - "./vs/base/browser/browser.ts", - "./vs/base/browser/dnd.ts", - "./vs/base/browser/dom.ts", - "./vs/base/browser/event.ts", - "./vs/base/browser/fastDomNode.ts", - "./vs/base/browser/globalMouseMoveMonitor.ts", - "./vs/base/browser/history.ts", - "./vs/base/browser/htmlContentRenderer.ts", - "./vs/base/browser/iframe.ts", - "./vs/base/browser/keyboardEvent.ts", - "./vs/base/browser/mouseEvent.ts", - "./vs/base/browser/touch.ts", - "./vs/base/browser/ui/aria/aria.ts", - "./vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts", - "./vs/base/browser/ui/button/button.ts", - "./vs/base/browser/ui/centered/centeredViewLayout.ts", - "./vs/base/browser/ui/contextview/contextview.ts", - "./vs/base/browser/ui/countBadge/countBadge.ts", - "./vs/base/browser/ui/grid/grid.ts", - "./vs/base/browser/ui/grid/gridview.ts", - "./vs/base/browser/ui/highlightedlabel/highlightedLabel.ts", - "./vs/base/browser/ui/iconLabel/iconLabel.ts", - "./vs/base/browser/ui/keybindingLabel/keybindingLabel.ts", - "./vs/base/browser/ui/list/list.ts", - "./vs/base/browser/ui/list/listPaging.ts", - "./vs/base/browser/ui/list/listView.ts", - "./vs/base/browser/ui/list/listWidget.ts", - "./vs/base/browser/ui/list/rangeMap.ts", - "./vs/base/browser/ui/list/rowCache.ts", - "./vs/base/browser/ui/list/splice.ts", - "./vs/base/browser/ui/octiconLabel/octiconLabel.mock.ts", - "./vs/base/browser/ui/octiconLabel/octiconLabel.ts", - "./vs/base/browser/ui/progressbar/progressbar.ts", - "./vs/base/browser/ui/sash/sash.ts", - "./vs/base/browser/ui/scrollbar/abstractScrollbar.ts", - "./vs/base/browser/ui/scrollbar/horizontalScrollbar.ts", - "./vs/base/browser/ui/scrollbar/scrollableElement.ts", - "./vs/base/browser/ui/scrollbar/scrollableElementOptions.ts", - "./vs/base/browser/ui/scrollbar/scrollbarArrow.ts", - "./vs/base/browser/ui/scrollbar/scrollbarState.ts", - "./vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts", - "./vs/base/browser/ui/scrollbar/verticalScrollbar.ts", - "./vs/base/browser/ui/splitview/panelview.ts", - "./vs/base/browser/ui/splitview/splitview.ts", - "./vs/base/browser/ui/tree/abstractTree.ts", - "./vs/base/browser/ui/tree/dataTree.ts", - "./vs/base/browser/ui/tree/indexTree.ts", - "./vs/base/browser/ui/tree/indexTreeModel.ts", - "./vs/base/browser/ui/tree/objectTree.ts", - "./vs/base/browser/ui/tree/objectTreeModel.ts", - "./vs/base/browser/ui/tree/tree.ts", - "./vs/base/browser/ui/widget.ts", - "./vs/base/common/json.ts", - "./vs/base/common/jsonEdit.ts", - "./vs/base/common/jsonFormatter.ts", - "./vs/base/common/paths.ts", - "./vs/base/common/uriIpc.ts", - "./vs/base/node/console.ts", - "./vs/base/node/crypto.ts", - "./vs/base/node/decoder.ts", - "./vs/base/node/encoding.ts", - "./vs/base/node/extfs.ts", - "./vs/base/node/flow.ts", - "./vs/base/node/id.ts", - "./vs/base/node/paths.ts", - "./vs/base/node/pfs.ts", - "./vs/base/node/ports.ts", - "./vs/base/node/processes.ts", - "./vs/base/node/proxy.ts", - "./vs/base/node/ps.ts", - "./vs/base/node/request.ts", - "./vs/base/node/stats.ts", - "./vs/base/node/storage.ts", - "./vs/base/node/stream.ts", - "./vs/base/parts/contextmenu/common/contextmenu.ts", - "./vs/base/parts/contextmenu/electron-browser/contextmenu.ts", - "./vs/base/parts/contextmenu/electron-main/contextmenu.ts", - "./vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts", - "./vs/base/parts/ipc/electron-main/ipc.electron-main.ts", - "./vs/base/parts/ipc/node/ipc.cp.ts", - "./vs/base/parts/ipc/node/ipc.electron.ts", - "./vs/base/parts/ipc/node/ipc.net.ts", - "./vs/base/parts/ipc/node/ipc.ts", - "./vs/base/parts/ipc/test/node/testApp.ts", - "./vs/base/parts/ipc/test/node/testService.ts", - "./vs/base/parts/quickopen/common/quickOpen.ts", - "./vs/base/parts/quickopen/common/quickOpenScorer.ts", - "./vs/base/test/browser/ui/grid/util.ts", - "./vs/base/test/common/json.test.ts", - "./vs/base/test/common/jsonEdit.test.ts", - "./vs/base/test/common/jsonFormatter.test.ts", - "./vs/base/test/common/paths.test.ts", - "./vs/base/test/common/utils.ts", - "./vs/base/test/node/processes/fixtures/fork.ts", - "./vs/base/test/node/processes/fixtures/fork_large.ts", - "./vs/base/test/node/uri.test.perf.ts", - "./vs/base/test/node/utils.ts", - "./vs/base/worker/defaultWorkerFactory.ts", - "./vs/base/worker/workerMain.ts", - "./vs/code/code.main.ts", - "./vs/code/electron-browser/issue/issueReporterModel.ts", - "./vs/code/electron-browser/issue/issueReporterPage.ts", - "./vs/code/electron-browser/issue/issueReporterUtil.ts", - "./vs/code/electron-browser/processExplorer/processExplorerMain.ts", - "./vs/code/electron-browser/sharedProcess/contrib/contributions.ts", - "./vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts", - "./vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts", - "./vs/code/electron-main/auth.ts", - "./vs/code/electron-main/keyboard.ts", - "./vs/code/electron-main/logUploader.ts", - "./vs/code/electron-main/sharedProcess.ts", - "./vs/code/electron-main/theme.ts", - "./vs/code/node/cli.ts", - "./vs/code/node/paths.ts", - "./vs/code/node/shellEnv.ts", - "./vs/code/node/wait.ts", - "./vs/code/node/windowsFinder.ts", - "./vs/editor/browser/config/charWidthReader.ts", - "./vs/editor/browser/config/configuration.ts", - "./vs/editor/browser/config/elementSizeObserver.ts", - "./vs/editor/browser/controller/coreCommands.ts", - "./vs/editor/browser/controller/mouseHandler.ts", - "./vs/editor/browser/controller/mouseTarget.ts", - "./vs/editor/browser/controller/pointerHandler.ts", - "./vs/editor/browser/controller/textAreaHandler.ts", - "./vs/editor/browser/controller/textAreaInput.ts", - "./vs/editor/browser/controller/textAreaState.ts", - "./vs/editor/browser/core/editorState.ts", - "./vs/editor/browser/editorBrowser.ts", - "./vs/editor/browser/editorDom.ts", - "./vs/editor/browser/editorExtensions", - "./vs/editor/browser/editorExtensions.ts", - "./vs/editor/browser/services/abstractCodeEditorService.ts", - "./vs/editor/browser/services/bulkEditService.ts", - "./vs/editor/browser/services/codeEditorService.ts", - "./vs/editor/browser/services/codeEditorServiceImpl.ts", - "./vs/editor/browser/services/openerService.ts", - "./vs/editor/browser/view/dynamicViewOverlay.ts", - "./vs/editor/browser/view/viewController.ts", - "./vs/editor/browser/view/viewImpl.ts", - "./vs/editor/browser/view/viewLayer.ts", - "./vs/editor/browser/view/viewOutgoingEvents.ts", - "./vs/editor/browser/view/viewOverlays.ts", - "./vs/editor/browser/view/viewPart.ts", - "./vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts", - "./vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts", - "./vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts", - "./vs/editor/browser/viewParts/decorations/decorations.ts", - "./vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts", - "./vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts", - "./vs/editor/browser/viewParts/indentGuides/indentGuides.ts", - "./vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts", - "./vs/editor/browser/viewParts/lines/rangeUtil.ts", - "./vs/editor/browser/viewParts/lines/viewLine.ts", - "./vs/editor/browser/viewParts/lines/viewLines.ts", - "./vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts", - "./vs/editor/browser/viewParts/margin/margin.ts", - "./vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts", - "./vs/editor/browser/viewParts/minimap/minimap.ts", - "./vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts", - "./vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts", - "./vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts", - "./vs/editor/browser/viewParts/rulers/rulers.ts", - "./vs/editor/browser/viewParts/scrollDecoration/scrollDecoration.ts", - "./vs/editor/browser/viewParts/selections/selections.ts", - "./vs/editor/browser/viewParts/viewCursors/viewCursor.ts", - "./vs/editor/browser/viewParts/viewCursors/viewCursors.ts", - "./vs/editor/browser/viewParts/viewZones/viewZones.ts", - "./vs/editor/browser/widget/codeEditorWidget.ts", - "./vs/editor/browser/widget/diffNavigator.ts", - "./vs/editor/common/commands/replaceCommand.ts", - "./vs/editor/common/commands/shiftCommand.ts", - "./vs/editor/common/commands/surroundSelectionCommand.ts", - "./vs/editor/common/commands/trimTrailingWhitespaceCommand.ts", - "./vs/editor/common/config/commonEditorConfig.ts", - "./vs/editor/common/config/editorOptions.ts", - "./vs/editor/common/config/editorZoom.ts", - "./vs/editor/common/config/fontInfo.ts", - "./vs/editor/common/controller/cursor.ts", - "./vs/editor/common/controller/cursorCollection.ts", - "./vs/editor/common/controller/cursorColumnSelection.ts", - "./vs/editor/common/controller/cursorCommon.ts", - "./vs/editor/common/controller/cursorDeleteOperations.ts", - "./vs/editor/common/controller/cursorEvents.ts", - "./vs/editor/common/controller/cursorMoveCommands.ts", - "./vs/editor/common/controller/cursorMoveOperations.ts", - "./vs/editor/common/controller/cursorTypeOperations.ts", - "./vs/editor/common/controller/cursorWordOperations.ts", - "./vs/editor/common/controller/oneCursor.ts", - "./vs/editor/common/controller/wordCharacterClassifier.ts", - "./vs/editor/common/core/characterClassifier.ts", - "./vs/editor/common/core/editOperation.ts", - "./vs/editor/common/core/lineTokens.ts", - "./vs/editor/common/core/position.ts", - "./vs/editor/common/core/range.ts", - "./vs/editor/common/core/rgba.ts", - "./vs/editor/common/core/selection.ts", - "./vs/editor/common/core/stringBuilder.ts", - "./vs/editor/common/core/token.ts", - "./vs/editor/common/core/uint.ts", - "./vs/editor/common/diff/diffComputer.ts", - "./vs/editor/common/editorAction.ts", - "./vs/editor/common/editorCommon.ts", - "./vs/editor/common/editorContextKeys.ts", - "./vs/editor/common/model.ts", - "./vs/editor/common/model/editStack.ts", - "./vs/editor/common/model/indentationGuesser.ts", - "./vs/editor/common/model/intervalTree.ts", - "./vs/editor/common/model/mirrorTextModel.ts", - "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts", - "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts", - "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts", - "./vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts", - "./vs/editor/common/model/textModel.ts", - "./vs/editor/common/model/textModelEvents.ts", - "./vs/editor/common/model/textModelSearch.ts", - "./vs/editor/common/model/textModelTokens.ts", - "./vs/editor/common/model/wordHelper.ts", - "./vs/editor/common/modes.ts", - "./vs/editor/common/modes/abstractMode.ts", - "./vs/editor/common/modes/languageConfiguration.ts", - "./vs/editor/common/modes/languageConfigurationRegistry.ts", - "./vs/editor/common/modes/languageFeatureRegistry.ts", - "./vs/editor/common/modes/languageSelector.ts", - "./vs/editor/common/modes/linkComputer.ts", - "./vs/editor/common/modes/modesRegistry.ts", - "./vs/editor/common/modes/nullMode.ts", - "./vs/editor/common/modes/supports.ts", - "./vs/editor/common/modes/supports/characterPair.ts", - "./vs/editor/common/modes/supports/electricCharacter.ts", - "./vs/editor/common/modes/supports/indentRules.ts", - "./vs/editor/common/modes/supports/inplaceReplaceSupport.ts", - "./vs/editor/common/modes/supports/onEnter.ts", - "./vs/editor/common/modes/supports/richEditBrackets.ts", - "./vs/editor/common/modes/supports/tokenization.ts", - "./vs/editor/common/modes/textToHtmlTokenizer.ts", - "./vs/editor/common/modes/tokenizationRegistry.ts", - "./vs/editor/common/services/editorSimpleWorker.ts", - "./vs/editor/common/services/editorWorkerService.ts", - "./vs/editor/common/services/editorWorkerServiceImpl.ts", - "./vs/editor/common/services/getIconClasses.ts", - "./vs/editor/common/services/languagesRegistry.ts", - "./vs/editor/common/services/modeService.ts", - "./vs/editor/common/services/modeServiceImpl.ts", - "./vs/editor/common/services/modelService.ts", - "./vs/editor/common/services/modelServiceImpl.ts", - "./vs/editor/common/services/resolverService.ts", - "./vs/editor/common/services/resourceConfiguration.ts", - "./vs/editor/common/services/resourceConfigurationImpl.ts", - "./vs/editor/common/services/webWorker.ts", - "./vs/editor/common/standalone/standaloneBase.ts", - "./vs/editor/common/standalone/standaloneEnums.ts", - "./vs/editor/common/view/editorColorRegistry.ts", - "./vs/editor/common/view/minimapCharRenderer.ts", - "./vs/editor/common/view/overviewZoneManager.ts", - "./vs/editor/common/view/renderingContext.ts", - "./vs/editor/common/view/runtimeMinimapCharRenderer.ts", - "./vs/editor/common/view/viewContext.ts", - "./vs/editor/common/view/viewEventDispatcher.ts", - "./vs/editor/common/view/viewEvents.ts", - "./vs/editor/common/viewLayout/lineDecorations.ts", - "./vs/editor/common/viewLayout/linesLayout.ts", - "./vs/editor/common/viewLayout/viewLayout.ts", - "./vs/editor/common/viewLayout/viewLineRenderer.ts", - "./vs/editor/common/viewLayout/viewLinesViewportData.ts", - "./vs/editor/common/viewLayout/whitespaceComputer.ts", - "./vs/editor/common/viewModel/characterHardWrappingLineMapper.ts", - "./vs/editor/common/viewModel/prefixSumComputer.ts", - "./vs/editor/common/viewModel/splitLinesCollection.ts", - "./vs/editor/common/viewModel/viewEventHandler.ts", - "./vs/editor/common/viewModel/viewModel.ts", - "./vs/editor/common/viewModel/viewModelDecorations.ts", - "./vs/editor/common/viewModel/viewModelImpl.ts", - "./vs/editor/contrib/bracketMatching/bracketMatching.ts", - "./vs/editor/contrib/caretOperations/caretOperations.ts", - "./vs/editor/contrib/caretOperations/moveCaretCommand.ts", - "./vs/editor/contrib/caretOperations/transpose.ts", - "./vs/editor/contrib/clipboard/clipboard.ts", - "./vs/editor/contrib/codeAction/codeAction.ts", - "./vs/editor/contrib/codeAction/codeActionModel.ts", - "./vs/editor/contrib/codeAction/codeActionTrigger.ts", - "./vs/editor/contrib/codeAction/lightBulbWidget.ts", - "./vs/editor/contrib/codelens/codelens.ts", - "./vs/editor/contrib/colorPicker/color.ts", - "./vs/editor/contrib/colorPicker/colorDetector.ts", - "./vs/editor/contrib/colorPicker/colorPickerModel.ts", - "./vs/editor/contrib/colorPicker/colorPickerWidget.ts", - "./vs/editor/contrib/comment/blockCommentCommand.ts", - "./vs/editor/contrib/comment/comment.ts", - "./vs/editor/contrib/comment/lineCommentCommand.ts", - "./vs/editor/contrib/cursorUndo/cursorUndo.ts", - "./vs/editor/contrib/dnd/dnd.ts", - "./vs/editor/contrib/dnd/dragAndDropCommand.ts", - "./vs/editor/contrib/find/findDecorations.ts", - "./vs/editor/contrib/find/findModel.ts", - "./vs/editor/contrib/find/findState.ts", - "./vs/editor/contrib/find/replaceAllCommand.ts", - "./vs/editor/contrib/find/replacePattern.ts", - "./vs/editor/contrib/folding/folding.ts", - "./vs/editor/contrib/folding/foldingDecorations.ts", - "./vs/editor/contrib/folding/foldingModel.ts", - "./vs/editor/contrib/folding/foldingRanges.ts", - "./vs/editor/contrib/folding/hiddenRangeModel.ts", - "./vs/editor/contrib/folding/indentRangeProvider.ts", - "./vs/editor/contrib/folding/intializingRangeProvider.ts", - "./vs/editor/contrib/folding/syntaxRangeProvider.ts", - "./vs/editor/contrib/folding/test/foldingModel.test.ts", - "./vs/editor/contrib/folding/test/foldingRanges.test.ts", - "./vs/editor/contrib/folding/test/hiddenRangeModel.test.ts", - "./vs/editor/contrib/folding/test/indentFold.test.ts", - "./vs/editor/contrib/folding/test/indentRangeProvider.test.ts", - "./vs/editor/contrib/folding/test/syntaxFold.test.ts", - "./vs/editor/contrib/fontZoom/fontZoom.ts", - "./vs/editor/contrib/format/format.ts", - "./vs/editor/contrib/format/formattingEdit.ts", - "./vs/editor/contrib/goToDefinition/clickLinkGesture.ts", - "./vs/editor/contrib/goToDefinition/goToDefinition.ts", - "./vs/editor/contrib/gotoError/gotoError.ts", - "./vs/editor/contrib/gotoError/gotoErrorWidget.ts", - "./vs/editor/contrib/hover/getHover.ts", - "./vs/editor/contrib/hover/hoverOperation.ts", - "./vs/editor/contrib/hover/hoverWidgets.ts", - "./vs/editor/contrib/hover/modesGlyphHover.ts", - "./vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts", - "./vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts", - "./vs/editor/contrib/indentation/indentUtils.ts", - "./vs/editor/contrib/indentation/indentation.ts", - "./vs/editor/contrib/linesOperations/copyLinesCommand.ts", - "./vs/editor/contrib/linesOperations/deleteLinesCommand.ts", - "./vs/editor/contrib/linesOperations/linesOperations.ts", - "./vs/editor/contrib/linesOperations/moveLinesCommand.ts", - "./vs/editor/contrib/linesOperations/sortLinesCommand.ts", - "./vs/editor/contrib/links/getLinks.ts", - "./vs/editor/contrib/links/links.ts", - "./vs/editor/contrib/markdown/markdownRenderer.ts", - "./vs/editor/contrib/message/messageController.ts", - "./vs/editor/contrib/parameterHints/parameterHints.ts", - "./vs/editor/contrib/parameterHints/parameterHintsWidget.ts", - "./vs/editor/contrib/parameterHints/provideSignatureHelp.ts", - "./vs/editor/contrib/quickOpen/quickOpen.ts", - "./vs/editor/contrib/referenceSearch/referencesModel.ts", - "./vs/editor/contrib/rename/rename.ts", - "./vs/editor/contrib/rename/renameInputField.ts", - "./vs/editor/contrib/smartSelect/smartSelect.ts", - "./vs/editor/contrib/smartSelect/tokenSelectionSupport.ts", - "./vs/editor/contrib/smartSelect/tokenTree.ts", - "./vs/editor/contrib/snippet/snippetParser.ts", - "./vs/editor/contrib/snippet/snippetVariables.ts", - "./vs/editor/contrib/suggest/suggest.ts", - "./vs/editor/contrib/suggest/wordContextKey.ts", - "./vs/editor/contrib/suggest/wordDistance.ts", - "./vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts", - "./vs/editor/contrib/wordHighlighter/wordHighlighter.ts", - "./vs/editor/contrib/wordOperations/test/wordTestUtils.ts", - "./vs/editor/contrib/wordOperations/wordOperations.ts", - "./vs/editor/contrib/wordPartOperations/wordPartOperations.ts", - "./vs/editor/contrib/zoneWidget/zoneWidget.ts", - "./vs/editor/editor.worker.ts", - "./vs/editor/standalone/browser/colorizer.ts", - "./vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts", - "./vs/editor/standalone/browser/inspectTokens/inspectTokens.ts", - "./vs/editor/standalone/browser/standaloneCodeServiceImpl.ts", - "./vs/editor/standalone/browser/standaloneThemeServiceImpl.ts", - "./vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts", - "./vs/editor/standalone/common/monarch/monarchCommon.ts", - "./vs/editor/standalone/common/monarch/monarchCompile.ts", - "./vs/editor/standalone/common/monarch/monarchTypes.ts", - "./vs/editor/standalone/common/standaloneThemeService.ts", - "./vs/editor/standalone/common/themes.ts", - "./vs/editor/test/browser/controller/imeTester.ts", - "./vs/editor/test/browser/editorTestServices.ts", - "./vs/editor/test/browser/testCodeEditor.ts", - "./vs/editor/test/browser/testCommand.ts", - "./vs/editor/test/browser/view/minimapFontCreator.ts", - "./vs/editor/test/common/commentMode.ts", - "./vs/editor/test/common/core/viewLineToken.ts", - "./vs/editor/test/common/editorTestUtils.ts", - "./vs/editor/test/common/mocks/mockMode.ts", - "./vs/editor/test/common/mocks/testConfiguration.ts", - "./vs/editor/test/common/model/benchmark/benchmarkUtils.ts", - "./vs/editor/test/common/model/benchmark/entry.ts", - "./vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts", - "./vs/editor/test/common/model/benchmark/operations.benchmark.ts", - "./vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts", - "./vs/editor/test/common/model/editableTextModelTestUtils.ts", - "./vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts", - "./vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts", - "./vs/editor/test/common/modesTestUtils.ts", - "./vs/editor/test/common/view/minimapCharRendererFactory.ts", - "./vs/editor/test/common/viewModel/testViewModel.ts", - "./vs/monaco.d.ts", - "./vs/nls.d.ts", - "./vs/nls.mock.ts", - "./vs/platform/actions/common/actions.ts", - "./vs/platform/actions/common/menu.ts", - "./vs/platform/actions/common/menuService.ts", - "./vs/platform/backup/common/backup.ts", - "./vs/platform/backup/electron-main/backupMainService.ts", - "./vs/platform/broadcast/electron-browser/broadcastService.ts", - "./vs/platform/clipboard/common/clipboardService.ts", - "./vs/platform/clipboard/electron-browser/clipboardService.ts", - "./vs/platform/commands/common/commands.ts", - "./vs/platform/configuration/common/configuration.ts", - "./vs/platform/configuration/common/configurationModels.ts", - "./vs/platform/configuration/common/configurationRegistry.ts", - "./vs/platform/contextkey/browser/contextKeyService.ts", - "./vs/platform/contextkey/common/contextkey.ts", - "./vs/platform/diagnostics/electron-main/diagnosticsService.ts", - "./vs/platform/dialogs/common/dialogs.ts", - "./vs/platform/dialogs/node/dialogIpc.ts", - "./vs/platform/dialogs/node/dialogService.ts", - "./vs/platform/download/common/download.ts", - "./vs/platform/download/node/downloadIpc.ts", - "./vs/platform/download/node/downloadService.ts", - "./vs/platform/driver/electron-main/driver.ts", - "./vs/platform/driver/node/driver.ts", - "./vs/platform/editor/common/editor.ts", - "./vs/platform/environment/common/environment.ts", - "./vs/platform/environment/node/argv.ts", - "./vs/platform/environment/node/environmentService.ts", - "./vs/platform/extensionManagement/common/extensionEnablementService.ts", - "./vs/platform/extensionManagement/common/extensionManagement.ts", - "./vs/platform/extensionManagement/common/extensionManagementUtil.ts", - "./vs/platform/extensionManagement/common/extensionNls.ts", - "./vs/platform/extensionManagement/node/extensionLifecycle.ts", - "./vs/platform/extensionManagement/node/extensionManagementIpc.ts", - "./vs/platform/extensionManagement/node/extensionManagementUtil.ts", - "./vs/platform/extensionManagement/node/extensionsManifestCache.ts", - "./vs/platform/extensions/common/extensionHost.ts", - "./vs/platform/extensions/common/extensions.ts", - "./vs/platform/extensions/node/extensionValidator.ts", - "./vs/platform/files/common/files.ts", - "./vs/platform/files/node/files.ts", - "./vs/platform/history/common/history.ts", - "./vs/platform/history/electron-main/historyMainService.ts", - "./vs/platform/instantiation/common/descriptors.ts", - "./vs/platform/instantiation/common/extensions.ts", - "./vs/platform/instantiation/common/graph.ts", - "./vs/platform/instantiation/common/instantiation.ts", - "./vs/platform/instantiation/common/instantiationService.ts", - "./vs/platform/instantiation/common/serviceCollection.ts", - "./vs/platform/instantiation/node/instantiationService.ts", - "./vs/platform/integrity/common/integrity.ts", - "./vs/platform/integrity/node/integrityServiceImpl.ts", - "./vs/platform/issue/common/issue.ts", - "./vs/platform/issue/electron-main/issueService.ts", - "./vs/platform/issue/node/issueIpc.ts", - "./vs/platform/jsonschemas/common/jsonContributionRegistry.ts", - "./vs/platform/keybinding/common/abstractKeybindingService.ts", - "./vs/platform/keybinding/common/keybinding.ts", - "./vs/platform/keybinding/common/keybindingResolver.ts", - "./vs/platform/keybinding/common/keybindingsRegistry.ts", - "./vs/platform/keybinding/common/resolvedKeybindingItem.ts", - "./vs/platform/keybinding/common/usLayoutResolvedKeybinding.ts", - "./vs/platform/keybinding/test/common/mockKeybindingService.ts", - "./vs/platform/label/common/label.ts", - "./vs/platform/label/electron-browser/label.contribution.ts", - "./vs/platform/launch/electron-main/launchService.ts", - "./vs/platform/lifecycle/common/lifecycle.ts", - "./vs/platform/lifecycle/electron-browser/lifecycleService.ts", - "./vs/platform/lifecycle/electron-main/lifecycleMain.ts", - "./vs/platform/localizations/common/localizations.ts", - "./vs/platform/localizations/node/localizationsIpc.ts", - "./vs/platform/log/common/bufferLog.ts", - "./vs/platform/log/common/log.ts", - "./vs/platform/log/node/logIpc.ts", - "./vs/platform/log/node/spdlogService.ts", - "./vs/platform/markers/common/markerService.ts", - "./vs/platform/markers/common/markers.ts", - "./vs/platform/menubar/common/menubar.ts", - "./vs/platform/menubar/node/menubarIpc.ts", - "./vs/platform/node/minimalTranslations.ts", - "./vs/platform/node/package.ts", - "./vs/platform/node/product.ts", - "./vs/platform/node/zip.ts", - "./vs/platform/notification/common/notification.ts", - "./vs/platform/notification/test/common/testNotificationService.ts", - "./vs/platform/opener/common/opener.ts", - "./vs/platform/output/node/outputAppender.ts", - "./vs/platform/progress/common/progress.ts", - "./vs/platform/quickOpen/common/quickOpen.ts", - "./vs/platform/quickinput/common/quickInput.ts", - "./vs/platform/registry/common/platform.ts", - "./vs/platform/remote/common/remoteAuthorityResolver.ts", - "./vs/platform/remote/common/remoteHosts.ts", - "./vs/platform/remote/node/remoteAgentConnection.ts", - "./vs/platform/remote/node/remoteAgentFileSystemChannel.ts", - "./vs/platform/remote/node/remoteAuthorityResolverChannel.ts", - "./vs/platform/remote/node/remoteAuthorityResolverService.ts", - "./vs/platform/request/electron-browser/requestService.ts", - "./vs/platform/request/electron-main/requestService.ts", - "./vs/platform/request/node/request.ts", - "./vs/platform/request/node/requestService.ts", - "./vs/platform/search/common/replace.ts", - "./vs/platform/search/common/search.ts", - "./vs/platform/search/test/common/replace.test.ts", - "./vs/platform/state/common/state.ts", - "./vs/platform/state/node/stateService.ts", - "./vs/platform/statusbar/common/statusbar.ts", - "./vs/platform/storage/common/storage.ts", - "./vs/platform/storage/common/storageLegacyService.ts", - "./vs/platform/storage/node/storageService.ts", - "./vs/platform/telemetry/browser/errorTelemetry.ts", - "./vs/platform/telemetry/common/telemetry.ts", - "./vs/platform/telemetry/common/telemetryService.ts", - "./vs/platform/telemetry/common/telemetryUtils.ts", - "./vs/platform/telemetry/node/commonProperties.ts", - "./vs/platform/telemetry/node/telemetryIpc.ts", - "./vs/platform/telemetry/node/telemetryNodeUtils.ts", - "./vs/platform/telemetry/node/workbenchCommonProperties.ts", - "./vs/platform/theme/common/colorRegistry.ts", - "./vs/platform/theme/common/styler.ts", - "./vs/platform/theme/common/themeService.ts", - "./vs/platform/theme/test/common/testThemeService.ts", - "./vs/platform/update/common/update.ts", - "./vs/platform/update/electron-main/abstractUpdateService.ts", - "./vs/platform/update/electron-main/updateService.darwin.ts", - "./vs/platform/update/electron-main/updateService.linux.ts", - "./vs/platform/update/electron-main/updateService.snap.ts", - "./vs/platform/update/node/update.config.contribution.ts", - "./vs/platform/update/node/updateIpc.ts", - "./vs/platform/url/common/url.ts", - "./vs/platform/url/common/urlService.ts", - "./vs/platform/url/electron-main/electronUrlListener.ts", - "./vs/platform/url/node/urlIpc.ts", - "./vs/platform/widget/common/contextScopedWidget.ts", - "./vs/platform/windows/common/windows.ts", - "./vs/platform/windows/electron-browser/windowService.ts", - "./vs/platform/windows/electron-main/windows.ts", - "./vs/platform/windows/node/windowsIpc.ts", - "./vs/platform/workbench/common/contextkeys.ts", - "./vs/platform/workspace/common/workspace.ts", - "./vs/platform/workspace/test/common/testWorkspace.ts", - "./vs/platform/workspaces/common/workspaces.ts", - "./vs/platform/workspaces/electron-main/workspacesMainService.ts", - "./vs/platform/workspaces/node/workspaces.ts", - "./vs/platform/workspaces/node/workspacesIpc.ts", - "./vs/vscode.d.ts", - "./vs/vscode.proposed.d.ts", - "./vs/workbench/api/node/extHostExtensionActivator.ts", - "./vs/workbench/api/shared/tasks.ts", - "./vs/workbench/browser/actions/toggleActivityBarVisibility.ts", - "./vs/workbench/browser/actions/toggleCenteredLayout.ts", - "./vs/workbench/browser/actions/toggleSidebarPosition.ts", - "./vs/workbench/browser/actions/toggleSidebarVisibility.ts", - "./vs/workbench/browser/actions/toggleStatusbarVisibility.ts", - "./vs/workbench/browser/actions/toggleTabsVisibility.ts", - "./vs/workbench/browser/actions/toggleZenMode.ts", - "./vs/workbench/browser/part.ts", - "./vs/workbench/browser/parts/editor/editorWidgets.ts", - "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", - "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", - "./vs/workbench/browser/parts/quickopen/quickopen.ts", - "./vs/workbench/browser/parts/statusbar/statusbar.ts", - "./vs/workbench/common/actions.ts", - "./vs/workbench/common/activity.ts", - "./vs/workbench/common/component.ts", - "./vs/workbench/common/composite.ts", - "./vs/workbench/common/contributions.ts", - "./vs/workbench/common/extensionHostProtocol.ts", - "./vs/workbench/common/memento.ts", - "./vs/workbench/common/notifications.ts", - "./vs/workbench/common/panel.ts", - "./vs/workbench/common/resources.ts", - "./vs/workbench/common/theme.ts", - "./vs/workbench/common/viewlet.ts", - "./vs/workbench/common/views.ts", - "./vs/workbench/parts/cli/electron-browser/cli.contribution.ts", - "./vs/workbench/parts/codeEditor/browser/menuPreventer.ts", - "./vs/workbench/parts/codeEditor/browser/simpleEditorOptions.ts", - "./vs/workbench/parts/codeEditor/electron-browser/accessibility.ts", - "./vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts", - "./vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts", - "./vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts", - "./vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts", - "./vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts", - "./vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts", - "./vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts", - "./vs/workbench/parts/comments/common/commentModel.ts", - "./vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts", - "./vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts", - "./vs/workbench/parts/emmet/browser/emmet.browser.contribution.ts", - "./vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts", - "./vs/workbench/parts/emmet/electron-browser/emmet.contribution.ts", - "./vs/workbench/parts/emmet/electron-browser/emmetActions.ts", - "./vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts", - "./vs/workbench/parts/execution/common/execution.ts", - "./vs/workbench/parts/execution/electron-browser/terminal.ts", - "./vs/workbench/parts/execution/electron-browser/terminalService.ts", - "./vs/workbench/parts/extensions/common/extensionQuery.ts", - "./vs/workbench/parts/extensions/common/extensions.ts", - "./vs/workbench/parts/extensions/common/extensionsFileTemplate.ts", - "./vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts", - "./vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts", - "./vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts", - "./vs/workbench/parts/logs/common/logConstants.ts", - "./vs/workbench/parts/logs/electron-browser/logs.contribution.ts", - "./vs/workbench/parts/logs/electron-browser/logsActions.ts", - "./vs/workbench/parts/markers/electron-browser/constants.ts", - "./vs/workbench/parts/markers/electron-browser/markers.ts", - "./vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts", - "./vs/workbench/parts/markers/electron-browser/markersFilterOptions.ts", - "./vs/workbench/parts/markers/electron-browser/markersModel.ts", - "./vs/workbench/parts/markers/electron-browser/messages.ts", - "./vs/workbench/parts/outline/electron-browser/outline.ts", - "./vs/workbench/parts/output/common/output.ts", - "./vs/workbench/parts/output/common/outputLinkComputer.ts", - "./vs/workbench/parts/output/common/outputLinkProvider.ts", - "./vs/workbench/parts/performance/electron-browser/stats.ts", - "./vs/workbench/parts/preferences/common/smartSnippetInserter.ts", - "./vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts", - "./vs/workbench/parts/scm/common/scm.ts", - "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", - "./vs/workbench/parts/search/common/constants.ts", - "./vs/workbench/parts/search/common/queryBuilder.ts", - "./vs/workbench/parts/snippets/electron-browser/configureSnippets.ts", - "./vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts", - "./vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts", - "./vs/workbench/parts/snippets/electron-browser/snippetsFile.ts", - "./vs/workbench/parts/surveys/electron-browser/nps.contribution.ts", - "./vs/workbench/parts/tasks/common/problemCollectors.ts", - "./vs/workbench/parts/tasks/common/problemMatcher.ts", - "./vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts", - "./vs/workbench/parts/tasks/common/taskService.ts", - "./vs/workbench/parts/tasks/common/taskSystem.ts", - "./vs/workbench/parts/tasks/common/taskTemplates.ts", - "./vs/workbench/parts/tasks/common/tasks.ts", - "./vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts", - "./vs/workbench/parts/tasks/node/tasks.ts", - "./vs/workbench/parts/terminal/browser/terminalTab.ts", - "./vs/workbench/parts/terminal/browser/terminalWidgetManager.ts", - "./vs/workbench/parts/terminal/common/terminal.ts", - "./vs/workbench/parts/terminal/common/terminalColorRegistry.ts", - "./vs/workbench/parts/terminal/common/terminalCommands.ts", - "./vs/workbench/parts/terminal/common/terminalMenu.ts", - "./vs/workbench/parts/terminal/common/terminalService.ts", - "./vs/workbench/parts/terminal/node/terminal.ts", - "./vs/workbench/parts/terminal/node/terminalCommandTracker.ts", - "./vs/workbench/parts/terminal/node/terminalEnvironment.ts", - "./vs/workbench/parts/terminal/node/terminalProcess.ts", - "./vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts", - "./vs/workbench/parts/terminal/node/windowsShellHelper.ts", - "./vs/workbench/parts/url/electron-browser/url.contribution.ts", - "./vs/workbench/parts/webview/electron-browser/webviewProtocols.ts", - "./vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts", - "./vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts", - "./vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts", - "./vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts", - "./vs/workbench/services/activity/common/activity.ts", - "./vs/workbench/services/backup/common/backup.ts", - "./vs/workbench/services/backup/node/backupFileService.ts", - "./vs/workbench/services/commands/common/commandService.ts", - "./vs/workbench/services/configuration/common/configuration.ts", - "./vs/workbench/services/configuration/common/configurationExtensionPoint.ts", - "./vs/workbench/services/configuration/common/configurationModels.ts", - "./vs/workbench/services/configuration/common/jsonEditing.ts", - "./vs/workbench/services/configurationResolver/common/configurationResolver.ts", - "./vs/workbench/services/configurationResolver/common/configurationResolverUtils.ts", - "./vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts", - "./vs/workbench/services/decorations/browser/decorations.ts", - "./vs/workbench/services/decorations/browser/decorationsService.ts", - "./vs/workbench/services/extensions/common/extensions.ts", - "./vs/workbench/services/extensions/common/extensionsRegistry.ts", - "./vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts", - "./vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts", - "./vs/workbench/services/extensions/node/extensionManagementServerService.ts", - "./vs/workbench/services/extensions/node/lazyPromise.ts", - "./vs/workbench/services/extensions/node/proxyIdentifier.ts", - "./vs/workbench/services/extensions/node/rpcProtocol.ts", - "./vs/workbench/services/files/electron-browser/encoding.ts", - "./vs/workbench/services/files/node/watcher/common.ts", - "./vs/workbench/services/files/node/watcher/nsfw/watcher.ts", - "./vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts", - "./vs/workbench/services/files/node/watcher/nsfw/watcherService.ts", - "./vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts", - "./vs/workbench/services/files/node/watcher/unix/watcher.ts", - "./vs/workbench/services/files/node/watcher/unix/watcherApp.ts", - "./vs/workbench/services/files/node/watcher/unix/watcherIpc.ts", - "./vs/workbench/services/files/node/watcher/unix/watcherService.ts", - "./vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts", - "./vs/workbench/services/files/node/watcher/win32/watcherService.ts", - "./vs/workbench/services/files/test/electron-browser/utils.ts", - "./vs/workbench/services/hash/common/hashService.ts", - "./vs/workbench/services/hash/node/hashService.ts", - "./vs/workbench/services/issue/common/issue.ts", - "./vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts", - "./vs/workbench/services/keybinding/common/keybindingIO.ts", - "./vs/workbench/services/keybinding/common/keyboardMapper.ts", - "./vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts", - "./vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts", - "./vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts", - "./vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts", - "./vs/workbench/services/mode/common/workbenchModeService.ts", - "./vs/workbench/services/notification/common/notificationService.ts", - "./vs/workbench/services/panel/common/panelService.ts", - "./vs/workbench/services/part/common/partService.ts", - "./vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts", - "./vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts", - "./vs/workbench/services/remote/node/remoteAgentService.ts", - "./vs/workbench/services/scm/common/scm.ts", - "./vs/workbench/services/scm/common/scmService.ts", - "./vs/workbench/services/search/common/searchHelpers.ts", - "./vs/workbench/services/search/node/fileSearchManager.ts", - "./vs/workbench/services/search/node/legacy/search.ts", - "./vs/workbench/services/search/node/ripgrepFileSearch.ts", - "./vs/workbench/services/search/node/ripgrepSearchProvider.ts", - "./vs/workbench/services/search/node/ripgrepSearchUtils.ts", - "./vs/workbench/services/search/node/ripgrepTextSearchEngine.ts", - "./vs/workbench/services/search/node/search.ts", - "./vs/workbench/services/search/node/searchHistoryService.ts", - "./vs/workbench/services/search/node/searchIpc.ts", - "./vs/workbench/services/search/node/textSearchAdapter.ts", - "./vs/workbench/services/search/node/textSearchManager.ts", - "./vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts", - "./vs/workbench/services/search/test/node/textSearchManager.test.ts", - "./vs/workbench/services/textMate/electron-browser/TMGrammars.ts", - "./vs/workbench/services/textMate/electron-browser/TMHelper.ts", - "./vs/workbench/services/textMate/electron-browser/textMateService.ts", - "./vs/workbench/services/textfile/electron-browser/textResourcePropertiesService.ts", - "./vs/workbench/services/themes/common/colorExtensionPoint.ts", - "./vs/workbench/services/themes/common/colorThemeSchema.ts", - "./vs/workbench/services/themes/common/fileIconThemeSchema.ts", - "./vs/workbench/services/themes/common/workbenchThemeService.ts", - "./vs/workbench/services/title/common/titleService.ts", - "./vs/workbench/services/workspace/common/workspaceEditing.ts", - "./vs/workbench/test/electron-browser/api/mock.ts" - ], - "exclude": [ - "./typings/require-monaco.d.ts" - ] -} \ No newline at end of file diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 607eeb388c9..20e58bcd730 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,5 +1,5 @@ -// Type definitions for Electron 2.0.5 -// Project: http://electron.atom.io/ +// Type definitions for Electron 3.1.8 +// Project: http://electronjs.org/ // Definitions by: The Electron Team <https://github.com/electron/electron> // Definitions: https://github.com/electron/electron-typescript-definitions @@ -64,6 +64,7 @@ declare namespace Electron { Menu: typeof Menu; MenuItem: typeof MenuItem; net: Net; + netLog: NetLog; Notification: typeof Notification; powerMonitor: PowerMonitor; powerSaveBlocker: PowerSaveBlocker; @@ -85,7 +86,7 @@ declare namespace Electron { webviewTag: WebviewTag; } - interface AllElectron extends MainInterface, RendererInterface { } + interface AllElectron extends MainInterface, RendererInterface {} const app: App; const autoUpdater: AutoUpdater; @@ -101,6 +102,7 @@ declare namespace Electron { type nativeImage = NativeImage; const nativeImage: typeof NativeImage; const net: Net; + const netLog: NetLog; const powerMonitor: PowerMonitor; const powerSaveBlocker: PowerSaveBlocker; const protocol: Protocol; @@ -470,6 +472,50 @@ declare namespace Electron { once(event: 'ready', listener: (launchInfo: any) => void): this; addListener(event: 'ready', listener: (launchInfo: any) => void): this; removeListener(event: 'ready', listener: (launchInfo: any) => void): this; + /** + * This event will be emitted inside the primary instance of your application when + * a second instance has been executed. argv is an Array of the second instance's + * command line arguments, and workingDirectory is its current working directory. + * Usually applications respond to this by making their primary window focused and + * non-minimized. This event is guaranteed to be emitted after the ready event of + * app gets emitted. + */ + on(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; + once(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; + addListener(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; + removeListener(event: 'second-instance', listener: (event: Event, + /** + * An array of the second instance's command line arguments + */ + argv: string[], + /** + * The second instance's working directory + */ + workingDirectory: string) => void): this; /** * Emitted when a client certificate is requested. The url corresponds to the * navigation entry requesting the client certificate and callback can be called @@ -496,6 +542,13 @@ declare namespace Electron { url: string, certificateList: Certificate[], callback: (certificate?: Certificate) => void) => void): this; + /** + * Emitted when Electron has created a new session. + */ + on(event: 'session-created', listener: (session: Session) => void): this; + once(event: 'session-created', listener: (session: Session) => void): this; + addListener(event: 'session-created', listener: (session: Session) => void): this; + removeListener(event: 'session-created', listener: (session: Session) => 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() @@ -581,7 +634,7 @@ declare namespace Electron { * event represents the applicationWillFinishLaunching notification of * NSApplication. You would usually set up listeners for the open-file and open-url * events here, and start the crash reporter and auto updater. In most cases, you - * should just do everything in the ready event handler. + * should do everything in the ready event handler. */ on(event: 'will-finish-launching', listener: Function): this; once(event: 'will-finish-launching', listener: Function): this; @@ -656,12 +709,12 @@ declare namespace Electron { * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, options: FileIconOptions, 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, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; getGPUFeatureStatus(): GPUFeatureStatus; getJumpListSettings(): JumpListSettings; /** @@ -688,6 +741,12 @@ declare namespace Electron { */ getPath(name: string): string; getVersion(): string; + /** + * This method returns whether or not this instance of your app is currently + * holding the single instance lock. You can request the lock with + * app.requestSingleInstanceLock() and release with app.releaseSingleInstanceLock() + */ + hasSingleInstanceLock(): boolean; /** * Hides all application windows without minimizing them. */ @@ -717,27 +776,6 @@ declare namespace Electron { isInApplicationsFolder(): boolean; isReady(): boolean; isUnityRunning(): boolean; - /** - * This method makes your application a Single Instance Application - instead of - * allowing multiple instances of your app to run, this will ensure that only a - * single instance of your app is running, and other instances signal this instance - * and exit. callback will be called by the first instance with callback(argv, - * workingDirectory) when a second instance has been executed. argv is an Array of - * the second instance's command line arguments, and workingDirectory is its - * current working directory. Usually applications respond to this by making their - * primary window focused and non-minimized. The callback is guaranteed to be - * executed after the ready event of app gets emitted. This method returns false if - * your process is the primary instance of the application and your app should - * continue loading. And returns true if your process has sent its parameters to - * another instance, and you should immediately quit. On macOS the system enforces - * single instance automatically when users try to open a second instance of your - * app in Finder, and the open-file and open-url events will be emitted for that. - * However when users start your app in command line the system's single instance - * mechanism will be bypassed and you have to use this method to ensure single - * instance. An example of activating the window of primary instance when a second - * instance starts: - */ - 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 @@ -770,15 +808,34 @@ declare namespace Electron { */ relaunch(options?: RelaunchOptions): void; /** - * Releases all locks that were created by makeSingleInstance. This will allow - * multiple instances of the application to once again run side by side. + * Releases all locks that were created by requestSingleInstanceLock. This will + * allow multiple instances of the application to once again run side by side. */ - releaseSingleInstance(): void; + releaseSingleInstanceLock(): void; /** * This method checks if the current executable as the default handler for a * protocol (aka URI scheme). If so, it will remove the app as the default handler. */ removeAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + /** + * This method makes your application a Single Instance Application - instead of + * allowing multiple instances of your app to run, this will ensure that only a + * single instance of your app is running, and other instances signal this instance + * and exit. The return value of this method indicates whether or not this instance + * of your application successfully obtained the lock. If it failed to obtain the + * lock you can assume that another instance of your application is already running + * with the lock and exit immediately. I.e. This method returns true if your + * process is the primary instance of your application and your app should continue + * loading. It returns false if your process should immediately quit as it has + * sent its parameters to another instance that has already acquired the lock. On + * macOS the system enforces single instance automatically when users try to open a + * second instance of your app in Finder, and the open-file and open-url events + * will be emitted for that. However when users start your app in command line the + * system's single instance mechanism will be bypassed and you have to use this + * method to ensure single instance. An example of activating the window of primary + * instance when a second instance starts: + */ + requestSingleInstanceLock(): boolean; /** * Set the about panel options. This will override the values defined in the app's * .plist file. See the Apple docs for more details. @@ -883,14 +940,32 @@ declare namespace Electron { * userInfo into its current userInfo dictionary. */ updateCurrentActivity(type: string, userInfo: any): void; + whenReady(): Promise<any>; commandLine: CommandLine; dock: Dock; + /** + * A Boolean property that returns true if the app is packaged, false otherwise. + * For many apps, this property can be used to distinguish development and + * production environments. + */ + isPackaged?: boolean; } interface AutoUpdater extends EventEmitter { // Docs: http://electron.atom.io/docs/api/auto-updater + /** + * This event is emitted after a user calls quitAndInstall(). When this API is + * called, the before-quit event is not emitted before all windows are closed. As a + * result you should listen to this event if you wish to perform actions before the + * windows are closed while a process is quitting, as well as listening to + * before-quit. + */ + on(event: 'before-quit-for-update', listener: Function): this; + once(event: 'before-quit-for-update', listener: Function): this; + addListener(event: 'before-quit-for-update', listener: Function): this; + removeListener(event: 'before-quit-for-update', listener: Function): this; /** * Emitted when checking if an update has started. */ @@ -1034,7 +1109,7 @@ declare namespace Electron { * 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 + * set the event.returnValue explicitly, instead of only returning a value, as the * former works more consistently within Electron. */ on(event: 'close', listener: (event: Event) => void): this; @@ -1107,7 +1182,7 @@ declare namespace Electron { removeListener(event: 'minimize', listener: Function): this; /** * Emitted when the window is being moved to a new position. Note: On macOS this - * event is just an alias of moved. + * event is an alias of moved. */ on(event: 'move', listener: Function): this; once(event: 'move', listener: Function): this; @@ -1274,7 +1349,7 @@ declare namespace Electron { * emitted. */ static getExtensions(): Extensions; - static getFocusedWindow(): BrowserWindow; + static getFocusedWindow(): BrowserWindow | null; /** * Remove a DevTools extension by name. Note: This API cannot be called before the * ready event of the app module is emitted. @@ -1439,6 +1514,10 @@ declare namespace Electron { * more than one tab in the current window. */ moveTabToNewWindow(): void; + /** + * Moves window to top(z-order) regardless of focus + */ + moveTop(): void; /** * Uses Quick Look to preview a file at a given path. */ @@ -1484,8 +1563,9 @@ declare namespace Electron { * ratio for HD @1920x1080) within the player itself we would call this function * with arguments of 16/9 and [ 40, 50 ]. The second argument doesn't care where * the extra width and height are within the content view--only that they exist. - * Just sum any extra width and height areas you have within the overall content - * view. + * Sum any extra width and height areas you have within the overall content view. + * Calling this function with a value of 0 will remove any previously set aspect + * ratios. */ setAspectRatio(aspectRatio: number, extraSize: Size): void; /** @@ -1602,7 +1682,7 @@ declare namespace Electron { * 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. */ - setOverlayIcon(overlay: NativeImage, description: string): void; + setOverlayIcon(overlay: NativeImage | null, description: string): void; /** * Sets parent as current window's parent window, passing null will turn current * window into a top-level window. @@ -1632,6 +1712,14 @@ declare namespace Electron { * Sets whether the window can be manually resized by user. */ setResizable(resizable: boolean): void; + /** + * Setting a window shape determines the area within the window where the system + * permits drawing and user interaction. Outside of the given region, no pixels + * will be drawn and no mouse events will be registered. Mouse events outside of + * the region will not be received by that window, but will fall through to + * whatever is behind the window. + */ + setShape(rects: Rectangle[]): void; /** * Changes the attachment point for sheets on macOS. By default, sheets are * attached just below the window frame, but you may want to display them beneath a @@ -1644,7 +1732,8 @@ declare namespace Electron { */ setSimpleFullScreen(flag: boolean): void; /** - * Resizes the window to width and height. + * Resizes the window to width and height. If width or height are below any set + * minimum size constraints the window will snap to its minimum size. */ setSize(width: number, height: number, animate?: boolean): void; /** @@ -1962,6 +2051,11 @@ declare namespace Electron { */ followRedirect(): void; getHeader(name: string): Header; + /** + * You can use this method in conjunction with POST requests to get the progress of + * a file upload or other data transfer. + */ + getUploadProgress(): UploadProgress; /** * Removes a previously set extra header name. This method can be called only * before first write. Trying to call it after the first write will throw an error. @@ -2085,20 +2179,9 @@ declare namespace Electron { * Start recording on all processes. Recording begins immediately locally and * asynchronously on child processes as soon as they receive the EnableRecording * request. The callback will be called once all child processes have acknowledged - * the startRecording request. categoryFilter is a filter to control what category - * groups should be traced. A filter can have an optional - prefix to exclude - * category groups that contain a matching category. Having both included and - * excluded category patterns in the same list is not supported. Examples: - * traceOptions controls what kind of tracing is enabled, it is a comma-delimited - * list. Possible options are: The first 3 options are trace recording modes and - * hence mutually exclusive. If more than one trace recording modes appear in the - * traceOptions string, the last one takes precedence. If none of the trace - * recording modes are specified, recording mode is record-until-full. The trace - * option will first be reset to the default option (record_mode set to - * record-until-full, enable_sampling and enable_systrace set to false) before - * options parsed from traceOptions are applied on it. + * the startRecording request. */ - startRecording(options: StartRecordingOptions, callback: Function): void; + startRecording(options: TraceCategoriesAndOptions | TraceConfig, callback: Function): void; /** * Stop monitoring on all processes. Once all child processes have acknowledged the * stopMonitoring request the callback is called. @@ -2437,6 +2520,12 @@ declare namespace Electron { // Docs: http://electron.atom.io/docs/api/structures/desktop-capturer-source + /** + * A unique identifier that will correspond to the id of the matching returned by + * the . On some platforms, this is equivalent to the XX portion of the id field + * above and on others it will differ. It will be an empty string if not available. + */ + display_id: string; /** * The identifier of a window or screen that can be used as a chromeMediaSourceId * constraint when calling [navigator.webkitGetUserMedia]. The format of the @@ -2705,7 +2794,9 @@ declare namespace Electron { * registered shortcut is pressed by the user. When the accelerator is already * taken by other applications, this call will silently fail. This behavior is * intended by operating systems, since they don't want applications to fight for - * global shortcuts. + * global shortcuts. The following accelerators will not be registered successfully + * on macOS 10.14 Mojave unless the app has been authorized as a trusted + * accessibility client: */ register(accelerator: Accelerator, callback: Function): void; /** @@ -2785,26 +2876,42 @@ declare namespace Electron { */ on(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; once(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; addListener(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; removeListener(event: 'transactions-updated', listener: (event: Event, /** - * Array of transactions. + * Array of objects. */ transactions: Transaction[]) => void): this; canMakePayments(): boolean; + /** + * Completes all pending transactions. + */ + finishAllTransactions(): void; + /** + * Completes the pending transactions corresponding to the date. + */ + finishTransactionByDate(date: string): void; + /** + * Retrieves the product descriptions. + */ + getProducts(productIDs: string[], callback: (products: Product[]) => void): void; getReceiptURL(): string; + /** + * You should listen for the transactions-updated event as soon as possible and + * certainly before you call purchaseProduct. + */ purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; } @@ -2964,9 +3071,9 @@ declare namespace Electron { */ // 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. + * Sends a message to a window with webContentsId via channel. */ - sendTo(windowId: number, channel: string, ...args: any[]): void; + sendTo(webContentsId: 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. @@ -3097,10 +3204,9 @@ declare namespace Electron { removeListener(event: 'menu-will-show', listener: (event: Event) => void): this; constructor(); /** - * Generally, the template is just an array of options for constructing a MenuItem. - * The usage can be referenced above. You can also attach other fields to the - * element of the template and they will become properties of the constructed menu - * items. + * Generally, the template is an array of options for constructing a MenuItem. The + * usage can be referenced above. You can also attach other fields to the element + * of the template and they will become properties of the constructed menu items. */ static buildFromTemplate(template: MenuItemConstructorOptions[]): Menu; /** @@ -3110,9 +3216,9 @@ declare namespace Electron { 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 - * property of a MenuItem. See the macOS Cocoa Event Handling Guide for more - * information on macOS' native actions. + * emulating default macOS menu behaviors. Usually you would use the role property + * of a MenuItem. See the macOS Cocoa Event Handling Guide for more information on + * macOS' native actions. */ static sendActionToFirstResponder(action: string): void; /** @@ -3248,6 +3354,29 @@ declare namespace Electron { request(options: any | string): ClientRequest; } + interface NetLog extends EventEmitter { + + // Docs: http://electron.atom.io/docs/api/net-log + + /** + * Starts recording network events to path. + */ + startLogging(path: string): void; + /** + * Stops recording network events. If not called, net logging will automatically + * end when app quits. + */ + stopLogging(callback?: (path: string) => void): void; + /** + * A Boolean property that indicates whether network logs are recorded. + */ + currentlyLogging?: boolean; + /** + * A String property that returns the path to the current log file. + */ + currentlyLoggingPath?: string; + } + class Notification extends EventEmitter { // Docs: http://electron.atom.io/docs/api/notification @@ -3329,11 +3458,11 @@ declare namespace Electron { 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. If the notification has been shown before, this method - * will dismiss the previously shown notification and create a new one with - * identical properties. + * the HTML5 Notification implementation, instantiating a new Notification does not + * immediately show it to the user, you need to call this method before 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; } @@ -3364,6 +3493,13 @@ declare namespace Electron { // Docs: http://electron.atom.io/docs/api/power-monitor + /** + * Emitted when the system is about to lock the screen. + */ + on(event: 'lock-screen', listener: Function): this; + once(event: 'lock-screen', listener: Function): this; + addListener(event: 'lock-screen', listener: Function): this; + removeListener(event: 'lock-screen', listener: Function): this; /** * Emitted when the system changes to AC power. */ @@ -3402,6 +3538,13 @@ declare namespace Electron { once(event: 'suspend', listener: Function): this; addListener(event: 'suspend', listener: Function): this; removeListener(event: 'suspend', listener: Function): this; + /** + * Emitted as soon as the systems screen is unlocked. + */ + on(event: 'unlock-screen', listener: Function): this; + once(event: 'unlock-screen', listener: Function): this; + addListener(event: 'unlock-screen', listener: Function): this; + removeListener(event: 'unlock-screen', listener: Function): this; } interface PowerSaveBlocker extends EventEmitter { @@ -3458,6 +3601,45 @@ declare namespace Electron { type: string; } + interface Product { + + // Docs: http://electron.atom.io/docs/api/structures/product + + /** + * The total size of the content, in bytes. + */ + contentLengths: number[]; + /** + * A string that identifies the version of the content. + */ + contentVersion: string; + /** + * A Boolean value that indicates whether the App Store has downloadable content + * for this product. + */ + downloadable: boolean; + /** + * The locale formatted price of the product. + */ + formattedPrice: string; + /** + * A description of the product. + */ + localizedDescription: string; + /** + * The name of the product. + */ + localizedTitle: string; + /** + * The cost of the product in the local currency. + */ + price: number; + /** + * The string that identifies the product to the Apple App Store. + */ + productIdentifier: string; + } + interface Protocol extends EventEmitter { // Docs: http://electron.atom.io/docs/api/protocol @@ -3590,11 +3772,32 @@ declare namespace Electron { y: number; } + interface Referrer { + + // Docs: http://electron.atom.io/docs/api/structures/referrer + + /** + * Can be default, unsafe-url, no-referrer-when-downgrade, no-referrer, origin, + * strict-origin-when-cross-origin, same-origin or strict-origin. See the for more + * details on the meaning of these values. + */ + policy: ('default' | 'unsafe-url' | 'no-referrer-when-downgrade' | 'no-referrer' | 'origin' | 'strict-origin-when-cross-origin' | 'same-origin' | 'strict-origin'); + /** + * HTTP Referrer URL. + */ + url: string; + } + interface Remote extends MainInterface { // Docs: http://electron.atom.io/docs/api/remote getCurrentWebContents(): WebContents; + /** + * Note: Do not use removeAllListeners on BrowserWindow. Use of this can remove all + * blur listeners, disable click events on touch bar buttons, and other unintended + * consequences. + */ getCurrentWindow(): BrowserWindow; getGlobal(name: string): any; /** @@ -3698,6 +3901,17 @@ declare namespace Electron { oldDisplay: Display) => void): this; removeListener(event: 'display-removed', listener: (event: Event, oldDisplay: Display) => void): this; + /** + * Converts a screen DIP point to a screen physical point. The DPI scale is + * performed relative to the display containing the DIP point. + */ + dipToScreenPoint(point: Point): Point; + /** + * Converts a screen DIP rect to a screen physical rect. The DPI scale is performed + * relative to the display nearest to window. If window is null, scaling will be + * performed to the display nearest to rect. + */ + dipToScreenRect(window: BrowserWindow | null, rect: Rectangle): Rectangle; getAllDisplays(): Display[]; /** * The current absolute position of the mouse pointer. @@ -3705,8 +3919,18 @@ declare namespace Electron { getCursorScreenPoint(): Point; getDisplayMatching(rect: Rectangle): Display; getDisplayNearestPoint(point: Point): Display; - getMenuBarHeight(): number; getPrimaryDisplay(): Display; + /** + * Converts a screen physical point to a screen DIP point. The DPI scale is + * performed relative to the display containing the physical point. + */ + screenToDipPoint(point: Point): Point; + /** + * Converts a screen physical rect to a screen DIP rect. The DPI scale is performed + * relative to the display nearest to window. If window is null, scaling will be + * performed to the display nearest to rect. + */ + screenToDipRect(window: BrowserWindow | null, rect: Rectangle): Rectangle; } interface ScrubberItem { @@ -3869,6 +4093,7 @@ declare namespace Electron { */ setUserAgent(userAgent: string, acceptLanguages?: string): void; cookies: Cookies; + netLog: NetLog; protocol: Protocol; webRequest: WebRequest; } @@ -4051,6 +4276,11 @@ declare namespace Electron { * contains the user information dictionary sent along with the notification. */ postNotification(event: string, userInfo: any): void; + /** + * Posts event as native notifications of macOS. The userInfo is an Object that + * contains the user information dictionary sent along with the notification. + */ + postWorkspaceNotification(event: string, userInfo: any): void; /** * Add the specified defaults to your application's NSUserDefaults. */ @@ -4069,7 +4299,7 @@ declare namespace Electron { * Same as subscribeNotification, but uses NSNotificationCenter for local defaults. * This is necessary for events such as NSUserDefaultsDidChangeNotification. */ - subscribeLocalNotification(event: string, callback: (event: string, userInfo: any) => void): void; + subscribeLocalNotification(event: string, callback: (event: string, userInfo: any) => void): number; /** * Subscribes to native notifications of macOS, callback will be called with * callback(event, userInfo) when the corresponding event happens. The userInfo is @@ -4078,7 +4308,13 @@ declare namespace Electron { * unsubscribe the event. Under the hood this API subscribes to * NSDistributedNotificationCenter, example values of event are: */ - subscribeNotification(event: string, callback: (event: string, userInfo: any) => void): void; + subscribeNotification(event: string, callback: (event: string, userInfo: any) => void): number; + /** + * Same as subscribeNotification, but uses + * NSWorkspace.sharedWorkspace.notificationCenter. This is necessary for events + * such as NSWorkspaceDidActivateApplicationNotification. + */ + subscribeWorkspaceNotification(event: string, callback: (event: string, userInfo: any) => void): void; /** * Same as unsubscribeNotification, but removes the subscriber from * NSNotificationCenter. @@ -4088,6 +4324,11 @@ declare namespace Electron { * Removes the subscriber with id. */ unsubscribeNotification(id: number): void; + /** + * Same as unsubscribeNotification, but removes the subscriber from + * NSWorkspace.sharedWorkspace.notificationCenter. + */ + unsubscribeWorkspaceNotification(id: number): void; } interface Task { @@ -4247,21 +4488,72 @@ declare namespace Electron { static TouchBarSpacer: typeof TouchBarSpacer; } + interface TraceCategoriesAndOptions { + + // Docs: http://electron.atom.io/docs/api/structures/trace-categories-and-options + + /** + * – is a filter to control what category groups should be traced. A filter can + * have an optional prefix to exclude category groups that contain a matching + * category. Having both included and excluded category patterns in the same list + * is not supported. Examples: test_MyTest*, test_MyTest*,test_OtherStuff, + * -excluded_category1,-excluded_category2. + */ + categoryFilter: string; + /** + * Controls what kind of tracing is enabled, it is a comma-delimited sequence of + * the following strings: record-until-full, record-continuously, trace-to-console, + * enable-sampling, enable-systrace, e.g. 'record-until-full,enable-sampling'. The + * first 3 options are trace recording modes and hence mutually exclusive. If more + * than one trace recording modes appear in the traceOptions string, the last one + * takes precedence. If none of the trace recording modes are specified, recording + * mode is record-until-full. The trace option will first be reset to the default + * option (record_mode set to record-until-full, enable_sampling and + * enable_systrace set to false) before options parsed from traceOptions are + * applied on it. + */ + traceOptions: string; + } + + interface TraceConfig { + + // Docs: http://electron.atom.io/docs/api/structures/trace-config + + excluded_categories?: string[]; + included_categories?: string[]; + memory_dump_config?: MemoryDumpConfig; + } + interface Transaction { // Docs: http://electron.atom.io/docs/api/structures/transaction + /** + * The error code if an error occurred while processing the transaction. + */ errorCode: number; + /** + * The error message if an error occurred while processing the transaction. + */ errorMessage: string; + /** + * The identifier of the restored transaction by the App Store. + */ originalTransactionIdentifier: string; payment: Payment; + /** + * The date the transaction was added to the App Store’s payment queue. + */ transactionDate: string; + /** + * A string that uniquely identifies a successful payment transaction. + */ transactionIdentifier: string; /** - * The transaction sate ("purchasing", "purchased", "failed", "restored", or - * "deferred") + * The transaction state, can be purchasing, purchased, failed, restored or + * deferred. */ - transactionState: string; + transactionState: ('purchasing' | 'purchased' | 'failed' | 'restored' | 'deferred'); } class Tray extends EventEmitter { @@ -4531,6 +4823,7 @@ declare namespace Electron { * The bounds of this tray icon as Object. */ getBounds(): Rectangle; + getIgnoreDoubleClickEvents(): boolean; isDestroyed(): boolean; /** * Pops up the context menu of the tray icon. When menu is passed, the menu will be @@ -4541,13 +4834,19 @@ declare namespace Electron { /** * Sets the context menu for this icon. */ - setContextMenu(menu: Menu): void; + setContextMenu(menu: Menu | null): void; /** * Sets when the tray's icon background becomes highlighted (in blue). Note: You * can use highlightMode with a BrowserWindow by toggling between 'never' and * 'always' modes when the window visibility changes. */ setHighlightMode(mode: 'selection' | 'always' | 'never'): void; + /** + * Sets the option to ignore double click events. Ignoring these events allows you + * to detect every individual click of the tray icon. This value is set to false by + * default. + */ + setIgnoreDoubleClickEvents(ignore: boolean): void; /** * Sets the image associated with this tray icon. */ @@ -4555,7 +4854,7 @@ declare namespace Electron { /** * Sets the image associated with this tray icon when pressed on macOS. */ - setPressedImage(image: NativeImage): void; + setPressedImage(image: NativeImage | string): void; /** * Sets the title displayed aside of the tray icon in the status bar (Support ANSI * colors). @@ -4625,32 +4924,6 @@ declare namespace Electron { type: string; } - interface UploadFileSystem { - - // Docs: http://electron.atom.io/docs/api/structures/upload-file-system - - /** - * FileSystem url to read data for upload. - */ - filsSystemURL: string; - /** - * Number of bytes to read from offset. Defaults to 0. - */ - length: number; - /** - * Last Modification time in number of seconds since the UNIX epoch. - */ - modificationTime: number; - /** - * Defaults to 0. - */ - offset: number; - /** - * fileSystem. - */ - type: string; - } - interface UploadRawData { // Docs: http://electron.atom.io/docs/api/structures/upload-raw-data @@ -4737,19 +5010,23 @@ declare namespace Electron { * 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, + on(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; - once(event: 'console-message', listener: (level: number, + once(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; - addListener(event: 'console-message', listener: (level: number, + addListener(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; - removeListener(event: 'console-message', listener: (level: number, + removeListener(event: 'console-message', listener: (event: Event, + level: number, message: string, line: number, sourceId: string) => void): this; @@ -4938,22 +5215,30 @@ declare namespace Electron { errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; once(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; addListener(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; removeListener(event: 'did-fail-load', listener: (event: Event, errorCode: number, errorDescription: string, validatedURL: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Emitted when the navigation is done, i.e. the spinner of the tab has stopped * spinning, and the onload event was dispatched. @@ -4966,119 +5251,149 @@ declare namespace Electron { * Emitted when a frame has done navigation. */ on(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; once(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; addListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; removeListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** - * Emitted when a redirect is received while requesting a resource. - */ - on(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, - isMainFrame: boolean, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - once(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, - isMainFrame: boolean, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - addListener(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, - isMainFrame: boolean, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - removeListener(event: 'did-get-redirect-request', listener: (event: Event, - oldURL: string, - newURL: string, - isMainFrame: boolean, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any) => void): this; - /** - * Emitted when details regarding a requested resource are available. status - * indicates the socket connection to download the resource. - */ - on(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; - once(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; - addListener(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; - removeListener(event: 'did-get-response-details', listener: (event: Event, - status: boolean, - newURL: string, - originalURL: string, - httpResponseCode: number, - requestMethod: string, - referrer: string, - headers: any, - resourceType: string) => void): this; - /** - * Emitted when a navigation is done. This event is not emitted for in-page + * Emitted when any frame navigation is done. This event is not emitted for in-page * navigations, such as clicking anchor links or updating the window.location.hash. * Use did-navigate-in-page event for this purpose. */ - on(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; - once(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; - addListener(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; - removeListener(event: 'did-navigate', listener: (event: Event, - url: string) => void): this; + on(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + once(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + addListener(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + removeListener(event: 'did-frame-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations, + */ + httpStatusText: string, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** - * Emitted when an in-page navigation happened. When in-page navigation happens, - * the page URL changes but does not cause navigation outside of the page. Examples - * of this occurring are when anchor links are clicked or when the DOM hashchange - * event is triggered. + * Emitted when a main frame navigation is done. This event is not emitted for + * in-page navigations, such as clicking anchor links or updating the + * window.location.hash. Use did-navigate-in-page event for this purpose. + */ + on(event: 'did-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; + once(event: 'did-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; + addListener(event: 'did-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; + removeListener(event: 'did-navigate', listener: (event: Event, + url: string, + /** + * -1 for non HTTP navigations + */ + httpResponseCode: number, + /** + * empty for non HTTP navigations + */ + httpStatusText: string) => void): this; + /** + * Emitted when an in-page navigation happened in any frame. When in-page + * navigation happens, the page URL changes but does not cause navigation outside + * of the page. Examples of this occurring are when anchor links are clicked or + * when the DOM hashchange event is triggered. */ on(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; once(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; addListener(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; removeListener(event: 'did-navigate-in-page', listener: (event: Event, url: string, - isMainFrame: boolean) => void): this; + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Corresponds to the points in time when the spinner of the tab started spinning. */ @@ -5086,6 +5401,30 @@ declare namespace Electron { once(event: 'did-start-loading', listener: Function): this; addListener(event: 'did-start-loading', listener: Function): this; removeListener(event: 'did-start-loading', listener: Function): this; + /** + * Emitted when any frame (including main) starts navigating. isInplace will be + * true for in-page navigations. + */ + on(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + once(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + addListener(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; + removeListener(event: 'did-start-navigation', listener: (url: string, + isInPlace: boolean, + isMainFrame: boolean, + frameProcessId: number, + frameRoutingId: number) => void): this; /** * Corresponds to the points in time when the spinner of the tab stopped spinning. */ @@ -5171,7 +5510,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; once(event: 'new-window', listener: (event: Event, url: string, frameName: string, @@ -5188,7 +5532,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; addListener(event: 'new-window', listener: (event: Event, url: string, frameName: string, @@ -5205,7 +5554,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; removeListener(event: 'new-window', listener: (event: Event, url: string, frameName: string, @@ -5222,7 +5576,12 @@ declare namespace Electron { * The non-standard features (features not handled by Chromium or Electron) given * to `window.open()`. */ - additionalFeatures: string[]) => void): this; + additionalFeatures: string[], + /** + * The referrer that will be passed to the new window. May or may not result in the + * `Referer` header being sent, depending on the referrer policy. + */ + referrer: Referrer) => void): this; /** * Emitted when page receives favicon urls. */ @@ -5289,6 +5648,13 @@ declare namespace Electron { removeListener(event: 'plugin-crashed', listener: (event: Event, name: string, version: string) => void): this; + /** + * Emitted when the unresponsive web page becomes responsive again. + */ + on(event: 'responsive', listener: Function): this; + once(event: 'responsive', listener: Function): this; + addListener(event: 'responsive', listener: Function): this; + removeListener(event: 'responsive', listener: Function): this; /** * Emitted when bluetooth device needs to be selected on call to * navigator.bluetooth.requestDevice. To use navigator.bluetooth api webBluetooth @@ -5328,6 +5694,13 @@ declare namespace Electron { url: string, certificateList: Certificate[], callback: (certificate: Certificate) => void) => void): this; + /** + * Emitted when the web page becomes unresponsive. + */ + on(event: 'unresponsive', listener: Function): this; + once(event: 'unresponsive', listener: Function): this; + addListener(event: 'unresponsive', listener: Function): this; + removeListener(event: 'unresponsive', listener: Function): this; /** * Emitted when mouse moves over a link or the keyboard moves the focus to a link. */ @@ -5424,30 +5797,22 @@ declare namespace Electron { addWorkSpace(path: string): void; /** * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(frameBuffer, dirtyRect) when there is a presentation - * event. The frameBuffer is a Buffer that contains raw pixel data. On most - * machines, the pixel data is effectively stored in 32bit BGRA format, but the - * actual representation depends on the endianness of the processor (most modern - * processors are little-endian, on machines with big-endian processors the data is - * in 32bit ARGB format). The dirtyRect is an object with x, y, width, height - * properties that describes which part of the page was repainted. If onlyDirty is - * set to true, frameBuffer will only contain the repainted area. onlyDirty - * defaults to false. + * be called with callback(image, dirtyRect) when there is a presentation event. + * The image is an instance of NativeImage that stores the captured frame. The + * dirtyRect is an object with x, y, width, height properties that describes which + * part of the page was repainted. If onlyDirty is set to true, image will only + * contain the repainted area. onlyDirty defaults to false. */ - beginFrameSubscription(callback: (frameBuffer: Buffer, dirtyRect: Rectangle) => void): void; + beginFrameSubscription(callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; /** * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(frameBuffer, dirtyRect) when there is a presentation - * event. The frameBuffer is a Buffer that contains raw pixel data. On most - * machines, the pixel data is effectively stored in 32bit BGRA format, but the - * actual representation depends on the endianness of the processor (most modern - * processors are little-endian, on machines with big-endian processors the data is - * in 32bit ARGB format). The dirtyRect is an object with x, y, width, height - * properties that describes which part of the page was repainted. If onlyDirty is - * set to true, frameBuffer will only contain the repainted area. onlyDirty - * defaults to false. + * be called with callback(image, dirtyRect) when there is a presentation event. + * The image is an instance of NativeImage that stores the captured frame. The + * dirtyRect is an object with x, y, width, height properties that describes which + * part of the page was repainted. If onlyDirty is set to true, image will only + * contain the repainted area. onlyDirty defaults to false. */ - beginFrameSubscription(onlyDirty: boolean, callback: (frameBuffer: Buffer, dirtyRect: Rectangle) => void): void; + beginFrameSubscription(onlyDirty: boolean, callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; canGoBack(): boolean; canGoForward(): boolean; canGoToOffset(offset: number): boolean; @@ -5456,13 +5821,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(callback: (image: NativeImage) => void): void; + capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; /** * Captures a snapshot of the page within rect. Upon completion callback will be * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + capturePage(callback: (image: NativeImage) => void): void; /** * Clears the navigation history. */ @@ -5528,6 +5893,7 @@ declare namespace Electron { * Get the system printer list. */ getPrinters(): PrinterInfo[]; + getProcessId(): number; getTitle(): string; getURL(): string; getUserAgent(): string; @@ -5714,10 +6080,6 @@ declare namespace Electron { * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. */ setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Set the size of the page. This is only supported for <webview> guest contents. - */ - setSize(options: SizeOptions): void; /** * Overrides the user agent for this web page. */ @@ -5817,6 +6179,9 @@ declare namespace Electron { * Work like executeJavaScript but evaluates scripts in isolated context. */ executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): void; + findFrameByName(name: string): WebFrame; + findFrameByRoutingId(routingId: number): WebFrame; + getFrameForSelector(selector: string): WebFrame; /** * Returns an object describing usage information of Blink's internal memory * caches. This will generate: @@ -5840,12 +6205,6 @@ declare namespace Electron { * privileged scheme, without bypassing Content Security Policy: */ registerURLSchemeAsPrivileged(scheme: string, options?: RegisterURLSchemeAsPrivilegedOptions): void; - /** - * Registers the scheme as secure scheme. Secure schemes do not trigger mixed - * content warnings. For example, https and data are secure schemes because they - * cannot be corrupted by active network attackers. - */ - registerURLSchemeAsSecure(scheme: string): void; /** * Set the content security policy of the isolated world. */ @@ -5883,6 +6242,39 @@ declare namespace Electron { * limits of 300% and 50% of original size, respectively. */ setZoomLevel(level: number): void; + /** + * A WebFrame representing the first child frame of webFrame, the property would be + * null if webFrame has no children or if first child is not in the current + * renderer process. + */ + firstChild?: WebFrame; + /** + * A WebFrame representing next sibling frame, the property would be null if + * webFrame is the last frame in its parent or if the next sibling is not in the + * current renderer process. + */ + nextSibling?: WebFrame; + /** + * A WebFrame representing the frame which opened webFrame, the property would be + * null if there's no opener or opener is not in the current renderer process. + */ + opener?: WebFrame; + /** + * A WebFrame representing parent frame of webFrame, the property would be null if + * webFrame is top or parent is not in the current renderer process. + */ + parent?: WebFrame; + /** + * An Integer representing the unique frame id in the current renderer process. + * Distinct WebFrame instances that refer to the same underlying frame will have + * the same routingId. + */ + routingId?: number; + /** + * A WebFrame representing top frame in frame hierarchy to which webFrame belongs, + * the property would be null if top frame is not in the current renderer process. + */ + top?: WebFrame; } class WebRequest extends EventEmitter { @@ -5917,14 +6309,14 @@ declare namespace Electron { * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: Function): void; + onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): 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; + onBeforeSendHeaders(listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; /** * The listener will be called with listener(details) when a request is completed. */ @@ -5946,13 +6338,13 @@ declare namespace Electron { * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(filter: OnHeadersReceivedFilter, listener: Function): void; + onHeadersReceived(filter: OnHeadersReceivedFilter, listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): 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; + onHeadersReceived(listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => 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 @@ -6029,17 +6421,6 @@ declare namespace Electron { */ addEventListener(event: 'did-stop-loading', listener: (event: Event) => void, useCapture?: boolean): this; removeEventListener(event: 'did-stop-loading', listener: (event: Event) => void): this; - /** - * Fired when details regarding a requested resource is available. status indicates - * socket connection to download the resource. - */ - addEventListener(event: 'did-get-response-details', listener: (event: DidGetResponseDetailsEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-get-response-details', listener: (event: DidGetResponseDetailsEvent) => void): this; - /** - * Fired when a redirect was received while requesting a resource. - */ - addEventListener(event: 'did-get-redirect-request', listener: (event: DidGetRedirectRequestEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-get-redirect-request', listener: (event: DidGetRedirectRequestEvent) => void): this; /** * Fired when document in the given frame is loaded. */ @@ -6118,8 +6499,8 @@ declare namespace Electron { removeEventListener(event: 'close', listener: (event: Event) => void): this; /** * Fired when the guest page has sent an asynchronous message to embedder page. - * With sendToHost method and ipc-message event you can easily communicate between - * guest page and embedder page: + * With sendToHost method and ipc-message event you can communicate between guest + * page and embedder page: */ addEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void, useCapture?: boolean): this; removeEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void): this; @@ -6383,40 +6764,23 @@ declare namespace Electron { * than the minimum values or greater than the maximum. */ autosize?: string; - /** - * A list of strings which specifies the blink features to be enabled separated by - * ,. The full list of supported feature strings can be found in the - * RuntimeEnabledFeatures.json5 file. - */ - blinkfeatures?: string; /** * A list of strings which specifies the blink features to be disabled separated by * ,. The full list of supported feature strings can be found in the * RuntimeEnabledFeatures.json5 file. */ disableblinkfeatures?: string; - /** - * When this attribute is present the webview contents will be prevented from - * resizing when the webview element itself is resized. This can be used in - * combination with webContents.setSize to manually resize the webview contents in - * reaction to a window size change. This can make resizing faster compared to - * relying on the webview element bounds to automatically resize the contents. - */ - disableguestresize?: string; /** * When this attribute is present the guest page will have web security disabled. * Web security is enabled by default. */ // 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 - * identifier. Setting this attribute on a new or existing webview connects it to - * the existing webContents that currently renders in a different webview. The - * existing webview will see the destroy event and will then create a new - * webContents when a new url is loaded. + * A list of strings which specifies the blink features to be enabled separated by + * ,. The full list of supported feature strings can be found in the + * RuntimeEnabledFeatures.json5 file. */ - guestinstance?: string; + enableblinkfeatures?: string; /** * Sets the referrer URL for the guest page. */ @@ -6727,7 +7091,9 @@ declare namespace Electron { enableLargerThanScreen?: boolean; /** * Window's background color as a hexadecimal value, like #66CD00 or #FFF or - * #80FFFFFF (alpha is supported). Default is #FFF (white). + * #80FFFFFF (alpha is supported). Default is #FFF (white). If transparent is set + * to true, only values with transparent (#00-------) or opaque (#FF-----) alpha + * values are respected. */ backgroundColor?: string; /** @@ -6827,7 +7193,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, cachestorage. */ storages?: string[]; /** @@ -7097,23 +7463,6 @@ declare namespace Electron { isMainFrame: boolean; } - interface DidGetRedirectRequestEvent extends Event { - oldURL: string; - newURL: string; - isMainFrame: boolean; - } - - interface DidGetResponseDetailsEvent extends Event { - status: boolean; - newURL: string; - originalURL: string; - httpResponseCode: number; - requestMethod: string; - referrer: string; - headers: Headers; - resourceType: string; - } - interface DidNavigateEvent extends Event { url: string; } @@ -7284,6 +7633,18 @@ declare namespace Electron { interface Headers { } + interface HeapStatistics { + totalHeapSize: number; + totalHeapSizeExecutable: number; + totalPhysicalSize: number; + totalAvailableSize: number; + usedHeapSize: number; + heapSizeLimit: number; + mallocedMemory: number; + peakMallocedMemory: number; + doesZapGarbage: boolean; + } + interface IgnoreMouseEventsOptions { /** * If true, forwards mouse move messages to Chromium, enabling mouse related events @@ -7413,9 +7774,9 @@ declare namespace Electron { interface LoadURLOptions { /** - * A HTTP Referrer url. + * An HTTP Referrer url. */ - httpReferrer?: string; + httpReferrer?: string | Referrer; /** * A user agent originating the request. */ @@ -7424,10 +7785,7 @@ declare namespace Electron { * Extra headers separated by "\n" */ extraHeaders?: string; - /** - * - - */ - postData?: UploadRawData[] | UploadFile[] | UploadFileSystem[] | UploadBlob[]; + postData?: UploadRawData[] | UploadFile[] | UploadBlob[]; /** * Base url (with trailing path separator) for files to be loaded by the data url. * This is needed only if the specified url is a data url and needs to load other @@ -7477,6 +7835,9 @@ declare namespace Electron { args?: string[]; } + interface MemoryDumpConfig { + } + interface MenuItemConstructorOptions { /** * Will be called with click(menuItem, browserWindow, event) when the menu item is @@ -7508,6 +7869,11 @@ declare namespace Electron { * Should only be specified for checkbox or radio type menu items. */ checked?: boolean; + /** + * If false, the accelerator won't be registered with the system, but it will still + * be displayed. Defaults to true. + */ + registerAccelerator?: 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 then it will be @@ -7695,6 +8061,16 @@ declare namespace Electron { urls: string[]; } + interface OnBeforeSendHeadersDetails { + id: number; + url: string; + method: string; + webContentsId?: number; + resourceType: string; + timestamp: number; + requestHeaders: RequestHeaders; + } + interface OnBeforeSendHeadersFilter { /** * Array of URL patterns that will be used to filter out the requests that do not @@ -7703,6 +8079,14 @@ declare namespace Electron { urls: string[]; } + interface OnBeforeSendHeadersResponse { + cancel?: boolean; + /** + * When provided, request will be made with these headers. + */ + requestHeaders?: RequestHeaders; + } + interface OnCompletedDetails { id: number; url: string; @@ -7746,6 +8130,18 @@ declare namespace Electron { urls: string[]; } + interface OnHeadersReceivedDetails { + id: number; + url: string; + method: string; + webContentsId?: number; + resourceType: string; + timestamp: number; + statusLine: string; + statusCode: number; + responseHeaders: ResponseHeaders; + } + interface OnHeadersReceivedFilter { /** * Array of URL patterns that will be used to filter out the requests that do not @@ -7754,6 +8150,19 @@ declare namespace Electron { urls: string[]; } + interface OnHeadersReceivedResponse { + cancel: boolean; + /** + * When provided, the server is assumed to have responded with these headers. + */ + responseHeaders?: ResponseHeaders; + /** + * Should be provided when overriding responseHeaders to change header status + * otherwise original response header's status will be used. + */ + statusLine?: string; + } + interface OnResponseStartedDetails { id: number; url: string; @@ -7879,7 +8288,13 @@ declare namespace Electron { } interface Payment { + /** + * The identifier of the purchased product. + */ productIdentifier: string; + /** + * The quantity purchased. + */ quantity: number; } @@ -7944,7 +8359,7 @@ declare namespace Electron { * Specify page size of the generated PDF. Can be A3, A4, A5, Legal, Letter, * Tabloid or an Object containing height and width in microns. */ - pageSize?: string; + pageSize?: string | Size; /** * Whether to print CSS backgrounds. */ @@ -8104,6 +8519,7 @@ declare namespace Electron { interface ResourceUsage { images: MemoryUsageDetails; + scripts: MemoryUsageDetails; cssStyleSheets: MemoryUsageDetails; xslStyleSheets: MemoryUsageDetails; fonts: MemoryUsageDetails; @@ -8176,7 +8592,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 + * app.getLoginItemSettings().wasOpenedAsHidden should be checked when the app is * opened to know the current value. This setting is not available on . */ openAsHidden?: boolean; @@ -8191,29 +8607,6 @@ declare namespace Electron { args?: string[]; } - 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?: 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 { /** * An array of Strings that lists the types of desktop sources to be captured, @@ -8232,11 +8625,6 @@ declare namespace Electron { traceOptions: string; } - interface StartRecordingOptions { - categoryFilter: string; - traceOptions: string; - } - interface SystemMemoryInfo { /** * The total amount of physical memory in Kilobytes available to the system. @@ -8451,6 +8839,27 @@ declare namespace Electron { url: string; } + interface UploadProgress { + /** + * Whether the request is currently active. If this is false no other properties + * will be set + */ + active: boolean; + /** + * Whether the upload has started. If this is false both current and total will be + * set to 0. + */ + started: boolean; + /** + * The number of bytes that have been uploaded so far + */ + current: number; + /** + * The number of bytes that will be uploaded this request + */ + total: number; + } + interface Versions { /** * A String representing Chrome's version string. @@ -8675,7 +9084,7 @@ declare namespace Electron { * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to * enable. The full list of supported feature strings can be found in the file. */ - blinkFeatures?: string; + enableBlinkFeatures?: string; /** * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to * disable. The full list of supported feature strings can be found in the file. @@ -8727,7 +9136,9 @@ declare namespace Electron { */ contextIsolation?: boolean; /** - * Whether to use native window.open(). Defaults to false. This option is currently + * Whether to use native window.open(). If set to true, the webPreferences of child + * window will always be the same with parent window, regardless of the parameters + * passed to window.open(). Defaults to false. This option is currently * experimental. */ nativeWindowOpen?: boolean; @@ -8745,7 +9156,22 @@ declare namespace Electron { * of this app. Useful for passing small bits of data down to renderer process * preload scripts. */ - additionArguments?: string[]; + additionalArguments?: string[]; + /** + * Whether to enable browser style consecutive dialog protection. Default is false. + */ + safeDialogs?: boolean; + /** + * The message to display when consecutive dialog protection is triggered. If not + * defined the default message would be used, note that currently the default + * message is in English and not localized. + */ + safeDialogsMessage?: string; + /** + * Whether dragging and dropping a file or link onto the page causes a navigation. + * Default is false. + */ + navigateOnDragDrop?: boolean; } interface DefaultFontFamily { @@ -8774,6 +9200,7 @@ declare namespace Electron { */ fantasy?: string; } + } declare module 'electron' { @@ -8823,6 +9250,11 @@ declare namespace NodeJS { */ crash(): void; getCPUUsage(): Electron.CPUUsage; + /** + * Returns an object with V8 heap statistics. Note that all statistics are reported + * in Kilobytes. + */ + getHeapStatistics(): Electron.HeapStatistics; getIOCounters(): Electron.IOCounters; /** * Returns an object giving memory usage statistics about the current process. Note @@ -8903,4 +9335,4 @@ declare namespace NodeJS { electron: string; chrome: string; } -} \ No newline at end of file +} diff --git a/src/typings/es6-promise.d.ts b/src/typings/es6-promise.d.ts index f674baa6a87..2d3271e2848 100644 --- a/src/typings/es6-promise.d.ts +++ b/src/typings/es6-promise.d.ts @@ -44,7 +44,12 @@ declare namespace Promise { * Make a new promise from the thenable. * A thenable is promise-like in as far as it has a "then" method. */ - function resolve<T>(value?: T | Thenable<T>): Promise<T>; + function resolve<T>(value: T | Thenable<T>): Promise<T>; + + /** + * + */ + function resolve(): Promise<void>; /** * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error diff --git a/src/typings/fast-plist.d.ts b/src/typings/fast-plist.d.ts deleted file mode 100644 index 537e7c2e8ec..00000000000 --- a/src/typings/fast-plist.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -declare module "fast-plist" { - /** - * A very fast plist parser - */ - export function parse(content: string): any; -} diff --git a/src/typings/lib.es2018.promise.d.ts b/src/typings/lib.es2018.promise.d.ts new file mode 100644 index 00000000000..9f7b2d38cb2 --- /dev/null +++ b/src/typings/lib.es2018.promise.d.ts @@ -0,0 +1,27 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +/** + * Represents the completion of an asynchronous operation + */ +interface Promise<T> { + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): Promise<T>; +} diff --git a/src/typings/lib.ie11_safe_es6.d.ts b/src/typings/lib.ie11_safe_es6.d.ts index e4f8fc57e76..43bde6f7c64 100644 --- a/src/typings/lib.ie11_safe_es6.d.ts +++ b/src/typings/lib.ie11_safe_es6.d.ts @@ -10,7 +10,7 @@ interface Map<K, V> { clear(): void; delete(key: K): boolean; forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void; - get(key: K): V; + get(key: K): V | undefined; has(key: K): boolean; set(key: K, value?: V): Map<K, V>; readonly size: number; @@ -59,7 +59,7 @@ interface SetConstructor { declare var Set: SetConstructor; -interface WeakMap<K, V> { +interface WeakMap<K extends object, V> { delete(key: K): boolean; get(key: K): V | undefined; has(key: K): boolean; @@ -70,9 +70,9 @@ interface WeakMap<K, V> { interface WeakMapConstructor { new(): WeakMap<any, any>; - new <K, V>(): WeakMap<K, V>; + new <K extends object, V>(): WeakMap<K, V>; // new <K, V>(entries?: [K, V][]): WeakMap<K, V>; - readonly prototype: WeakMap<any, any>; + readonly prototype: WeakMap<object, any>; } declare var WeakMap: WeakMapConstructor; diff --git a/src/typings/lib.webworker.importscripts.d.ts b/src/typings/lib.webworker.importscripts.d.ts new file mode 100644 index 00000000000..e84f717c9a4 --- /dev/null +++ b/src/typings/lib.webworker.importscripts.d.ts @@ -0,0 +1,23 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +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 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + + + +///////////////////////////// +/// WorkerGlobalScope APIs +///////////////////////////// +// These are only available in a Web Worker +declare function importScripts(...urls: string[]): void; diff --git a/src/typings/node-pty.d.ts b/src/typings/node-pty.d.ts index 2fdb9dff99a..8eeb51e3b37 100644 --- a/src/typings/node-pty.d.ts +++ b/src/typings/node-pty.d.ts @@ -25,6 +25,11 @@ declare module 'node-pty' { uid?: number; gid?: number; encoding?: string; + /** + * Whether to use the experimental ConPTY system on Windows. This setting will be ignored on + * non-Windows. + */ + experimentalUseConpty?: boolean; } /** diff --git a/src/typings/spdlog.d.ts b/src/typings/spdlog.d.ts index 4762b11143f..bf6bb9c3dfd 100644 --- a/src/typings/spdlog.d.ts +++ b/src/typings/spdlog.d.ts @@ -6,7 +6,9 @@ declare module 'spdlog' { export const version: string; - export function setAsyncMode(bufferSize: number, flushInterval: number); + export function setAsyncMode(bufferSize: number, flushInterval: number): void; + export function createRotatingLogger(name: string, filename: string, filesize: number, filecount: number): RotatingLogger; + export function createRotatingLoggerAsync(name: string, filename: string, filesize: number, filecount: number): Promise<RotatingLogger>; export enum LogLevel { CRITICAL, @@ -21,14 +23,14 @@ declare module 'spdlog' { export class RotatingLogger { constructor(name: string, filename: string, filesize: number, filecount: number); - trace(message: string); - debug(message: string); - info(message: string); - warn(message: string); - error(message: string); - critical(message: string); - setLevel(level: number); - clearFormatters(); + trace(message: string): void; + debug(message: string): void; + info(message: string): void; + warn(message: string): void; + error(message: string): void; + critical(message: string): void; + setLevel(level: number): void; + clearFormatters(): void; /** * A synchronous operation to flush the contents into file */ diff --git a/src/typings/sudo-prompt.d.ts b/src/typings/sudo-prompt.d.ts index 4a204229051..85f9783d0a6 100644 --- a/src/typings/sudo-prompt.d.ts +++ b/src/typings/sudo-prompt.d.ts @@ -5,5 +5,5 @@ declare module 'sudo-prompt' { - export function exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void); + export function exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void): void; } \ No newline at end of file diff --git a/src/typings/v8-inspect-profiler.d.ts b/src/typings/v8-inspect-profiler.d.ts index c60190b3fe3..59d7f81cfa0 100644 --- a/src/typings/v8-inspect-profiler.d.ts +++ b/src/typings/v8-inspect-profiler.d.ts @@ -32,16 +32,24 @@ declare module 'v8-inspect-profiler' { } export interface Target { - description: string, - devtoolsFrontendUrl: string, - id: string, - title: string, - type: string, - url: string, - webSocketDebuggerUrl: string + description: string; + devtoolsFrontendUrl: string; + id: string; + title: string; + type: string; + url: string; + webSocketDebuggerUrl: string; } - export function startProfiling(options: { port: number, tries?: number, retyWait?: number, target?: (targets: Target[]) => Target }): PromiseLike<ProfilingSession>; + export interface StartOptions { + port: number; + tries?: number; + retyWait?: number; + checkForPaused?: boolean; + target?: (targets: Target[]) => Target; + } + + export function startProfiling(options: StartOptions): PromiseLike<ProfilingSession>; export function writeProfile(profile: ProfileResult, name?: string): PromiseLike<void>; export function rewriteAbsolutePaths(profile: ProfileResult, replaceWith?: string): ProfileResult; } diff --git a/src/typings/vscode-textmate.d.ts b/src/typings/vscode-textmate.d.ts index 816e6bfc237..224b9100482 100644 --- a/src/typings/vscode-textmate.d.ts +++ b/src/typings/vscode-textmate.d.ts @@ -30,7 +30,7 @@ declare module "vscode-textmate" { */ export interface RegistryOptions { theme?: IRawTheme; - loadGrammar(scopeName: string): Thenable<IRawGrammar>; + loadGrammar(scopeName: string): Thenable<IRawGrammar | null> | null; getInjections?(scopeName: string): string[]; getOnigLib?(): Thenable<IOnigLib>; } @@ -102,7 +102,7 @@ declare module "vscode-textmate" { /** * Tokenize `lineText` using previous line state `prevState`. */ - tokenizeLine(lineText: string, prevState: StackElement): ITokenizeLineResult; + tokenizeLine(lineText: string, prevState: StackElement | null): ITokenizeLineResult; /** * Tokenize `lineText` using previous line state `prevState`. * The result contains the tokens in binary format, resolved with the following information: @@ -113,7 +113,7 @@ declare module "vscode-textmate" { * - background color * e.g. for getting the languageId: `(metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET` */ - tokenizeLine2(lineText: string, prevState: StackElement): ITokenizeLineResult2; + tokenizeLine2(lineText: string, prevState: StackElement | null): ITokenizeLineResult2; } export interface ITokenizeLineResult { readonly tokens: IToken[]; diff --git a/src/typings/vscode-windows-registry.d.ts b/src/typings/vscode-windows-registry.d.ts new file mode 100644 index 00000000000..9be14daa4b2 --- /dev/null +++ b/src/typings/vscode-windows-registry.d.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode-windows-registry' { + export type HKEY = "HKEY_CURRENT_USER" | "HKEY_LOCAL_MACHINE" | "HKEY_CLASSES_ROOT" | "HKEY_USERS" | "HKEY_CURRENT_CONFIG"; + export function GetStringRegKey(hive: HKEY, path: string, name: string): string | undefined; +} \ No newline at end of file diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index 89d0ae3802b..0e5e59a9b51 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -7,6 +7,8 @@ * to be stable and consumed by external programs. */ +/// <reference lib="dom"/> + declare module 'vscode-xterm' { /** * A string representing text font weight. @@ -39,6 +41,16 @@ declare module 'vscode-xterm' { */ bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; + /** + * When enabled the cursor will be set to the beginning of the next line + * with every new line. This equivalent to sending '\r\n' for each '\n'. + * Normally the termios settings of the underlying PTY deals with the + * translation of '\n' to '\r\n' and this setting should not be used. If you + * deal with data from a non-PTY related source, this settings might be + * useful. + */ + convertEol?: boolean; + /** * The number of columns in the terminal. */ @@ -89,16 +101,6 @@ declare module 'vscode-xterm' { */ experimentalCharAtlas?: 'none' | 'static' | 'dynamic'; - /** - * (EXPERIMENTAL) Defines which implementation to use for buffer lines. - * - * - 'JsArray': The default/stable implementation. - * - 'TypedArray': The new experimental implementation based on TypedArrays that is expected to - * significantly boost performance and memory consumption. Use at your own risk. - * - * This option will be removed in the future. - */ - experimentalBufferLineImpl?: 'JsArray' | 'TypedArray'; /** * The font size used to render text. */ @@ -144,19 +146,12 @@ declare module 'vscode-xterm' { macOptionClickForcesSelection?: boolean; /** - * (EXPERIMENTAL) The type of renderer to use, this allows using the - * fallback DOM renderer when canvas is too slow for the environment. The - * following features do not work when the DOM renderer is used: + * The type of renderer to use, this allows using the fallback DOM renderer + * when canvas is too slow for the environment. The following features do + * not work when the DOM renderer is used: * - * - Links - * - Line height * - Letter spacing * - Cursor blink - * - Cursor style - * - * This option is marked as experiemental because it will eventually be - * moved to an addon. You can only set this option in the constructor (not - * setOption). */ rendererType?: RendererType; @@ -193,6 +188,18 @@ declare module 'vscode-xterm' { * The color theme of the terminal. */ theme?: ITheme; + + /** + * Whether "Windows mode" is enabled. Because Windows backends winpty and + * conpty operate by doing line wrapping on their side, xterm.js does not + * have access to wrapped lines. When Windows mode is enabled the following + * changes will be in effect: + * + * - Reflow is disabled. + * - Lines are assumed to be wrapped if the last character of the line is + * not whitespace. + */ + windowsMode?: boolean; } /** @@ -268,7 +275,7 @@ declare module 'vscode-xterm' { * A callback that fires when the mouse leaves a link. Note that this can * happen even when tooltipCallback hasn't fired for the link yet. */ - leaveCallback?: (event: MouseEvent, uri: string) => boolean | void; + leaveCallback?: () => void; /** * The priority of the link matcher, this defines the order in which the link @@ -300,6 +307,14 @@ declare module 'vscode-xterm' { dispose(): void; } + /** + * An event that can be listened to. + * @returns an `IDisposable` to stop listening. + */ + export interface IEvent<T> { + (listener: (e: T) => any): IDisposable; + } + export interface IMarker extends IDisposable { readonly id: number; readonly isDisposed: boolean; @@ -319,28 +334,39 @@ declare module 'vscode-xterm' { /** * The element containing the terminal. */ - element: HTMLElement; + readonly element: HTMLElement; /** * The textarea that accepts input for the terminal. */ - textarea: HTMLTextAreaElement; + readonly textarea: HTMLTextAreaElement; /** - * The number of rows in the terminal's viewport. + * The number of rows in the terminal's viewport. Use + * `ITerminalOptions.rows` to set this in the constructor and + * `Terminal.resize` for when the terminal exists. */ - rows: number; + readonly rows: number; /** - * The number of columns in the terminal's viewport. + * The number of columns in the terminal's viewport. Use + * `ITerminalOptions.cols` to set this in the constructor and + * `Terminal.resize` for when the terminal exists. */ - cols: number; + readonly cols: number; + + /** + * (EXPERIMENTAL) The terminal's current buffer, this might be either the + * normal buffer or the alt buffer depending on what's running in the + * terminal. + */ + readonly buffer: IBuffer; /** * (EXPERIMENTAL) Get all markers registered against the buffer. If the alt * buffer is active this will always return []. */ - markers: IMarker[]; + readonly markers: ReadonlyArray<IMarker>; /** * Natural language strings that can be localized. @@ -354,6 +380,70 @@ declare module 'vscode-xterm' { */ constructor(options?: ITerminalOptions); + /** + * Adds an event listener for the cursor moves. + * @returns an `IDisposable` to stop listening. + */ + onCursorMove: IEvent<void>; + + /** + * Adds an event listener for when a data event fires. This happens for + * example when the user types or pastes into the terminal. The event value + * is whatever `string` results, in a typical setup, this should be passed + * on to the backing pty. + * @returns an `IDisposable` to stop listening. + */ + onData: IEvent<string>; + + /** + * Adds an event listener for a key is pressed. The event value contains the + * string that will be sent in the data event as well as the DOM event that + * triggered it. + * @returns an `IDisposable` to stop listening. + */ + onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>; + + /** + * Adds an event listener for when a line feed is added. + * @returns an `IDisposable` to stop listening. + */ + onLineFeed: IEvent<void>; + + /** + * Adds an event listener for when a scroll occurs. The event value is the + * new position of the viewport. + * @returns an `IDisposable` to stop listening. + */ + onScroll: IEvent<number>; + + /** + * Adds an event listener for when a selection change occurs. + * @returns an `IDisposable` to stop listening. + */ + onSelectionChange: IEvent<void>; + + /** + * Adds an event listener for when rows are rendered. The event value + * contains the start row and end rows of the rendered area (ranges from `0` + * to `Terminal.rows - 1`). + * @returns an `IDisposable` to stop listening. + */ + onRender: IEvent<{ start: number, end: number }>; + + /** + * Adds an event listener for when the terminal is resized. The event value + * contains the new size. + * @returns an `IDisposable` to stop listening. + */ + onResize: IEvent<{ cols: number, rows: number }>; + + /** + * Adds an event listener for when an OSC 0 or OSC 2 title change occurs. + * The event value is the new title. + * @returns an `IDisposable` to stop listening. + */ + onTitleChange: IEvent<string>; + /** * Unfocus the terminal. */ @@ -368,54 +458,63 @@ declare module 'vscode-xterm' { * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'blur' | 'focus' | 'linefeed' | 'selection', listener: () => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'data', listener: (...args: any[]) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'key', listener: (key: string, event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'keypress' | 'keydown', listener: (event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ - on(type: 'refresh', listener: (data: {start: number, end: number}) => void): void; + on(type: 'refresh', listener: (data: { start: number, end: number }) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ - on(type: 'resize', listener: (data: {cols: number, rows: number}) => void): void; + on(type: 'resize', listener: (data: { cols: number, rows: number }) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'scroll', listener: (ydisp: number) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'title', listener: (title: string) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: string, listener: (...args: any[]) => void): void; @@ -423,26 +522,37 @@ declare module 'vscode-xterm' { * Deregisters an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener).dispose()` instead. */ off(type: 'blur' | 'focus' | 'linefeed' | 'selection' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; + /** + * Emits an event on the terminal. + * @param type The type of event + * @param data data associated with the event. + * @deprecated This is being removed from the API with no replacement, see + * issue #1505. + */ emit(type: string, data?: any): void; + /** + * Adds an event listener to the Terminal, returning an IDisposable that can + * be used to conveniently remove the event listener. + * @param type The type of event. + * @param handler The event handler. + * @deprecated use `Terminal.onEvent(listener)` instead. + */ addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; /** - * Resizes the terminal. + * Resizes the terminal. It's best practice to debounce calls to resize, + * this will help ensure that the pty can respond to the resize event + * before another one occurs. * @param x The number of columns to resize to. * @param y The number of rows to resize to. */ resize(columns: number, rows: number): void; - /** - * Writes text to the terminal, followed by a break line character (\n). - * @param data The text to write to the terminal. - */ - writeln(data: string): void; - /** * Opens the terminal within an element. * @param parent The element to create the terminal within. This element @@ -457,11 +567,35 @@ declare module 'vscode-xterm' { * should be processed by the terminal and what keys should not. * @param customKeyEventHandler The custom KeyboardEvent handler to attach. * This is a function that takes a KeyboardEvent, allowing consumers to stop - * propogation and/or prevent the default action. The function returns + * propagation and/or prevent the default action. The function returns * whether the event should be processed by xterm.js. */ attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; + /** + * (EXPERIMENTAL) Adds a handler for CSI escape sequences. + * @param flag The flag should be one-character string, which specifies the + * final character (e.g "m" for SGR) of the CSI sequence. + * @param callback The function to handle the escape sequence. The callback + * is called with the numerical params, as well as the special characters + * (e.g. "$" for DECSCPP). Return true if the sequence was handled; false if + * we should try a previous handler (set by addCsiHandler or setCsiHandler). + * The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable; + + /** + * (EXPERIMENTAL) Adds a handler for OSC escape sequences. + * @param ident The number (first parameter) of the sequence. + * @param callback The function to handle the escape sequence. The callback + * is called with OSC data string. Return true if the sequence was handled; + * false if we should try a previous handler (set by addOscHandler or + * setOscHandler). The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; + /** * (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to * be matched and handled. @@ -480,6 +614,44 @@ declare module 'vscode-xterm' { */ deregisterLinkMatcher(matcherId: number): void; + /** + * (EXPERIMENTAL) Registers a character joiner, allowing custom sequences of + * characters to be rendered as a single unit. This is useful in particular + * for rendering ligatures and graphemes, among other things. + * + * Each registered character joiner is called with a string of text + * representing a portion of a line in the terminal that can be rendered as + * a single unit. The joiner must return a sorted array, where each entry is + * itself an array of length two, containing the start (inclusive) and end + * (exclusive) index of a substring of the input that should be rendered as + * a single unit. When multiple joiners are provided, the results of each + * are collected. If there are any overlapping substrings between them, they + * are combined into one larger unit that is drawn together. + * + * All character joiners that are registered get called every time a line is + * rendered in the terminal, so it is essential for the handler function to + * run as quickly as possible to avoid slowdowns when rendering. Similarly, + * joiners should strive to return the smallest possible substrings to + * render together, since they aren't drawn as optimally as individual + * characters. + * + * NOTE: character joiners are only used by the canvas renderer. + * + * @param handler The function that determines character joins. It is called + * with a string of text that is eligible for joining and returns an array + * where each entry is an array containing the start (inclusive) and end + * (exclusive) indexes of ranges that should be rendered as a single unit. + * @return The ID of the new joiner, this can be used to deregister + */ + registerCharacterJoiner(handler: (text: string) => [number, number][]): number; + + /** + * (EXPERIMENTAL) Deregisters the character joiner if one was registered. + * NOTE: character joiners are only used by the canvas renderer. + * @param joinerId The character joiner's ID (returned after register) + */ + deregisterCharacterJoiner(joinerId: number): void; + /** * (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the * alt buffer is active, undefined is returned. @@ -498,11 +670,24 @@ declare module 'vscode-xterm' { */ getSelection(): string; + /** + * Gets the selection position or undefined if there is no selection. + */ + getSelectionPosition(): ISelectionPosition | undefined; + /** * Clears the current terminal selection. */ clearSelection(): void; + /** + * Selects text within the terminal. + * @param column The column the selection starts at.. + * @param row The row the selection starts at. + * @param length The length of the selection. + */ + select(column: number, row: number, length: number): void; + /** * Selects all text within the terminal. */ @@ -567,6 +752,20 @@ declare module 'vscode-xterm' { */ write(data: string): void; + /** + * Writes text to the terminal, followed by a break line character (\n). + * @param data The text to write to the terminal. + */ + writeln(data: string): void; + + /** + * Writes UTF8 data to the terminal. + * This has a slight performance advantage over the string based write method + * due to lesser data conversions needed on the way from the pty to xterm.js. + * @param data The data to write to the terminal. + */ + writeUtf8(data: Uint8Array): void; + /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -576,7 +775,7 @@ declare module 'vscode-xterm' { * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -627,7 +826,7 @@ declare module 'vscode-xterm' { * @param key The option key. * @param value The option value. */ - setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. @@ -682,32 +881,147 @@ declare module 'vscode-xterm' { * Applies an addon to the Terminal prototype, making it available to all * newly created Terminals. * @param addon The addon to apply. + * @deprecated Use the new loadAddon API/addon format. */ static applyAddon(addon: any): void; + + /** + * (EXPERIMENTAL) Loads an addon into this instance of xterm.js. + * @param addon The addon to load. + */ + loadAddon(addon: ITerminalAddon): void; + } + + /** + * An addon that can provide additional functionality to the terminal. + */ + export interface ITerminalAddon extends IDisposable { + /** + * (EXPERIMENTAL) This is called when the addon is activated within xterm.js. + */ + activate(terminal: Terminal): void; + } + + /** + * An object representing a selecrtion within the terminal. + */ + interface ISelectionPosition { + /** + * The start column of the selection. + */ + startColumn: number; + + /** + * The start row of the selection. + */ + startRow: number; + + /** + * The end column of the selection. + */ + endColumn: number; + + /** + * The end row of the selection. + */ + endRow: number; + } + + interface IBuffer { + /** + * The y position of the cursor. This ranges between `0` (when the + * cursor is at baseY) and `Terminal.rows - 1` (when the cursor is on the + * last row). + */ + readonly cursorY: number; + + /** + * The x position of the cursor. This ranges between `0` (left side) and + * `Terminal.cols - 1` (right side). + */ + readonly cursorX: number; + + /** + * The line within the buffer where the top of the viewport is. + */ + readonly viewportY: number; + + /** + * The line within the buffer where the top of the bottom page is (when + * fully scrolled down); + */ + readonly baseY: number; + + /** + * The amount of lines in the buffer. + */ + readonly length: number; + + /** + * Gets a line from the buffer, or undefined if the line index does not exist. + * + * Note that the result of this function should be used immediately after calling as when the + * terminal updates it could lead to unexpected behavior. + * + * @param y The line index to get. + */ + getLine(y: number): IBufferLine | undefined; + } + + interface IBufferLine { + /** + * Whether the line is wrapped from the previous line. + */ + readonly isWrapped: boolean; + + /** + * Gets a cell from the line, or undefined if the line index does not exist. + * + * Note that the result of this function should be used immediately after calling as when the + * terminal updates it could lead to unexpected behavior. + * + * @param x The character index to get. + */ + getCell(x: number): IBufferCell; + + /** + * Gets the line as a string. Note that this is gets only the string for the line, not taking + * isWrapped into account. + * + * @param trimRight Whether to trim any whitespace at the right of the line. + * @param startColumn The column to start from (inclusive). + * @param endColumn The column to end at (exclusive). + */ + translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string; + } + + interface IBufferCell { + /** + * The character within the cell. + */ + readonly char: string; + + /** + * The width of the character. Some examples: + * + * - This is `1` for most cells. + * - This is `2` for wide character like CJK glyphs. + * - This is `0` for cells immediately following cells with a width of `2`. + */ + readonly width: number; } } + // Modifications to official .d.ts below declare module 'vscode-xterm' { interface TerminalCore { debug: boolean; - buffer: { - y: number; - ybase: number; - ydisp: number; - x: number; - lines: any[]; - - translateBufferLineToString(lineIndex: number, trimRight: boolean): string; - }; - handler(text: string): void; - /** - * Emit an event on the terminal. - */ - emit(type: string, data: any): void; + _onScroll: IEventEmitter2<number>; + _onKey: IEventEmitter2<{ key: string }>; charMeasure?: { height: number, width: number }; @@ -717,7 +1031,11 @@ declare module 'vscode-xterm' { }; } - interface ISearchOptions { + interface IEventEmitter2<T> { + fire(e: T): void; + } + + interface ISearchOptions { /** * Whether the find should be done as a regex. */ @@ -736,7 +1054,6 @@ declare module 'vscode-xterm' { _core: TerminalCore; webLinksInit(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void; - winptyCompatInit(): void; /** * Find the next instance of the term, then scroll to and select it. If it diff --git a/src/typings/yauzl.d.ts b/src/typings/yauzl.d.ts index 3295c92ecb9..39ef2ad9488 100644 --- a/src/typings/yauzl.d.ts +++ b/src/typings/yauzl.d.ts @@ -31,18 +31,19 @@ declare module 'yauzl' { } export class ZipFile extends EventEmitter { - readEntry(); - openReadStream(entry: Entry, callback: (err?: Error, stream?: Readable) => void); - close(); + readEntry(): void; + openReadStream(entry: Entry, callback: (err?: Error, stream?: Readable) => void): void; + close(): void; isOpen: boolean; entryCount: number; comment: string; } export interface IOptions { - autoClose: boolean; + autoClose?: boolean; + lazyEntries?: boolean; } export function open(path: string, callback: (err?: Error, zipfile?: ZipFile) => void): void; - export function open(path: string, options: IOptions, callback: (err?: Error, zipfile?: ZipFile) => void): void; + export function open(path: string, options: IOptions | undefined, callback: (err?: Error, zipfile?: ZipFile) => void): void; } \ No newline at end of file diff --git a/src/typings/yazl.d.ts b/src/typings/yazl.d.ts index 0aa227a6c82..5644d092913 100644 --- a/src/typings/yazl.d.ts +++ b/src/typings/yazl.d.ts @@ -8,8 +8,8 @@ declare module 'yazl' { class ZipFile { outputStream: stream.Stream; - addBuffer(buffer: Buffer, path: string); - addFile(localPath: string, path: string); - end(); + addBuffer(buffer: Buffer, path: string): void; + addFile(localPath: string, path: string): void; + end(): void; } } \ No newline at end of file diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 08d9b2b8ec8..6a0cc217897 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -5,7 +5,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import * as platform from 'vs/base/common/platform'; class WindowManager { @@ -14,7 +13,7 @@ class WindowManager { // --- Zoom Level private _zoomLevel: number = 0; private _lastZoomLevelChangeTime: number = 0; - private readonly _onDidChangeZoomLevel: Emitter<number> = new Emitter<number>(); + private readonly _onDidChangeZoomLevel = new Emitter<number>(); public readonly onDidChangeZoomLevel: Event<number> = this._onDidChangeZoomLevel.event; public getZoomLevel(): number { @@ -35,7 +34,7 @@ class WindowManager { } // --- Zoom Factor - private _zoomFactor: number = 0; + private _zoomFactor: number = 1; public getZoomFactor(): number { return this._zoomFactor; @@ -46,19 +45,19 @@ class WindowManager { // --- Pixel Ratio public getPixelRatio(): number { - let ctx = document.createElement('canvas').getContext('2d'); + let ctx: any = document.createElement('canvas').getContext('2d'); let dpr = window.devicePixelRatio || 1; - let bsr = (<any>ctx).webkitBackingStorePixelRatio || - (<any>ctx).mozBackingStorePixelRatio || - (<any>ctx).msBackingStorePixelRatio || - (<any>ctx).oBackingStorePixelRatio || - (<any>ctx).backingStorePixelRatio || 1; + let bsr = ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1; return dpr / bsr; } // --- Fullscreen private _fullscreen: boolean; - private readonly _onDidChangeFullscreen: Emitter<void> = new Emitter<void>(); + private readonly _onDidChangeFullscreen = new Emitter<void>(); public readonly onDidChangeFullscreen: Event<void> = this._onDidChangeFullscreen.event; public setFullscreen(fullscreen: boolean): void { @@ -72,23 +71,6 @@ class WindowManager { public isFullscreen(): boolean { return this._fullscreen; } - - // --- Accessibility - private _accessibilitySupport = platform.AccessibilitySupport.Unknown; - private readonly _onDidChangeAccessibilitySupport: Emitter<void> = new Emitter<void>(); - - public readonly onDidChangeAccessibilitySupport: Event<void> = this._onDidChangeAccessibilitySupport.event; - public setAccessibilitySupport(accessibilitySupport: platform.AccessibilitySupport): void { - if (this._accessibilitySupport === accessibilitySupport) { - return; - } - - this._accessibilitySupport = accessibilitySupport; - this._onDidChangeAccessibilitySupport.fire(); - } - public getAccessibilitySupport(): platform.AccessibilitySupport { - return this._accessibilitySupport; - } } /** A zoom index, e.g. 1, 2, 3 */ @@ -126,16 +108,6 @@ export function isFullscreen(): boolean { } export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscreen; -export function setAccessibilitySupport(accessibilitySupport: platform.AccessibilitySupport): void { - WindowManager.INSTANCE.setAccessibilitySupport(accessibilitySupport); -} -export function getAccessibilitySupport(): platform.AccessibilitySupport { - return WindowManager.INSTANCE.getAccessibilitySupport(); -} -export function onDidChangeAccessibilitySupport(callback: () => void): IDisposable { - return WindowManager.INSTANCE.onDidChangeAccessibilitySupport(callback); -} - const userAgent = navigator.userAgent; export const isIE = (userAgent.indexOf('Trident') >= 0); @@ -146,7 +118,8 @@ export const isOpera = (userAgent.indexOf('Opera') >= 0); export const isFirefox = (userAgent.indexOf('Firefox') >= 0); export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0); export const isChrome = (userAgent.indexOf('Chrome') >= 0); -export const isSafari = (userAgent.indexOf('Chrome') === -1) && (userAgent.indexOf('Safari') >= 0); +export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0)); +export const isWebkitWebView = (!isChrome && !isSafari && isWebKit); export const isIPad = (userAgent.indexOf('iPad') >= 0); export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0); diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index c45ef765e92..d943a131474 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { SubmenuAction } from 'vs/base/browser/ui/menu/menu'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; @@ -17,17 +17,17 @@ export interface IContextMenuEvent { } export class ContextSubMenu extends SubmenuAction { - constructor(label: string, public entries: (ContextSubMenu | IAction)[]) { + constructor(label: string, public entries: Array<ContextSubMenu | IAction>) { super(label, entries, 'contextsubmenu'); } } export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; - getActions(): (IAction | ContextSubMenu)[]; - getActionItem?(action: IAction): IActionItem; + getActions(): Array<IAction | ContextSubMenu>; + getActionViewItem?(action: IAction): IActionViewItem | undefined; getActionsContext?(event?: IContextMenuEvent): any; - getKeyBinding?(action: IAction): ResolvedKeybinding; + getKeyBinding?(action: IAction): ResolvedKeybinding | undefined; getMenuClassName?(): string; onHide?(didCancel: boolean): void; actionRunner?: IActionRunner; diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index cc72a3d90c0..1a5d28e802e 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -16,7 +16,9 @@ export class DelayedDragHandler extends Disposable { constructor(container: HTMLElement, callback: () => void) { super(); - this._register(addDisposableListener(container, 'dragover', () => { + this._register(addDisposableListener(container, 'dragover', e => { + e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + if (!this.timeout) { this.timeout = setTimeout(() => { callback(); @@ -66,12 +68,12 @@ export const DataTransfers = { FILES: 'Files', /** - * Typicaly transfer type for copy/paste transfers. + * Typically transfer type for copy/paste transfers. */ TEXT: 'text/plain' }; -export function applyDragImage(event: DragEvent, label: string, clazz: string): void { +export function applyDragImage(event: DragEvent, label: string | null, clazz: string): void { const dragImage = document.createElement('div'); dragImage.className = clazz; dragImage.textContent = label; @@ -83,4 +85,30 @@ export function applyDragImage(event: DragEvent, label: string, clazz: string): // Removes the element when the DND operation is done setTimeout(() => document.body.removeChild(dragImage), 0); } -} \ No newline at end of file +} + +export interface IDragAndDropData { + update(dataTransfer: DataTransfer): void; + getData(): any; +} + +export class DragAndDropData<T> implements IDragAndDropData { + + constructor(private data: T) { } + + update(): void { + // noop + } + + getData(): T { + return this.data; + } +} + +export interface IStaticDND { + CurrentDragAndDropData: IDragAndDropData | undefined; +} + +export const StaticDND: IStaticDND = { + CurrentDragAndDropData: undefined +}; \ No newline at end of file diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 4e5530e52ee..85ce189ee98 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -11,8 +11,9 @@ import { TimeoutTimer } from 'vs/base/common/async'; import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; +import { coalesce } from 'vs/base/common/arrays'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -146,10 +147,10 @@ const _manualClassList = new class implements IDomClassList { toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void { this._findClassName(node, className); - if (this._lastStart !== -1 && (shouldHaveIt === void 0 || !shouldHaveIt)) { + if (this._lastStart !== -1 && (shouldHaveIt === undefined || !shouldHaveIt)) { this.removeClass(node, className); } - if (this._lastStart === -1 && (shouldHaveIt === void 0 || shouldHaveIt)) { + if (this._lastStart === -1 && (shouldHaveIt === undefined || shouldHaveIt)) { this.addClass(node, className); } } @@ -226,6 +227,8 @@ class DomListener implements IDisposable { } } +export function addDisposableListener<K extends keyof GlobalEventHandlersEventMap>(node: Element | Window | Document, type: K, handler: (event: GlobalEventHandlersEventMap[K]) => void, useCapture?: boolean): IDisposable; +export function addDisposableListener(node: Element | Window | Document, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable; export function addDisposableListener(node: Element | Window | Document, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable { return new DomListener(node, type, handler, useCapture); } @@ -263,7 +266,7 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { return addDisposableListener(node, 'mouseout', (e: MouseEvent) => { // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = <Node>(e.relatedTarget || e.toElement); + let toElement: Node | null = <Node>(e.relatedTarget || e.target); while (toElement && toElement !== node) { toElement = toElement.parentNode; } @@ -464,28 +467,6 @@ export function getComputedStyle(el: HTMLElement): CSSStyleDeclaration { return document.defaultView!.getComputedStyle(el, null); } -// Adapted from WinJS -// Converts a CSS positioning string for the specified element to pixels. -const convertToPixels: (element: HTMLElement, value: string) => number = (function () { - return function (element: HTMLElement, value: string): number { - return parseFloat(value) || 0; - }; -})(); - -function getDimension(element: HTMLElement, cssPropertyName: string, jsPropertyName: string): number { - let computedStyle: CSSStyleDeclaration = getComputedStyle(element); - let value = '0'; - if (computedStyle) { - if (computedStyle.getPropertyValue) { - value = computedStyle.getPropertyValue(cssPropertyName); - } else { - // IE8 - value = (<any>computedStyle).getAttribute(jsPropertyName); - } - } - return convertToPixels(element, value); -} - export function getClientArea(element: HTMLElement): Dimension { // Try with DOM clientWidth / clientHeight @@ -511,48 +492,66 @@ export function getClientArea(element: HTMLElement): Dimension { throw new Error('Unable to figure out browser width and height'); } -const sizeUtils = { +class SizeUtils { + // Adapted from WinJS + // Converts a CSS positioning string for the specified element to pixels. + private static convertToPixels(element: HTMLElement, value: string): number { + return parseFloat(value) || 0; + } - getBorderLeftWidth: function (element: HTMLElement): number { - return getDimension(element, 'border-left-width', 'borderLeftWidth'); - }, - getBorderRightWidth: function (element: HTMLElement): number { - return getDimension(element, 'border-right-width', 'borderRightWidth'); - }, - getBorderTopWidth: function (element: HTMLElement): number { - return getDimension(element, 'border-top-width', 'borderTopWidth'); - }, - getBorderBottomWidth: function (element: HTMLElement): number { - return getDimension(element, 'border-bottom-width', 'borderBottomWidth'); - }, + private static getDimension(element: HTMLElement, cssPropertyName: string, jsPropertyName: string): number { + let computedStyle: CSSStyleDeclaration = getComputedStyle(element); + let value = '0'; + if (computedStyle) { + if (computedStyle.getPropertyValue) { + value = computedStyle.getPropertyValue(cssPropertyName); + } else { + // IE8 + value = (<any>computedStyle).getAttribute(jsPropertyName); + } + } + return SizeUtils.convertToPixels(element, value); + } - getPaddingLeft: function (element: HTMLElement): number { - return getDimension(element, 'padding-left', 'paddingLeft'); - }, - getPaddingRight: function (element: HTMLElement): number { - return getDimension(element, 'padding-right', 'paddingRight'); - }, - getPaddingTop: function (element: HTMLElement): number { - return getDimension(element, 'padding-top', 'paddingTop'); - }, - getPaddingBottom: function (element: HTMLElement): number { - return getDimension(element, 'padding-bottom', 'paddingBottom'); - }, + static getBorderLeftWidth(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'border-left-width', 'borderLeftWidth'); + } + static getBorderRightWidth(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'border-right-width', 'borderRightWidth'); + } + static getBorderTopWidth(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'border-top-width', 'borderTopWidth'); + } + static getBorderBottomWidth(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'border-bottom-width', 'borderBottomWidth'); + } - getMarginLeft: function (element: HTMLElement): number { - return getDimension(element, 'margin-left', 'marginLeft'); - }, - getMarginTop: function (element: HTMLElement): number { - return getDimension(element, 'margin-top', 'marginTop'); - }, - getMarginRight: function (element: HTMLElement): number { - return getDimension(element, 'margin-right', 'marginRight'); - }, - getMarginBottom: function (element: HTMLElement): number { - return getDimension(element, 'margin-bottom', 'marginBottom'); - }, - __commaSentinel: false -}; + static getPaddingLeft(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'padding-left', 'paddingLeft'); + } + static getPaddingRight(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'padding-right', 'paddingRight'); + } + static getPaddingTop(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'padding-top', 'paddingTop'); + } + static getPaddingBottom(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'padding-bottom', 'paddingBottom'); + } + + static getMarginLeft(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'margin-left', 'marginLeft'); + } + static getMarginTop(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'margin-top', 'marginTop'); + } + static getMarginRight(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'margin-right', 'marginRight'); + } + static getMarginBottom(element: HTMLElement): number { + return SizeUtils.getDimension(element, 'margin-bottom', 'marginBottom'); + } +} // ---------------------------------------------------------------------------------------- // Position & Dimension @@ -591,8 +590,8 @@ export function getTopLeftOffset(element: HTMLElement): { left: number; top: num } if (element === offsetParent) { - left += sizeUtils.getBorderLeftWidth(element); - top += sizeUtils.getBorderTopWidth(element); + left += SizeUtils.getBorderLeftWidth(element); + top += SizeUtils.getBorderTopWidth(element); top += element.offsetTop; left += element.offsetLeft; offsetParent = element.offsetParent; @@ -612,7 +611,7 @@ export interface IDomNodePagePosition { height: number; } -export function size(element: HTMLElement, width: number, height: number): void { +export function size(element: HTMLElement, width: number | null, height: number | null): void { if (typeof width === 'number') { element.style.width = `${width}px`; } @@ -683,33 +682,33 @@ export const StandardWindow: IStandardWindow = new class implements IStandardWin // Adapted from WinJS // Gets the width of the element, including margins. export function getTotalWidth(element: HTMLElement): number { - let margin = sizeUtils.getMarginLeft(element) + sizeUtils.getMarginRight(element); + let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element); return element.offsetWidth + margin; } export function getContentWidth(element: HTMLElement): number { - let border = sizeUtils.getBorderLeftWidth(element) + sizeUtils.getBorderRightWidth(element); - let padding = sizeUtils.getPaddingLeft(element) + sizeUtils.getPaddingRight(element); + let border = SizeUtils.getBorderLeftWidth(element) + SizeUtils.getBorderRightWidth(element); + let padding = SizeUtils.getPaddingLeft(element) + SizeUtils.getPaddingRight(element); return element.offsetWidth - border - padding; } export function getTotalScrollWidth(element: HTMLElement): number { - let margin = sizeUtils.getMarginLeft(element) + sizeUtils.getMarginRight(element); + let margin = SizeUtils.getMarginLeft(element) + SizeUtils.getMarginRight(element); return element.scrollWidth + margin; } // Adapted from WinJS // Gets the height of the content of the specified element. The content height does not include borders or padding. export function getContentHeight(element: HTMLElement): number { - let border = sizeUtils.getBorderTopWidth(element) + sizeUtils.getBorderBottomWidth(element); - let padding = sizeUtils.getPaddingTop(element) + sizeUtils.getPaddingBottom(element); + let border = SizeUtils.getBorderTopWidth(element) + SizeUtils.getBorderBottomWidth(element); + let padding = SizeUtils.getPaddingTop(element) + SizeUtils.getPaddingBottom(element); return element.offsetHeight - border - padding; } // Adapted from WinJS // Gets the height of the element, including its margins. export function getTotalHeight(element: HTMLElement): number { - let margin = sizeUtils.getMarginTop(element) + sizeUtils.getMarginBottom(element); + let margin = SizeUtils.getMarginTop(element) + SizeUtils.getMarginBottom(element); return element.offsetHeight + margin; } @@ -879,7 +878,7 @@ export const EventType = { ANIMATION_START: browser.isWebKit ? 'webkitAnimationStart' : 'animationstart', ANIMATION_END: browser.isWebKit ? 'webkitAnimationEnd' : 'animationend', ANIMATION_ITERATION: browser.isWebKit ? 'webkitAnimationIteration' : 'animationiteration' -}; +} as const; export interface EventLike { preventDefault(): void; @@ -992,7 +991,7 @@ export function prepend<T extends Node>(parent: HTMLElement, child: T): T { const SELECTOR_REGEX = /([\w\-]+)?(#([\w\-]+))?((.([\w\-]+))*)/; -export function $<T extends HTMLElement>(description: string, attrs?: { [key: string]: any; }, ...children: (Node | string)[]): T { +export function $<T extends HTMLElement>(description: string, attrs?: { [key: string]: any; }, ...children: Array<Node | string>): T { let match = SELECTOR_REGEX.exec(description); if (!match) { @@ -1023,8 +1022,7 @@ export function $<T extends HTMLElement>(description: string, attrs?: { [key: st } }); - children - .filter(child => !!child) + coalesce(children) .forEach(child => { if (child instanceof Node) { result.appendChild(child); @@ -1155,3 +1153,13 @@ export function windowOpenNoOpener(url: string): void { } } } + +export function animate(fn: () => void): IDisposable { + const step = () => { + fn(); + stepDisposable = scheduleAtNextAnimationFrame(step); + }; + + let stepDisposable = scheduleAtNextAnimationFrame(step); + return toDisposable(() => stepDisposable.dispose()); +} diff --git a/src/vs/base/browser/event.ts b/src/vs/base/browser/event.ts index ba6349a1471..52cc2ae8281 100644 --- a/src/vs/base/browser/event.ts +++ b/src/vs/base/browser/event.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter, mapEvent } from 'vs/base/common/event'; +import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; export type EventHandler = HTMLElement | HTMLDocument | Window; export interface IDomEvent { - <K extends keyof HTMLElementEventMap>(element: EventHandler, type: K, useCapture?: boolean): Event<HTMLElementEventMap[K]>; - (element: EventHandler, type: string, useCapture?: boolean): Event<any>; + <K extends keyof HTMLElementEventMap>(element: EventHandler, type: K, useCapture?: boolean): BaseEvent<HTMLElementEventMap[K]>; + (element: EventHandler, type: string, useCapture?: boolean): BaseEvent<any>; } export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapture?: boolean) => { - const fn = e => emitter.fire(e); - const emitter = new Emitter<any>({ + const fn = (e: Event) => emitter.fire(e); + const emitter = new Emitter<Event>({ onFirstListenerAdd: () => { element.addEventListener(type, fn, useCapture); }, @@ -27,12 +27,12 @@ export const domEvent: IDomEvent = (element: EventHandler, type: string, useCapt }; export interface CancellableEvent { - preventDefault(); - stopPropagation(); + preventDefault(): void; + stopPropagation(): void; } -export function stop<T extends CancellableEvent>(event: Event<T>): Event<T> { - return mapEvent(event, e => { +export function stop<T extends CancellableEvent>(event: BaseEvent<T>): BaseEvent<T> { + return BaseEvent.map(event, e => { e.preventDefault(); e.stopPropagation(); return e; diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index 50b25d776e6..89210064781 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -92,12 +92,12 @@ export class GlobalMouseMoveMonitor<R> extends Disposable { this.onStopCallback = onStopCallback; let windowChain = IframeUtils.getSameOriginWindowChain(); - for (let i = 0; i < windowChain.length; i++) { - this.hooks.push(dom.addDisposableThrottledListener(windowChain[i].window.document, 'mousemove', + for (const element of windowChain) { + this.hooks.push(dom.addDisposableThrottledListener(element.window.document, 'mousemove', (data: R) => this.mouseMoveCallback!(data), (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) )); - this.hooks.push(dom.addDisposableListener(windowChain[i].window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); + this.hooks.push(dom.addDisposableListener(element.window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); } if (IframeUtils.hasDifferentOriginAncestor()) { diff --git a/src/vs/base/browser/hash.ts b/src/vs/base/browser/hash.ts new file mode 100644 index 00000000000..eee8c0f56e4 --- /dev/null +++ b/src/vs/base/browser/hash.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function createSHA1(content: string): Thenable<string> { + if (typeof require !== 'undefined') { + const _crypto: typeof crypto = require.__$__nodeRequire('crypto'); + return Promise.resolve(_crypto['createHash']('sha1').update(content).digest('hex')); + } + return crypto.subtle.digest('SHA-1', new TextEncoder().encode(content)).then(buffer => { + // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string + return Array.prototype.map.call(new Uint8Array(buffer), (value: number) => `00${value.toString(16)}`.slice(-2)).join(''); + }); +} \ No newline at end of file diff --git a/src/vs/base/browser/history.ts b/src/vs/base/browser/history.ts index 8c58bf64b00..9da95f45628 100644 --- a/src/vs/base/browser/history.ts +++ b/src/vs/base/browser/history.ts @@ -5,8 +5,8 @@ export interface IHistoryNavigationWidget { - showPreviousValue(); + showPreviousValue(): void; - showNextValue(); + showNextValue(): void; } \ No newline at end of file diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index 89780667b4d..5d94e04008d 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -6,12 +6,14 @@ import * as DOM from 'vs/base/browser/dom'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { escape } from 'vs/base/common/strings'; -import { removeMarkdownEscapes, IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; +import { removeMarkdownEscapes, IMarkdownString } from 'vs/base/common/htmlContent'; import * as marked from 'vs/base/common/marked/marked'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; +import { parse } from 'vs/base/common/marshalling'; +import { cloneAndChange } from 'vs/base/common/objects'; export interface IContentActionHandler { callback: (content: string, event?: IMouseEvent) => void; @@ -22,7 +24,7 @@ export interface RenderOptions { className?: string; inline?: boolean; actionHandler?: IContentActionHandler; - codeBlockRenderer?: (modeId: string, value: string) => Thenable<string>; + codeBlockRenderer?: (modeId: string, value: string) => Promise<string>; codeBlockRenderCallback?: () => void; } @@ -53,17 +55,44 @@ export function renderFormattedText(formattedText: string, options: RenderOption export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement { const element = createElement(options); + const _uriMassage = function (part: string): string { + let data: any; + try { + data = parse(decodeURIComponent(part)); + } catch (e) { + // ignore + } + if (!data) { + return part; + } + data = cloneAndChange(data, value => { + if (markdown.uris && markdown.uris[value]) { + return URI.revive(markdown.uris[value]); + } else { + return undefined; + } + }); + return encodeURIComponent(JSON.stringify(data)); + }; + const _href = function (href: string): string { const data = markdown.uris && markdown.uris[href]; + if (!data) { + return href; + } + let uri = URI.revive(data); + if (uri.query) { + uri = uri.with({ query: _uriMassage(uri.query) }); + } if (data) { - href = URI.revive(data).toString(true); + href = uri.toString(true); } return href; }; // signal to code-block render that the // element has been created - let signalInnerHTML: Function; + let signalInnerHTML: () => void; const withInnerHTML = new Promise(c => signalInnerHTML = c); const renderer = new marked.Renderer(); @@ -123,7 +152,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions } else { // HTML Encode href - href.replace(/&/g, '&') + href = href.replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') @@ -182,7 +211,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions } const markedOptions: marked.MarkedOptions = { - sanitize: markdown instanceof MarkdownString ? markdown.sanitize : true, + sanitize: true, renderer }; @@ -286,7 +315,7 @@ function parseFormattedText(content: string): IFormatParseTree { children: [] }; - let actionItemIndex = 0; + let actionViewItemIndex = 0; let current = root; const stack: IFormatParseTree[] = []; const stream = new StringStream(content); @@ -316,8 +345,8 @@ function parseFormattedText(content: string): IFormatParseTree { }; if (type === FormatType.Action) { - newCurrent.index = actionItemIndex; - actionItemIndex++; + newCurrent.index = actionViewItemIndex; + actionViewItemIndex++; } current.children!.push(newCurrent); diff --git a/src/vs/base/browser/iframe.ts b/src/vs/base/browser/iframe.ts index 96937dcf71e..7868cafbba2 100644 --- a/src/vs/base/browser/iframe.ts +++ b/src/vs/base/browser/iframe.ts @@ -111,8 +111,7 @@ export class IframeUtils { let windowChain = this.getSameOriginWindowChain(); - for (let i = 0; i < windowChain.length; i++) { - let windowChainEl = windowChain[i]; + for (const windowChainEl of windowChain) { if (windowChainEl.window === ancestorWindow) { break; diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index 0d7fcc3f1ff..03bdffc95ed 100644 --- a/src/vs/base/browser/keyboardEvent.ts +++ b/src/vs/base/browser/keyboardEvent.ts @@ -179,6 +179,9 @@ export function getCodeForKeyCode(keyCode: KeyCode): number { } export interface IKeyboardEvent { + + readonly _standardKeyboardEventBrand: true; + readonly browserEvent: KeyboardEvent; readonly target: HTMLElement; @@ -206,6 +209,8 @@ const metaKeyMod = (platform.isMacintosh ? KeyMod.CtrlCmd : KeyMod.WinCtrl); export class StandardKeyboardEvent implements IKeyboardEvent { + readonly _standardKeyboardEventBrand = true; + public readonly browserEvent: KeyboardEvent; public readonly target: HTMLElement; @@ -220,7 +225,7 @@ export class StandardKeyboardEvent implements IKeyboardEvent { private _asRuntimeKeybinding: SimpleKeybinding; constructor(source: KeyboardEvent) { - let e = <KeyboardEvent>source; + let e = source; this.browserEvent = e; this.target = <HTMLElement>e.target; diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index 89ff65ec499..dee572ce892 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -115,6 +115,13 @@ export class DragMouseEvent extends StandardMouseEvent { export interface IMouseWheelEvent extends MouseEvent { readonly wheelDelta: number; + readonly wheelDeltaX: number; + readonly wheelDeltaY: number; + + readonly deltaX: number; + readonly deltaY: number; + readonly deltaZ: number; + readonly deltaMode: number; } interface IWebKitMouseWheelEvent { diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index 812220c75c2..2499d715f8f 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -81,9 +81,9 @@ export class Gesture extends Disposable { this.activeTouches = {}; this.handle = null; this.targets = []; - 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))); + this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e))); + this._register(DomUtils.addDisposableListener(document, 'touchend', (e: TouchEvent) => this.onTouchEnd(e))); + this._register(DomUtils.addDisposableListener(document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e))); } public static addTarget(element: HTMLElement): void { @@ -214,10 +214,10 @@ export class Gesture extends Disposable { } } - private newGestureEvent(type: string, intialTarget?: EventTarget): GestureEvent { + private newGestureEvent(type: string, initialTarget?: EventTarget): GestureEvent { let event = <GestureEvent>(<any>document.createEvent('CustomEvent')); event.initEvent(type, false, true); - event.initialTarget = intialTarget; + event.initialTarget = initialTarget; return event; } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index e8b648a34ac..131c756a817 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -7,7 +7,7 @@ import 'vs/css!./actionbar'; import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import { Disposable, dispose } from 'vs/base/common/lifecycle'; -import { SelectBox, ISelectBoxOptions } from 'vs/base/browser/ui/selectBox/selectBox'; +import { SelectBox, ISelectOptionItem, 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'; import * as types from 'vs/base/common/types'; @@ -16,8 +16,9 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { Event, Emitter } from 'vs/base/common/event'; +import { asArray } from 'vs/base/common/arrays'; -export interface IActionItem { +export interface IActionViewItem { actionRunner: IActionRunner; setActionContext(context: any): void; render(element: HTMLElement): void; @@ -27,20 +28,20 @@ export interface IActionItem { dispose(): void; } -export interface IBaseActionItemOptions { +export interface IBaseActionViewItemOptions { draggable?: boolean; isMenu?: boolean; } -export class BaseActionItem extends Disposable implements IActionItem { +export class BaseActionViewItem extends Disposable implements IActionViewItem { - element: HTMLElement; + element?: HTMLElement; _context: any; _action: IAction; private _actionRunner: IActionRunner; - constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) { + constructor(context: any, action: IAction, protected options?: IBaseActionViewItemOptions) { super(); this._context = context || this; @@ -60,24 +61,24 @@ export class BaseActionItem extends Disposable implements IActionItem { } private handleActionChangeEvent(event: IActionChangeEvent): void { - if (event.enabled !== void 0) { + if (event.enabled !== undefined) { this.updateEnabled(); } - if (event.checked !== void 0) { + if (event.checked !== undefined) { this.updateChecked(); } - if (event.class !== void 0) { + if (event.class !== undefined) { this.updateClass(); } - if (event.label !== void 0) { + if (event.label !== undefined) { this.updateLabel(); this.updateTooltip(); } - if (event.tooltip !== void 0) { + if (event.tooltip !== undefined) { this.updateTooltip(); } } @@ -118,8 +119,7 @@ export class BaseActionItem extends Disposable implements IActionItem { 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) { + if (this._action.enabled && e.button === 0 && this.element) { DOM.addClass(this.element, 'active'); } })); @@ -146,9 +146,9 @@ export class BaseActionItem extends Disposable implements IActionItem { })); [DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT].forEach(event => { - this._register(DOM.addDisposableListener(this.element, event, e => { + this._register(DOM.addDisposableListener(this.element!, event, e => { DOM.EventHelper.stop(e); - DOM.removeClass(this.element, 'active'); + DOM.removeClass(this.element!, 'active'); })); }); } @@ -207,7 +207,7 @@ export class BaseActionItem extends Disposable implements IActionItem { dispose(): void { if (this.element) { DOM.removeNode(this.element); - this.element = null; + this.element = undefined; } super.dispose(); @@ -226,20 +226,20 @@ export class Separator extends Action { } } -export interface IActionItemOptions extends IBaseActionItemOptions { +export interface IActionViewItemOptions extends IBaseActionViewItemOptions { icon?: boolean; label?: boolean; - keybinding?: string; + keybinding?: string | null; } -export class ActionItem extends BaseActionItem { +export class ActionViewItem extends BaseActionViewItem { protected label: HTMLElement; - protected options: IActionItemOptions; + protected options: IActionViewItemOptions; - private cssClass: string; + private cssClass?: string; - constructor(context: any, action: IAction, options: IActionItemOptions = {}) { + constructor(context: any, action: IAction, options: IActionViewItemOptions = {}) { super(context, action, options); this.options = options; @@ -251,7 +251,9 @@ export class ActionItem extends BaseActionItem { render(container: HTMLElement): void { super.render(container); - this.label = DOM.append(this.element, DOM.$('a.action-label')); + if (this.element) { + this.label = DOM.append(this.element, DOM.$('a.action-label')); + } if (this._action.id === Separator.ID) { this.label.setAttribute('role', 'presentation'); // A separator is a presentation item } else { @@ -262,7 +264,7 @@ export class ActionItem extends BaseActionItem { } } - if (this.options.label && this.options.keybinding) { + if (this.options.label && this.options.keybinding && this.element) { DOM.append(this.element, DOM.$('span.keybinding')).textContent = this.options.keybinding; } @@ -325,12 +327,16 @@ export class ActionItem extends BaseActionItem { updateEnabled(): void { if (this.getAction().enabled) { this.label.removeAttribute('aria-disabled'); - DOM.removeClass(this.element, 'disabled'); + if (this.element) { + DOM.removeClass(this.element, 'disabled'); + } DOM.removeClass(this.label, 'disabled'); this.label.tabIndex = 0; } else { this.label.setAttribute('aria-disabled', 'true'); - DOM.addClass(this.element, 'disabled'); + if (this.element) { + DOM.addClass(this.element, 'disabled'); + } DOM.addClass(this.label, 'disabled'); DOM.removeTabIndexAndUpdateFocus(this.label); } @@ -357,21 +363,21 @@ export interface ActionTrigger { keyDown: boolean; } -export interface IActionItemProvider { - (action: IAction): IActionItem; +export interface IActionViewItemProvider { + (action: IAction): IActionViewItem | undefined; } export interface IActionBarOptions { orientation?: ActionsOrientation; context?: any; - actionItemProvider?: IActionItemProvider; + actionViewItemProvider?: IActionViewItemProvider; actionRunner?: IActionRunner; ariaLabel?: string; animated?: boolean; triggerKeys?: ActionTrigger; } -let defaultOptions: IActionBarOptions = { +const defaultOptions: IActionBarOptions = { orientation: ActionsOrientation.HORIZONTAL, context: null, triggerKeys: { @@ -380,7 +386,7 @@ let defaultOptions: IActionBarOptions = { } }; -export interface IActionOptions extends IActionItemOptions { +export interface IActionOptions extends IActionViewItemOptions { index?: number; } @@ -391,9 +397,9 @@ export class ActionBar extends Disposable implements IActionRunner { private _actionRunner: IActionRunner; private _context: any; - // Items - items: IActionItem[]; - protected focusedItem: number; + // View Items + viewItems: IActionViewItem[]; + protected focusedItem?: number; private focusTracker: DOM.IFocusTracker; // Elements @@ -417,13 +423,14 @@ export class ActionBar extends Disposable implements IActionRunner { this.options = options; this._context = options.context; - this._actionRunner = this.options.actionRunner; if (!this.options.triggerKeys) { this.options.triggerKeys = defaultOptions.triggerKeys; } - if (!this._actionRunner) { + if (this.options.actionRunner) { + this._actionRunner = this.options.actionRunner; + } else { this._actionRunner = new ActionRunner(); this._register(this._actionRunner); } @@ -431,7 +438,7 @@ export class ActionBar extends Disposable implements IActionRunner { this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e))); this._register(this._actionRunner.onDidBeforeRun(e => this._onDidBeforeRun.fire(e))); - this.items = []; + this.viewItems = []; this.focusedItem = undefined; this.domNode = document.createElement('div'); @@ -467,7 +474,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e); let eventHandled = true; if (event.equals(previousKey)) { @@ -492,11 +499,11 @@ export class ActionBar extends Disposable implements IActionRunner { })); this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_UP, e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e); // Run action on Enter/Space if (this.isTriggerKeyEvent(event)) { - if (!this.options.triggerKeys.keyDown) { + if (this.options.triggerKeys && !this.options.triggerKeys.keyDown) { this.doTrigger(event); } @@ -554,7 +561,7 @@ export class ActionBar extends Disposable implements IActionRunner { private updateFocusedItem(): void { for (let i = 0; i < this.actionsList.children.length; i++) { - let elem = this.actionsList.children[i]; + const elem = this.actionsList.children[i]; if (DOM.isAncestor(document.activeElement, elem)) { this.focusedItem = i; break; @@ -568,7 +575,7 @@ export class ActionBar extends Disposable implements IActionRunner { set context(context: any) { this._context = context; - this.items.forEach(i => i.setActionContext(context)); + this.viewItems.forEach(i => i.setActionContext(context)); } get actionRunner(): IActionRunner { @@ -578,7 +585,7 @@ export class ActionBar extends Disposable implements IActionRunner { set actionRunner(actionRunner: IActionRunner) { if (actionRunner) { this._actionRunner = actionRunner; - this.items.forEach(item => item.actionRunner = actionRunner); + this.viewItems.forEach(item => item.actionRunner = actionRunner); } } @@ -587,41 +594,41 @@ export class ActionBar extends Disposable implements IActionRunner { } push(arg: IAction | IAction[], options: IActionOptions = {}): void { - const actions: IAction[] = !Array.isArray(arg) ? [arg] : arg; + const actions: IAction[] = asArray(arg); let index = types.isNumber(options.index) ? options.index : null; actions.forEach((action: IAction) => { - const actionItemElement = document.createElement('li'); - actionItemElement.className = 'action-item'; - actionItemElement.setAttribute('role', 'presentation'); + const actionViewItemElement = document.createElement('li'); + actionViewItemElement.className = 'action-item'; + actionViewItemElement.setAttribute('role', 'presentation'); // Prevent native context menu on actions - this._register(DOM.addDisposableListener(actionItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { + this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => { e.preventDefault(); e.stopPropagation(); })); - let item: IActionItem | null = null; + let item: IActionViewItem | undefined; - if (this.options.actionItemProvider) { - item = this.options.actionItemProvider(action); + if (this.options.actionViewItemProvider) { + item = this.options.actionViewItemProvider(action); } if (!item) { - item = new ActionItem(this.context, action, options); + item = new ActionViewItem(this.context, action, options); } item.actionRunner = this._actionRunner; item.setActionContext(this.context); - item.render(actionItemElement); + item.render(actionViewItemElement); if (index === null || index < 0 || index >= this.actionsList.children.length) { - this.actionsList.appendChild(actionItemElement); - this.items.push(item); + this.actionsList.appendChild(actionViewItemElement); + this.viewItems.push(item); } else { - this.actionsList.insertBefore(actionItemElement, this.actionsList.children[index]); - this.items.splice(index, 0, item); + this.actionsList.insertBefore(actionViewItemElement, this.actionsList.children[index]); + this.viewItems.splice(index, 0, item); index++; } }); @@ -629,7 +636,10 @@ export class ActionBar extends Disposable implements IActionRunner { getWidth(index: number): number { if (index >= 0 && index < this.actionsList.children.length) { - return this.actionsList.children.item(index).clientWidth; + const item = this.actionsList.children.item(index); + if (item) { + return item.clientWidth; + } } return 0; @@ -637,53 +647,72 @@ export class ActionBar extends Disposable implements IActionRunner { getHeight(index: number): number { if (index >= 0 && index < this.actionsList.children.length) { - return this.actionsList.children.item(index).clientHeight; + const item = this.actionsList.children.item(index); + if (item) { + return item.clientHeight; + } } return 0; } pull(index: number): void { - if (index >= 0 && index < this.items.length) { - this.items.splice(index, 1); + if (index >= 0 && index < this.viewItems.length) { this.actionsList.removeChild(this.actionsList.childNodes[index]); + dispose(this.viewItems.splice(index, 1)); } } clear(): void { - this.items = dispose(this.items); + this.viewItems = dispose(this.viewItems); DOM.clearNode(this.actionsList); } length(): number { - return this.items.length; + return this.viewItems.length; } isEmpty(): boolean { - return this.items.length === 0; + return this.viewItems.length === 0; } - focus(selectFirst?: boolean): void { + focus(index?: number): void; + focus(selectFirst?: boolean): void; + focus(arg?: number | boolean): void { + let selectFirst: boolean = false; + let index: number | undefined = undefined; + if (arg === undefined) { + selectFirst = true; + } else if (typeof arg === 'number') { + index = arg; + } else if (typeof arg === 'boolean') { + selectFirst = arg; + } + if (selectFirst && typeof this.focusedItem === 'undefined') { // Focus the first enabled item - this.focusedItem = this.items.length - 1; + this.focusedItem = this.viewItems.length - 1; this.focusNext(); } else { + if (index !== undefined) { + this.focusedItem = index; + } + this.updateFocus(); } } - private focusNext(): void { + protected focusNext(): void { if (typeof this.focusedItem === 'undefined') { - this.focusedItem = this.items.length - 1; + this.focusedItem = this.viewItems.length - 1; } - let startIndex = this.focusedItem; - let item: IActionItem; + const startIndex = this.focusedItem; + let item: IActionViewItem; do { - this.focusedItem = (this.focusedItem + 1) % this.items.length; - item = this.items[this.focusedItem]; + this.focusedItem = (this.focusedItem + 1) % this.viewItems.length; + item = this.viewItems[this.focusedItem]; } while (this.focusedItem !== startIndex && !item.isEnabled()); if (this.focusedItem === startIndex && !item.isEnabled()) { @@ -693,22 +722,22 @@ export class ActionBar extends Disposable implements IActionRunner { this.updateFocus(); } - private focusPrevious(): void { + protected focusPrevious(): void { if (typeof this.focusedItem === 'undefined') { this.focusedItem = 0; } - let startIndex = this.focusedItem; - let item: IActionItem; + const startIndex = this.focusedItem; + let item: IActionViewItem; do { this.focusedItem = this.focusedItem - 1; if (this.focusedItem < 0) { - this.focusedItem = this.items.length - 1; + this.focusedItem = this.viewItems.length - 1; } - item = this.items[this.focusedItem]; + item = this.viewItems[this.focusedItem]; } while (this.focusedItem !== startIndex && !item.isEnabled()); if (this.focusedItem === startIndex && !item.isEnabled()) { @@ -723,22 +752,21 @@ export class ActionBar extends Disposable implements IActionRunner { this.actionsList.focus(); } - for (let i = 0; i < this.items.length; i++) { - let item = this.items[i]; - - let actionItem = item; + for (let i = 0; i < this.viewItems.length; i++) { + const item = this.viewItems[i]; + const actionViewItem = item; if (i === this.focusedItem) { - if (types.isFunction(actionItem.isEnabled)) { - if (actionItem.isEnabled() && types.isFunction(actionItem.focus)) { - actionItem.focus(fromRight); + if (types.isFunction(actionViewItem.isEnabled)) { + if (actionViewItem.isEnabled() && types.isFunction(actionViewItem.focus)) { + actionViewItem.focus(fromRight); } else { this.actionsList.focus(); } } } else { - if (types.isFunction(actionItem.blur)) { - actionItem.blur(); + if (types.isFunction(actionViewItem.blur)) { + actionViewItem.blur(); } } } @@ -750,30 +778,28 @@ export class ActionBar extends Disposable implements IActionRunner { } // trigger action - let actionItem = this.items[this.focusedItem]; - if (actionItem instanceof BaseActionItem) { - const context = (actionItem._context === null || actionItem._context === undefined) ? event : actionItem._context; - this.run(actionItem._action, context); + const actionViewItem = this.viewItems[this.focusedItem]; + if (actionViewItem instanceof BaseActionViewItem) { + const context = (actionViewItem._context === null || actionViewItem._context === undefined) ? event : actionViewItem._context; + this.run(actionViewItem._action, context); } } private cancel(): void { if (document.activeElement instanceof HTMLElement) { - (<HTMLElement>document.activeElement).blur(); // remove focus from focused action + document.activeElement.blur(); // remove focus from focused action } this._onDidCancel.fire(); } - run(action: IAction, context?: any): Thenable<void> { + run(action: IAction, context?: any): Promise<void> { return this._actionRunner.run(action, context); } dispose(): void { - if (this.items !== null) { - dispose(this.items); - } - this.items = null; + dispose(this.viewItems); + this.viewItems = []; DOM.removeNode(this.getContainer()); @@ -781,20 +807,20 @@ export class ActionBar extends Disposable implements IActionRunner { } } -export class SelectActionItem extends BaseActionItem { +export class SelectActionViewItem extends BaseActionViewItem { protected selectBox: SelectBox; - constructor(ctx: any, action: IAction, options: string[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions) { + constructor(ctx: any, action: IAction, options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, selectBoxOptions?: ISelectBoxOptions) { super(ctx, action); - this.selectBox = new SelectBox(options, selected, contextViewProvider, null, selectBoxOptions); + this.selectBox = new SelectBox(options, selected, contextViewProvider, undefined, selectBoxOptions); this._register(this.selectBox); this.registerListeners(); } - setOptions(options: string[], selected?: number, disabled?: number): void { - this.selectBox.setOptions(options, selected, disabled); + setOptions(options: ISelectOptionItem[], selected?: number): void { + this.selectBox.setOptions(options, selected); } select(index: number): void { diff --git a/src/vs/base/browser/ui/aria/aria.ts b/src/vs/base/browser/ui/aria/aria.ts index 1922450144d..fc71827eafa 100644 --- a/src/vs/base/browser/ui/aria/aria.ts +++ b/src/vs/base/browser/ui/aria/aria.ts @@ -33,48 +33,50 @@ export function setARIAContainer(parent: HTMLElement) { /** * Given the provided message, will make sure that it is read as alert to screen readers. */ -export function alert(msg: string): void { - insertMessage(alertContainer, msg); +export function alert(msg: string, disableRepeat?: boolean): void { + insertMessage(alertContainer, msg, disableRepeat); } /** * Given the provided message, will make sure that it is read as status to screen readers. */ -export function status(msg: string): void { +export function status(msg: string, disableRepeat?: boolean): void { if (isMacintosh) { - alert(msg); // VoiceOver does not seem to support status role + alert(msg, disableRepeat); // VoiceOver does not seem to support status role } else { - insertMessage(statusContainer, msg); + insertMessage(statusContainer, msg, disableRepeat); } } let repeatedTimes = 0; let prevText: string | undefined = undefined; -function insertMessage(target: HTMLElement, msg: string): void { +function insertMessage(target: HTMLElement, msg: string, disableRepeat?: boolean): void { if (!ariaContainer) { - // console.warn('ARIA support needs a container. Call setARIAContainer() first.'); return; } - if (prevText === msg) { - repeatedTimes++; - } - else { - prevText = msg; - repeatedTimes = 0; - } + // If the same message should be inserted that is already present, a screen reader would + // not announce this message because it matches the previous one. As a workaround, we + // alter the message with the number of occurences unless this is explicitly disabled + // via the disableRepeat flag. + if (!disableRepeat) { + 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; + 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); target.textContent = msg; - // See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/ target.style.visibility = 'hidden'; target.style.visibility = 'visible'; diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 42f43d27322..28658f3ee60 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -126,7 +126,7 @@ export class BreadcrumbsWidget { this._pendingLayout.dispose(); } if (dim) { - // only meaure + // only measure this._pendingLayout = this._updateDimensions(dim); } else { this._pendingLayout = this._updateScrollbar(); @@ -200,8 +200,8 @@ export class BreadcrumbsWidget { return this._items[this._focusedItemIdx]; } - setFocused(item: BreadcrumbsItem, payload?: any): void { - this._focus(this._items.indexOf(item), payload); + setFocused(item: BreadcrumbsItem | undefined, payload?: any): void { + this._focus(this._items.indexOf(item!), payload); } focusPrev(payload?: any): any { @@ -256,8 +256,8 @@ export class BreadcrumbsWidget { return this._items[this._selectedItemIdx]; } - setSelection(item: BreadcrumbsItem, payload?: any): void { - this._select(this._items.indexOf(item), payload); + setSelection(item: BreadcrumbsItem | undefined, payload?: any): void { + this._select(this._items.indexOf(item!), payload); } private _select(nth: number, payload: any): void { @@ -274,7 +274,7 @@ export class BreadcrumbsWidget { this._onDidSelectItem.fire({ type: 'select', item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx], payload }); } - getItems(): ReadonlyArray<BreadcrumbsItem> { + getItems(): readonly BreadcrumbsItem[] { return this._items; } @@ -334,7 +334,7 @@ export class BreadcrumbsWidget { private _onClick(event: IMouseEvent): void { for (let el: HTMLElement | null = event.target; el; el = el.parentElement) { - let idx = this._nodes.indexOf(el as any); + let idx = this._nodes.indexOf(el as HTMLDivElement); if (idx >= 0) { this._focus(idx, event); this._select(idx, event); diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 0a508f08d32..82c1a85b8fb 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -11,7 +11,7 @@ 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 } from 'vs/base/browser/touch'; +import { Gesture, EventType } from 'vs/base/browser/touch'; export interface IButtonOptions extends IButtonStyles { title?: boolean; @@ -40,7 +40,7 @@ export class Button extends Disposable { private buttonForeground: Color | undefined; private buttonBorder: Color | undefined; - private _onDidClick = this._register(new Emitter<any>()); + private _onDidClick = this._register(new Emitter<Event>()); get onDidClick(): BaseEvent<Event> { return this._onDidClick.event; } private focusTracker: DOM.IFocusTracker; @@ -65,17 +65,19 @@ export class Button extends Disposable { Gesture.addTarget(this._element); - this._register(DOM.addDisposableListener(this._element, DOM.EventType.CLICK, e => { - if (!this.enabled) { - DOM.EventHelper.stop(e); - return; - } + [DOM.EventType.CLICK, EventType.Tap].forEach(eventType => { + this._register(DOM.addDisposableListener(this._element, eventType, e => { + if (!this.enabled) { + DOM.EventHelper.stop(e); + return; + } - this._onDidClick.fire(e); - })); + this._onDidClick.fire(e); + })); + }); this._register(DOM.addDisposableListener(this._element, DOM.EventType.KEY_DOWN, e => { - const event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e); let eventHandled = false; if (this.enabled && event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { this._onDidClick.fire(e); @@ -199,7 +201,7 @@ export class ButtonGroup extends Disposable { // Implement keyboard access in buttons if there are multiple if (count > 1) { this._register(DOM.addDisposableListener(button.element, DOM.EventType.KEY_DOWN, e => { - const event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e); let eventHandled = true; // Next / Previous Button diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 7964af23c07..a776ba92a6d 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -5,7 +5,7 @@ import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; -import { Event, mapEvent } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IView } from 'vs/base/browser/ui/grid/gridview'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; @@ -39,8 +39,8 @@ function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { element: view.element, get maximumSize() { return view.maximumWidth; }, get minimumSize() { return view.minimumWidth; }, - onDidChange: mapEvent(view.onDidChange, e => e && e.width), - layout: size => view.layout(size, getHeight()) + onDidChange: Event.map(view.onDidChange, e => e && e.width), + layout: size => view.layout(size, getHeight(), Orientation.HORIZONTAL) }; } @@ -78,7 +78,7 @@ export class CenteredViewLayout { this.resizeMargins(); } } else { - this.view.layout(width, height); + this.view.layout(width, height, Orientation.HORIZONTAL); } this.didLayout = true; } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index 318217402df..a4419eda426 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -11,11 +11,11 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import * as objects from 'vs/base/common/objects'; -import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; export interface ICheckboxOpts extends ICheckboxStyles { - readonly actionClassName: string; + readonly actionClassName?: string; readonly title: string; readonly isChecked: boolean; } @@ -28,7 +28,7 @@ const defaultOpts = { inputActiveOptionBorder: Color.fromHex('#007ACC') }; -export class CheckboxActionItem extends BaseActionItem { +export class CheckboxActionViewItem extends BaseActionViewItem { private checkbox: Checkbox; private disposables: IDisposable[] = []; @@ -92,7 +92,7 @@ export class Checkbox extends Widget { this.domNode = document.createElement('div'); this.domNode.title = this._opts.title; - this.domNode.className = 'monaco-custom-checkbox ' + this._opts.actionClassName + ' ' + (this._checked ? 'checked' : 'unchecked'); + this.domNode.className = 'monaco-custom-checkbox ' + (this._opts.actionClassName || '') + ' ' + (this._checked ? 'checked' : 'unchecked'); this.domNode.tabIndex = 0; this.domNode.setAttribute('role', 'checkbox'); this.domNode.setAttribute('aria-checked', String(this._checked)); diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 409f3c9497f..02ead6f7eaf 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -25,7 +25,8 @@ export const enum AnchorPosition { export interface IDelegate { getAnchor(): HTMLElement | IAnchor; - render(container: HTMLElement): IDisposable; + render(container: HTMLElement): IDisposable | null; + focus?(): void; layout?(): void; anchorAlignment?: AnchorAlignment; // default: left anchorPosition?: AnchorPosition; // default: below @@ -117,9 +118,9 @@ export class ContextView extends Disposable { this._register(toDisposable(() => this.setContainer(null))); } - public setContainer(container: HTMLElement | null): void { + setContainer(container: HTMLElement | null): void { if (this.container) { - this.toDisposeOnSetContainer = dispose(this.toDisposeOnSetContainer); + dispose(this.toDisposeOnSetContainer); this.container.removeChild(this.view); this.container = null; } @@ -131,13 +132,13 @@ export class ContextView extends Disposable { ContextView.BUBBLE_UP_EVENTS.forEach(event => { toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { - this.onDOMEvent(e, <HTMLElement>document.activeElement, false); + this.onDOMEvent(e, false); })); }); ContextView.BUBBLE_DOWN_EVENTS.forEach(event => { toDisposeOnSetContainer.push(DOM.addStandardDisposableListener(this.container!, event, (e: Event) => { - this.onDOMEvent(e, <HTMLElement>document.activeElement, true); + this.onDOMEvent(e, true); }, true)); }); @@ -145,7 +146,7 @@ export class ContextView extends Disposable { } } - public show(delegate: IDelegate): void { + show(delegate: IDelegate): void { if (this.isVisible()) { this.hide(); } @@ -165,9 +166,14 @@ export class ContextView extends Disposable { // Layout this.doLayout(); + + // Focus + if (this.delegate.focus) { + this.delegate.focus(); + } } - public layout(): void { + layout(): void { if (!this.isVisible()) { return; } @@ -207,13 +213,11 @@ export class ContextView extends Disposable { height: elementPosition.height }; } else { - let realAnchor = <IAnchor>anchor; - around = { - top: realAnchor.y, - left: realAnchor.x, - width: realAnchor.width || 1, - height: realAnchor.height || 2 + top: anchor.y, + left: anchor.x, + width: anchor.width || 1, + height: anchor.height || 2 }; } @@ -223,7 +227,7 @@ export class ContextView extends Disposable { const anchorPosition = this.delegate!.anchorPosition || AnchorPosition.BELOW; const anchorAlignment = this.delegate!.anchorAlignment || AnchorAlignment.LEFT; - const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + const verticalAnchor: ILayoutAnchor = { offset: around.top - window.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; let horizontalAnchor: ILayoutAnchor; @@ -233,7 +237,7 @@ export class ContextView extends Disposable { horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After }; } - const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor); + const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; // if view intersects vertically with anchor, shift it horizontally if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) { @@ -252,7 +256,7 @@ export class ContextView extends Disposable { this.view.style.width = 'initial'; } - public hide(data?: any): void { + hide(data?: any): void { if (this.delegate && this.delegate.onHide) { this.delegate.onHide(data); } @@ -271,7 +275,7 @@ export class ContextView extends Disposable { return !!this.delegate; } - private onDOMEvent(e: Event, element: HTMLElement, onCapture: boolean): void { + private onDOMEvent(e: Event, onCapture: boolean): void { if (this.delegate) { if (this.delegate.onDOMEvent) { this.delegate.onDOMEvent(e, <HTMLElement>document.activeElement); @@ -281,7 +285,7 @@ export class ContextView extends Disposable { } } - public dispose(): void { + dispose(): void { this.hide(); super.dispose(); diff --git a/src/vs/base/browser/ui/dialog/close-inverse.svg b/src/vs/base/browser/ui/dialog/close-inverse.svg new file mode 100644 index 00000000000..a174033d912 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/close-inverse.svg @@ -0,0 +1 @@ +<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#C5C5C5"/></svg> \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/close.svg b/src/vs/base/browser/ui/dialog/close.svg new file mode 100644 index 00000000000..f4038b8bfa5 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/close.svg @@ -0,0 +1 @@ +<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.784 8L13 11.217 11.215 13 8.001 9.786 4.785 13 3 11.216l3.214-3.215L3 4.785 4.784 3 8 6.216 11.216 3 13 4.785 9.784 8.001z" fill="#424242"/></svg> \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css new file mode 100644 index 00000000000..c909dada79e --- /dev/null +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +/** Dialog: Modal Block */ +.monaco-workbench .dialog-modal-block { + position: fixed; + height: 100%; + width: 100%; + left:0; + top:0; + z-index: 2000; + display: flex; + justify-content: center; + align-items: center; +} + +.monaco-workbench .dialog-modal-block.dimmed { + background: rgba(0, 0, 0, 0.3); +} + +/** Dialog: Container */ +.monaco-workbench .dialog-box { + display: flex; + flex-direction: column-reverse; + width: min-content; + min-width: 500px; + max-width: 90%; + min-height: 75px; + padding: 5px; +} + +/** Dialog: Title Actions Row */ +.monaco-workbench .dialog-box .dialog-toolbar-row { + padding-right: 1px; +} + +.monaco-workbench .dialog-box .action-label { + height: 16px; + min-width: 16px; + background-size: 16px; + background-position: 50%; + background-repeat: no-repeat; + margin: 0px; + margin-left: 4px; +} + + +.monaco-workbench .dialog-box .dialog-close-action { + background: url('close.svg') center center no-repeat; +} + +.vs-dark .monaco-workbench .dialog-box .dialog-close-action, +.hc-black .monaco-workbench .dialog-box .dialog-close-action { + background: url('close-inverse.svg') center center no-repeat; +} + +/** Dialog: Message Row */ +.monaco-workbench .dialog-box .dialog-message-row { + display: flex; + flex-grow: 1; + padding: 10px 15px 20px; + align-items: center; +} + +.monaco-workbench .dialog-box .dialog-message-row .dialog-icon { + flex: 0 0 30px; + height: 30px; + padding-right: 4px; + padding-left: 4px; + background-position: center; + background-repeat: no-repeat; +} + +.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { + background-image: url('pending.svg'); +} + +.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info { + background-image: url('info.svg'); +} + +.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning { + background-image: url('warning.svg'); +} + +.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error { + background-image: url('error.svg'); +} + +.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { + background-image: url('pending-dark.svg'); +} + +.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info, +.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info { + background-image: url('info-inverse.svg'); +} + +.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning, +.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning { + background-image: url('warning-inverse.svg'); +} + +.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error, +.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error { + background-image: url('error-inverse.svg'); +} + +.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { + background-image: url('pending-hc.svg'); +} + +.monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { + background-size: 30px; +} + +/** Dialog: Message Container */ +.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container { + display: flex; + flex-direction: column; + overflow: hidden; + text-overflow: ellipsis; + padding-left: 20px; + user-select: text; + word-wrap: break-word; /* never overflow long words, but break to next line */ + white-space: normal; +} + +/** Dialog: Message */ +.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-message { + line-height: 22px; + font-size: 18px; + flex: 1; /* let the message always grow */ + white-space: normal; + word-wrap: break-word; /* never overflow long words, but break to next line */ + padding-bottom: 10px; +} + +/** Dialog: Details */ +.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-message-detail { + line-height: 22px; + flex: 1; /* let the message always grow */ + opacity: .9; +} + +.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-message a:focus { + outline-width: 1px; + outline-style: solid; +} + +/** Dialog: Buttons Row */ +.monaco-workbench .dialog-box > .dialog-buttons-row { + display: flex; + align-items: center; + justify-content: flex-end; + padding-right: 1px; + overflow: hidden; /* buttons row should never overflow */ +} + +.monaco-workbench .dialog-box > .dialog-buttons-row { + display: flex; + white-space: nowrap; +} + +/** Dialog: Buttons */ +.monaco-workbench .dialog-box > .dialog-buttons-row > .dialog-buttons { + display: flex; + overflow: hidden; +} + +.monaco-workbench .dialog-box > .dialog-buttons-row > .dialog-buttons > .monaco-button { + max-width: fit-content; + padding: 5px 10px; + margin: 4px 5px; /* allows button focus outline to be visible */ + overflow: hidden; + text-overflow: ellipsis; +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts new file mode 100644 index 00000000000..6dc7d9ee067 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./dialog'; +import * as nls from 'vs/nls'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { $, hide, show, EventHelper, clearNode, removeClasses, addClass, removeNode, isAncestor } from 'vs/base/browser/dom'; +import { domEvent } from 'vs/base/browser/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Color } from 'vs/base/common/color'; +import { ButtonGroup, IButtonStyles } from 'vs/base/browser/ui/button/button'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; +import { isMacintosh } from 'vs/base/common/platform'; + +export interface IDialogOptions { + cancelId?: number; + detail?: string; + type?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending'; + keyEventProcessor?: (event: StandardKeyboardEvent) => void; +} + +export interface IDialogStyles extends IButtonStyles { + dialogForeground?: Color; + dialogBackground?: Color; + dialogShadow?: Color; + dialogBorder?: Color; +} + +interface ButtonMapEntry { + label: string; + index: number; +} + +export class Dialog extends Disposable { + private element: HTMLElement | undefined; + private modal: HTMLElement | undefined; + private buttonsContainer: HTMLElement | undefined; + private messageDetailElement: HTMLElement | undefined; + private iconElement: HTMLElement | undefined; + private toolbarContainer: HTMLElement | undefined; + private buttonGroup: ButtonGroup | undefined; + private styles: IDialogStyles | undefined; + private focusToReturn: HTMLElement | undefined; + + constructor(private container: HTMLElement, private message: string, private buttons: string[], private options: IDialogOptions) { + super(); + this.modal = this.container.appendChild($(`.dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`)); + this.element = this.modal.appendChild($('.dialog-box')); + hide(this.element); + + const buttonsRowElement = this.element.appendChild($('.dialog-buttons-row')); + this.buttonsContainer = buttonsRowElement.appendChild($('.dialog-buttons')); + + const messageRowElement = this.element.appendChild($('.dialog-message-row')); + this.iconElement = messageRowElement.appendChild($('.dialog-icon')); + const messageContainer = messageRowElement.appendChild($('.dialog-message-container')); + + if (this.options.detail) { + const messageElement = messageContainer.appendChild($('.dialog-message')); + messageElement.innerText = this.message; + } + + this.messageDetailElement = messageContainer.appendChild($('.dialog-message-detail')); + this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message; + + const toolbarRowElement = this.element.appendChild($('.dialog-toolbar-row')); + this.toolbarContainer = toolbarRowElement.appendChild($('.dialog-toolbar')); + } + + updateMessage(message: string): void { + if (this.messageDetailElement) { + this.messageDetailElement.innerText = message; + } + } + + async show(): Promise<number> { + this.focusToReturn = document.activeElement as HTMLElement; + + return new Promise<number>((resolve) => { + if (!this.element || !this.buttonsContainer || !this.iconElement || !this.toolbarContainer) { + resolve(0); + return; + } + + if (this.modal) { + this._register(domEvent(this.modal, 'mousedown')(e => { + // Used to stop focusing of modal with mouse + EventHelper.stop(e, true); + })); + } + + clearNode(this.buttonsContainer); + + let focusedButton = 0; + this.buttonGroup = new ButtonGroup(this.buttonsContainer, this.buttons.length, { title: true }); + const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId); + this.buttonGroup.buttons.forEach((button, index) => { + button.label = mnemonicButtonLabel(buttonMap[index].label, true); + + this._register(button.onDidClick(e => { + EventHelper.stop(e); + resolve(buttonMap[index].index); + })); + }); + + this._register(domEvent(window, 'keydown', true)((e: KeyboardEvent) => { + const evt = new StandardKeyboardEvent(e); + if (evt.equals(KeyCode.Enter) || evt.equals(KeyCode.Space)) { + return; + } + + let eventHandled = false; + if (this.buttonGroup) { + if (evt.equals(KeyMod.Shift | KeyCode.Tab) || evt.equals(KeyCode.LeftArrow)) { + focusedButton = focusedButton + this.buttonGroup.buttons.length - 1; + focusedButton = focusedButton % this.buttonGroup.buttons.length; + this.buttonGroup.buttons[focusedButton].focus(); + eventHandled = true; + } else if (evt.equals(KeyCode.Tab) || evt.equals(KeyCode.RightArrow)) { + focusedButton++; + focusedButton = focusedButton % this.buttonGroup.buttons.length; + this.buttonGroup.buttons[focusedButton].focus(); + eventHandled = true; + } + } + + if (eventHandled) { + EventHelper.stop(e, true); + } else if (this.options.keyEventProcessor) { + this.options.keyEventProcessor(evt); + } + })); + + this._register(domEvent(window, 'keyup', true)((e: KeyboardEvent) => { + EventHelper.stop(e, true); + const evt = new StandardKeyboardEvent(e); + + if (evt.equals(KeyCode.Escape)) { + resolve(this.options.cancelId || 0); + } + })); + + this._register(domEvent(this.element, 'focusout', false)((e: FocusEvent) => { + if (!!e.relatedTarget && !!this.element) { + if (!isAncestor(e.relatedTarget as HTMLElement, this.element)) { + this.focusToReturn = e.relatedTarget as HTMLElement; + + if (e.target) { + (e.target as HTMLElement).focus(); + EventHelper.stop(e, true); + } + } + } + })); + + removeClasses(this.iconElement, 'icon-error', 'icon-warning', 'icon-info'); + + switch (this.options.type) { + case 'error': + addClass(this.iconElement, 'icon-error'); + break; + case 'warning': + addClass(this.iconElement, 'icon-warning'); + break; + case 'pending': + addClass(this.iconElement, 'icon-pending'); + break; + case 'none': + case 'info': + case 'question': + default: + addClass(this.iconElement, 'icon-info'); + break; + } + + const actionBar = new ActionBar(this.toolbarContainer, {}); + + const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), 'dialog-close-action', true, () => { + resolve(this.options.cancelId || 0); + return Promise.resolve(); + }); + + actionBar.push(action, { icon: true, label: false, }); + + this.applyStyles(); + + show(this.element); + + // Focus first element + this.buttonGroup.buttons[focusedButton].focus(); + }); + } + + private applyStyles() { + if (this.styles) { + const style = this.styles; + + const fgColor = style.dialogForeground ? `${style.dialogForeground}` : null; + const bgColor = style.dialogBackground ? `${style.dialogBackground}` : null; + const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : null; + const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : null; + + if (this.element) { + this.element.style.color = fgColor; + this.element.style.backgroundColor = bgColor; + this.element.style.boxShadow = shadowColor; + this.element.style.border = border; + + if (this.buttonGroup) { + this.buttonGroup.buttons.forEach(button => button.style(style)); + } + } + } + } + + style(style: IDialogStyles): void { + this.styles = style; + this.applyStyles(); + } + + dispose(): void { + super.dispose(); + if (this.modal) { + removeNode(this.modal); + this.modal = undefined; + } + + if (this.focusToReturn && isAncestor(this.focusToReturn, document.body)) { + this.focusToReturn.focus(); + this.focusToReturn = undefined; + } + } + + private rearrangeButtons(buttons: Array<string>, cancelId: number | undefined): ButtonMapEntry[] { + const buttonMap: ButtonMapEntry[] = []; + // Maps each button to its current label and old index so that when we move them around it's not a problem + buttons.forEach((button, index) => { + buttonMap.push({ label: button, index: index }); + }); + + if (isMacintosh) { + if (cancelId !== undefined) { + const cancelButton = buttonMap.splice(cancelId, 1)[0]; + buttonMap.reverse(); + buttonMap.splice(buttonMap.length - 1, 0, cancelButton); + } + } + + return buttonMap; + } +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/dialog/error-inverse.svg b/src/vs/base/browser/ui/dialog/error-inverse.svg new file mode 100644 index 00000000000..51e9dc81b99 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/error-inverse.svg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#CACACC;} + .st1{fill:#E51400;} + .st2{fill:#FFFFFF;} + .st3{fill:#F6F6F6;fill-opacity:0;} + .st4{fill:#1A1A1A;} +</style> +<path id="outline_2_" class="st0" d="M-169.7-71.2c0,1.4-1.2,2.6-2.6,2.6c-1.4,0-2.6-1.2-2.6-2.6s1.2-2.6,2.6-2.6 + C-170.9-73.8-169.7-72.6-169.7-71.2z"/> +<path id="iconBg_2_" class="st1" d="M-172.3-73.5c-1.3,0-2.3,1-2.3,2.3s1,2.3,2.3,2.3s2.3-1,2.3-2.3S-171.1-73.5-172.3-73.5z + M-170.9-70.2l-0.5,0.5l-1-1l-1,1l-0.5-0.5l1-1l-1-1l0.5-0.5l1,1l1-1l0.5,0.5l-1,1L-170.9-70.2z"/> +<g id="iconFg_2_"> + <path class="st2" d="M-171.9-71.2l1,1l-0.5,0.5l-1-1l-1,1l-0.5-0.5l1-1l-1-1l0.5-0.5l1,1l1-1l0.5,0.5L-171.9-71.2z"/> +</g> +<path id="canvas_1_" class="st3" d="M16,16H0V0h16V16z"/> +<path id="outline_1_" class="st4" d="M16,8c0,4.4-3.6,8-8,8s-8-3.6-8-8s3.6-8,8-8S16,3.6,16,8z"/> +<path id="iconBg_1_" class="st1" d="M8,1C4.1,1,1,4.1,1,8c0,3.9,3.1,7,7,7s7-3.1,7-7S11.9,1,8,1z M12.4,11L11,12.4l-3-3l-3,3L3.6,11 + l3-3l-3-3L5,3.6l3,3l3-3L12.4,5l-3,3L12.4,11z"/> +<g id="iconFg_1_"> + <path class="st2" d="M9.4,8l3,3L11,12.4l-3-3l-3,3L3.6,11l3-3l-3-3L5,3.6l3,3l3-3L12.4,5L9.4,8z"/> +</g> +</svg> diff --git a/src/vs/base/browser/ui/dialog/error.svg b/src/vs/base/browser/ui/dialog/error.svg new file mode 100644 index 00000000000..04b66689011 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/error.svg @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#CACACC;} + .st1{fill:#E51400;} + .st2{fill:#FFFFFF;} + .st3{fill:#F6F6F6;fill-opacity:0;} +</style> +<path id="outline" class="st0" d="M16,8c0,4.4-3.6,8-8,8s-8-3.6-8-8s3.6-8,8-8S16,3.6,16,8z"/> +<path id="iconBg" class="st1" d="M8,1C4.1,1,1,4.1,1,8c0,3.9,3.1,7,7,7s7-3.1,7-7S11.9,1,8,1z M12.4,11L11,12.4l-3-3l-3,3L3.6,11 + l3-3l-3-3L5,3.6l3,3l3-3L12.4,5l-3,3L12.4,11z"/> +<g id="iconFg"> + <path class="st2" d="M9.4,8l3,3L11,12.4l-3-3l-3,3L3.6,11l3-3l-3-3L5,3.6l3,3l3-3L12.4,5L9.4,8z"/> +</g> +<path id="canvas_4_" class="st3" d="M16,16H0V0h16V16z"/> +<path id="outline_2_" class="st0" d="M-192.7-71.2c0,1.4-1.2,2.6-2.6,2.6s-2.6-1.2-2.6-2.6s1.2-2.6,2.6-2.6S-192.7-72.6-192.7-71.2z + "/> +<path id="iconBg_2_" class="st1" d="M-195.4-73.5c-1.3,0-2.3,1-2.3,2.3s1,2.3,2.3,2.3c1.3,0,2.3-1,2.3-2.3S-194.1-73.5-195.4-73.5z + M-193.9-70.2l-0.5,0.5l-1-1l-1,1l-0.5-0.5l1-1l-1-1l0.5-0.5l1,1l1-1l0.5,0.5l-1,1L-193.9-70.2z"/> +<g id="iconFg_2_"> + <path class="st2" d="M-194.9-71.2l1,1l-0.5,0.5l-1-1l-1,1l-0.5-0.5l1-1l-1-1l0.5-0.5l1,1l1-1l0.5,0.5L-194.9-71.2z"/> +</g> +</svg> diff --git a/src/vs/base/browser/ui/dialog/info-inverse.svg b/src/vs/base/browser/ui/dialog/info-inverse.svg new file mode 100644 index 00000000000..64b801a63be --- /dev/null +++ b/src/vs/base/browser/ui/dialog/info-inverse.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#F6F6F6;fill-opacity:0;} + .st1{fill:#1A1A1A;} + .st2{fill:#1BA1E2;} + .st3{fill:#FFFFFF;} +</style> +<path id="canvas_1_" class="st0" d="M16,16H0V0h16V16z"/> +<path id="outline_1_" class="st1" d="M0,8c0-4.4,3.6-8,8-8s8,3.6,8,8s-3.6,8-8,8S0,12.4,0,8z"/> +<path id="iconBg_1_" class="st2" d="M8,1C4.1,1,1,4.1,1,8s3.1,7,7,7s7-3.1,7-7S11.9,1,8,1z M9,13H7V6h2V13z M9,5H7V3h2V5z"/> +<g id="iconFg_1_"> + <path class="st3" d="M7,6h2v7H7V6z M7,5h2V3H7V5z"/> +</g> +</svg> diff --git a/src/vs/base/browser/ui/dialog/info.svg b/src/vs/base/browser/ui/dialog/info.svg new file mode 100644 index 00000000000..3c603528a74 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/info.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#F6F6F6;fill-opacity:0;} + .st1{fill:#CACACC;} + .st2{fill:#1BA1E2;} + .st3{fill:#FFFFFF;} +</style> +<path id="canvas" class="st0" d="M16,16H0V0h16V16z"/> +<path id="outline" class="st1" d="M0,8c0-4.4,3.6-8,8-8s8,3.6,8,8s-3.6,8-8,8S0,12.4,0,8z"/> +<path id="iconBg" class="st2" d="M8,1C4.1,1,1,4.1,1,8s3.1,7,7,7s7-3.1,7-7S11.8,1,8,1z M9,13H7V6h2V13z M9,5H7V3h2V5z"/> +<g id="iconFg"> + <path class="st3" d="M7,6h2v7H7V6z M7,5h2V3H7V5z"/> +</g> +</svg> diff --git a/src/vs/base/browser/ui/dialog/pending-dark.svg b/src/vs/base/browser/ui/dialog/pending-dark.svg new file mode 100644 index 00000000000..5f388381162 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/pending-dark.svg @@ -0,0 +1,31 @@ +<?xml version='1.0' standalone='no' ?> +<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'> + <style> + circle { + animation: ball 1.04s steps(8) infinite; + } + + circle:nth-child(2) { animation-delay: 0.13s; } + circle:nth-child(3) { animation-delay: 0.26s; } + circle:nth-child(4) { animation-delay: 0.39s; } + circle:nth-child(5) { animation-delay: 0.52s; } + circle:nth-child(6) { animation-delay: 0.65s; } + circle:nth-child(7) { animation-delay: 0.78s; } + circle:nth-child(8) { animation-delay: 0.91s; } + + @keyframes ball { + from { opacity: 1; } + to { opacity: 0.3; } + } + </style> + <g style="fill:grey;"> + <circle cx='5' cy='1' r='1' style='opacity:0.3;' /> + <circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' /> + <circle cx='9' cy='5' r='1' style='opacity:0.3;' /> + <circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' /> + <circle cx='5' cy='9' r='1' style='opacity:0.3;' /> + <circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' /> + <circle cx='1' cy='5' r='1' style='opacity:0.3;' /> + <circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' /> + </g> +</svg> diff --git a/src/vs/base/browser/ui/dialog/pending-hc.svg b/src/vs/base/browser/ui/dialog/pending-hc.svg new file mode 100644 index 00000000000..c6d0ec7e29f --- /dev/null +++ b/src/vs/base/browser/ui/dialog/pending-hc.svg @@ -0,0 +1,31 @@ +<?xml version='1.0' standalone='no' ?> +<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'> + <style> + circle { + animation: ball 1.04s steps(8) infinite; + } + + circle:nth-child(2) { animation-delay: 0.13s; } + circle:nth-child(3) { animation-delay: 0.26s; } + circle:nth-child(4) { animation-delay: 0.39s; } + circle:nth-child(5) { animation-delay: 0.52s; } + circle:nth-child(6) { animation-delay: 0.65s; } + circle:nth-child(7) { animation-delay: 0.78s; } + circle:nth-child(8) { animation-delay: 0.91s; } + + @keyframes ball { + from { opacity: 1; } + to { opacity: 0.3; } + } + </style> + <g style="fill:white;"> + <circle cx='5' cy='1' r='1' style='opacity:0.3;' /> + <circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' /> + <circle cx='9' cy='5' r='1' style='opacity:0.3;' /> + <circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' /> + <circle cx='5' cy='9' r='1' style='opacity:0.3;' /> + <circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' /> + <circle cx='1' cy='5' r='1' style='opacity:0.3;' /> + <circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' /> + </g> +</svg> diff --git a/src/vs/base/browser/ui/dialog/pending.svg b/src/vs/base/browser/ui/dialog/pending.svg new file mode 100644 index 00000000000..47ce444bb24 --- /dev/null +++ b/src/vs/base/browser/ui/dialog/pending.svg @@ -0,0 +1,31 @@ +<?xml version='1.0' standalone='no' ?> +<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'> + <style> + circle { + animation: ball 1.04s steps(8) infinite; + } + + circle:nth-child(2) { animation-delay: 0.13s; } + circle:nth-child(3) { animation-delay: 0.26s; } + circle:nth-child(4) { animation-delay: 0.39s; } + circle:nth-child(5) { animation-delay: 0.52s; } + circle:nth-child(6) { animation-delay: 0.65s; } + circle:nth-child(7) { animation-delay: 0.78s; } + circle:nth-child(8) { animation-delay: 0.91s; } + + @keyframes ball { + from { opacity: 1; } + to { opacity: 0.3; } + } + </style> + <g> + <circle cx='5' cy='1' r='1' style='opacity:0.3;' /> + <circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' /> + <circle cx='9' cy='5' r='1' style='opacity:0.3;' /> + <circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' /> + <circle cx='5' cy='9' r='1' style='opacity:0.3;' /> + <circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' /> + <circle cx='1' cy='5' r='1' style='opacity:0.3;' /> + <circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' /> + </g> +</svg> diff --git a/src/vs/base/browser/ui/dialog/warning-inverse.svg b/src/vs/base/browser/ui/dialog/warning-inverse.svg new file mode 100644 index 00000000000..a7f4afbcc9c --- /dev/null +++ b/src/vs/base/browser/ui/dialog/warning-inverse.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#F6F6F6;fill-opacity:0;} + .st1{fill:#1A1A1A;} + .st2{fill:#FFCC00;} +</style> +<title>StatusWarning_16x + + + + + diff --git a/src/vs/base/browser/ui/dialog/warning.svg b/src/vs/base/browser/ui/dialog/warning.svg new file mode 100644 index 00000000000..6d8cffe913e --- /dev/null +++ b/src/vs/base/browser/ui/dialog/warning.svg @@ -0,0 +1,15 @@ + + + + +StatusWarning_16x + + + + + diff --git a/src/vs/base/browser/ui/dropdown/dropdown.css b/src/vs/base/browser/ui/dropdown/dropdown.css index 9c870c7e96f..29ed0b73d7d 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.css +++ b/src/vs/base/browser/ui/dropdown/dropdown.css @@ -9,23 +9,8 @@ padding: 0; } -.monaco-dropdown > .dropdown-label, -.monaco-dropdown > .dropdown-action { +.monaco-dropdown > .dropdown-label { display: inline-block; cursor: pointer; height: 100%; } - -.monaco-dropdown > .dropdown-action { - vertical-align: top; -} - -.monaco-dropdown > .dropdown-action > .action-label:hover { - color: inherit; - text-decoration: none; -} - -.monaco-dropdown > .dropdown-action, -.monaco-dropdown > .dropdown-action > .action-label { - display: inline-block; -} \ 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 00c510111f3..ee87b9a4fed 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -6,7 +6,7 @@ import 'vs/css!./dropdown'; import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch'; import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; -import { BaseActionItem, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionViewItem, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IContextViewProvider, IAnchor, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IMenuOptions } from 'vs/base/browser/ui/menu/menu'; @@ -16,7 +16,7 @@ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; export interface ILabelRenderer { - (container: HTMLElement): IDisposable; + (container: HTMLElement): IDisposable | null; } export interface IBaseDropdownOptions { @@ -26,9 +26,9 @@ export interface IBaseDropdownOptions { export class BaseDropdown extends ActionRunner { private _element: HTMLElement; - private boxContainer: HTMLElement; - private _label: HTMLElement; - private contents: HTMLElement; + private boxContainer?: HTMLElement; + private _label?: HTMLElement; + private contents?: HTMLElement; private visible: boolean; constructor(container: HTMLElement, options: IBaseDropdownOptions) { @@ -40,18 +40,18 @@ export class BaseDropdown extends ActionRunner { let labelRenderer = options.labelRenderer; if (!labelRenderer) { - labelRenderer = (container: HTMLElement): IDisposable => { + labelRenderer = (container: HTMLElement): IDisposable | null => { container.textContent = options.label || ''; return null; }; } - [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + for (const event of [EventType.CLICK, EventType.MOUSE_DOWN, GestureEventType.Tap]) { this._register(addDisposableListener(this._label, event, e => EventHelper.stop(e, true))); // prevent default click behaviour to trigger - }); + } - [EventType.MOUSE_DOWN, GestureEventType.Tap].forEach(event => { + for (const event of [EventType.MOUSE_DOWN, GestureEventType.Tap]) { this._register(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) @@ -63,10 +63,10 @@ export class BaseDropdown extends ActionRunner { this.show(); } })); - }); + } this._register(addDisposableListener(this._label, EventType.KEY_UP, e => { - const event = new StandardKeyboardEvent(e as KeyboardEvent); + const event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { EventHelper.stop(e, true); // https://github.com/Microsoft/vscode/issues/57997 @@ -90,12 +90,14 @@ export class BaseDropdown extends ActionRunner { return this._element; } - get label(): HTMLElement { + get label() { return this._label; } set tooltip(tooltip: string) { - this._label.title = tooltip; + if (this._label) { + this._label.title = tooltip; + } } show(): void { @@ -116,17 +118,17 @@ export class BaseDropdown extends ActionRunner { if (this.boxContainer) { this.boxContainer.remove(); - this.boxContainer = null; + this.boxContainer = undefined; } if (this.contents) { this.contents.remove(); - this.contents = null; + this.contents = undefined; } if (this._label) { this._label.remove(); - this._label = null; + this._label = undefined; } } } @@ -180,7 +182,7 @@ export class Dropdown extends BaseDropdown { } } - protected renderContents(container: HTMLElement): IDisposable { + protected renderContents(container: HTMLElement): IDisposable | null { return null; } } @@ -204,7 +206,7 @@ export class DropdownMenu extends BaseDropdown { private _contextMenuProvider: IContextMenuProvider; private _menuOptions: IMenuOptions; private _actions: IAction[]; - private actionProvider: IActionProvider; + private actionProvider?: IActionProvider; private menuClassName: string; constructor(container: HTMLElement, options: IDropdownMenuOptions) { @@ -245,11 +247,11 @@ export class DropdownMenu extends BaseDropdown { getAnchor: () => this.element, getActions: () => this.actions, getActionsContext: () => this.menuOptions ? this.menuOptions.context : null, - getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : null, - getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : null, + getActionViewItem: action => this.menuOptions && this.menuOptions.actionViewItemProvider ? this.menuOptions.actionViewItemProvider(action) : undefined, + getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : undefined, getMenuClassName: () => this.menuClassName, onHide: () => this.onHide(), - actionRunner: this.menuOptions ? this.menuOptions.actionRunner : null, + actionRunner: this.menuOptions ? this.menuOptions.actionRunner : undefined, anchorAlignment: this.menuOptions.anchorAlignment }); } @@ -264,23 +266,23 @@ export class DropdownMenu extends BaseDropdown { } } -export class DropdownMenuActionItem extends BaseActionItem { +export class DropdownMenuActionViewItem extends BaseActionViewItem { private menuActionsOrProvider: any; private dropdownMenu: DropdownMenu; private contextMenuProvider: IContextMenuProvider; - private actionItemProvider: IActionItemProvider; - private keybindings: (action: IAction) => ResolvedKeybinding; - private clazz: string; - private anchorAlignmentProvider: () => AnchorAlignment; + private actionViewItemProvider?: IActionViewItemProvider; + private keybindings?: (action: IAction) => ResolvedKeybinding | undefined; + private clazz: string | undefined; + private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; - constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment) { + constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment) { super(null, action); this.menuActionsOrProvider = menuActionsOrProvider; this.contextMenuProvider = contextMenuProvider; - this.actionItemProvider = actionItemProvider; + this.actionViewItemProvider = actionViewItemProvider; this.actionRunner = actionRunner; this.keybindings = keybindings; this.clazz = clazz; @@ -288,9 +290,11 @@ export class DropdownMenuActionItem extends BaseActionItem { } render(container: HTMLElement): void { - const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable => { + const labelRenderer: ILabelRenderer = (el: HTMLElement): IDisposable | null => { this.element = append(el, $('a.action-label.icon')); - addClasses(this.element, this.clazz); + if (this.clazz) { + addClasses(this.element, this.clazz); + } this.element.tabIndex = 0; this.element.setAttribute('role', 'button'); @@ -314,16 +318,23 @@ export class DropdownMenuActionItem extends BaseActionItem { this.dropdownMenu = this._register(new DropdownMenu(container, options)); - const that = this; this.dropdownMenu.menuOptions = { - actionItemProvider: this.actionItemProvider, + actionViewItemProvider: this.actionViewItemProvider, actionRunner: this.actionRunner, getKeyBinding: this.keybindings, - context: this._context, - get anchorAlignment(): AnchorAlignment { - return that.anchorAlignmentProvider(); - } + context: this._context }; + + if (this.anchorAlignmentProvider) { + const that = this; + + this.dropdownMenu.menuOptions = { + ...this.dropdownMenu.menuOptions, + get anchorAlignment(): AnchorAlignment { + return that.anchorAlignmentProvider!(); + } + }; + } } setActionContext(newContext: any): void { diff --git a/src/vs/base/browser/ui/findinput/findInput.css b/src/vs/base/browser/ui/findinput/findInput.css index c659f260e14..4bb6b8cec63 100644 --- a/src/vs/base/browser/ui/findinput/findInput.css +++ b/src/vs/base/browser/ui/findinput/findInput.css @@ -11,7 +11,6 @@ .monaco-findInput .monaco-inputbox { font-size: 13px; width: 100%; - height: 25px; } .monaco-findInput > .controls { diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 0ba7da016be..38a5082b141 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -23,6 +23,7 @@ export interface IFindInputOptions extends IFindInputStyles { readonly width?: number; readonly validation?: IInputValidator; readonly label: string; + readonly flexibleHeight?: boolean; readonly appendCaseSensitiveLabel?: string; readonly appendWholeWordsLabel?: string; @@ -41,26 +42,25 @@ export class FindInput extends Widget { static readonly OPTION_CHANGE: string = 'optionChange'; private contextViewProvider: IContextViewProvider; - private width: number; private placeholder: string; - private validation: IInputValidator; + private validation?: IInputValidator; private label: string; private fixFocusOnOptionClickEnabled = true; - private inputActiveOptionBorder: Color; - private inputBackground: Color; - private inputForeground: Color; - private inputBorder: Color; + private inputActiveOptionBorder?: Color; + private inputBackground?: Color; + private inputForeground?: Color; + private inputBorder?: Color; - private inputValidationInfoBorder: Color; - private inputValidationInfoBackground: Color; - private inputValidationInfoForeground: Color; - private inputValidationWarningBorder: Color; - private inputValidationWarningBackground: Color; - private inputValidationWarningForeground: Color; - private inputValidationErrorBorder: Color; - private inputValidationErrorBackground: Color; - private inputValidationErrorForeground: Color; + private inputValidationInfoBorder?: Color; + private inputValidationInfoBackground?: Color; + private inputValidationInfoForeground?: Color; + private inputValidationWarningBorder?: Color; + private inputValidationWarningBackground?: Color; + private inputValidationWarningForeground?: Color; + private inputValidationErrorBorder?: Color; + private inputValidationErrorBackground?: Color; + private inputValidationErrorForeground?: Color; private regex: RegexCheckbox; private wholeWords: WholeWordsCheckbox; @@ -89,10 +89,9 @@ export class FindInput extends Widget { private _onRegexKeyDown = this._register(new Emitter()); public readonly onRegexKeyDown: Event = this._onRegexKeyDown.event; - constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, private readonly _showOptionButtons: boolean, options?: IFindInputOptions) { + constructor(parent: HTMLElement | null, contextViewProvider: IContextViewProvider, private readonly _showOptionButtons: boolean, options: IFindInputOptions) { super(); this.contextViewProvider = contextViewProvider; - this.width = options.width || 100; this.placeholder = options.placeholder || ''; this.validation = options.validation; this.label = options.label || NLS_DEFAULT_LABEL; @@ -112,15 +111,9 @@ export class FindInput extends Widget { this.inputValidationErrorBackground = options.inputValidationErrorBackground; this.inputValidationErrorForeground = options.inputValidationErrorForeground; - this.regex = null; - this.wholeWords = null; - this.caseSensitive = null; - this.domNode = null; - this.inputBox = null; + this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history || [], !!options.flexibleHeight); - this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history); - - if (Boolean(parent)) { + if (parent) { parent.appendChild(this.domNode); } @@ -164,13 +157,6 @@ export class FindInput extends Widget { this.focus(); } - public setWidth(newWidth: number): void { - this.width = newWidth; - this.domNode.style.width = this.width + 'px'; - this.contextViewProvider.layout(); - this.setInputWidth(); - } - public getValue(): string { return this.inputBox.value; } @@ -245,7 +231,6 @@ export class FindInput extends Widget { public setCaseSensitive(value: boolean): void { this.caseSensitive.checked = value; - this.setInputWidth(); } public getWholeWords(): boolean { @@ -254,7 +239,6 @@ export class FindInput extends Widget { public setWholeWords(value: boolean): void { this.wholeWords.checked = value; - this.setInputWidth(); } public getRegex(): boolean { @@ -263,7 +247,6 @@ export class FindInput extends Widget { public setRegex(value: boolean): void { this.regex.checked = value; - this.setInputWidth(); this.validate(); } @@ -282,21 +265,15 @@ export class FindInput extends Widget { dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); } - private setInputWidth(): void { - let w = this.width - this.caseSensitive.width() - this.wholeWords.width() - this.regex.width(); - this.inputBox.width = w; - } - - private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string, history: string[]): void { + private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string, history: string[], flexibleHeight: boolean): void { this.domNode = document.createElement('div'); - this.domNode.style.width = this.width + 'px'; dom.addClass(this.domNode, 'monaco-findInput'); this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, { placeholder: this.placeholder || '', ariaLabel: this.label || '', validationOptions: { - validation: this.validation || null + validation: this.validation }, inputBackground: this.inputBackground, inputForeground: this.inputForeground, @@ -310,7 +287,8 @@ export class FindInput extends Widget { inputValidationErrorBackground: this.inputValidationErrorBackground, inputValidationErrorForeground: this.inputValidationErrorForeground, inputValidationErrorBorder: this.inputValidationErrorBorder, - history + history, + flexibleHeight })); this.regex = this._register(new RegexCheckbox({ @@ -323,7 +301,6 @@ export class FindInput extends Widget { if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { this.inputBox.focus(); } - this.setInputWidth(); this.validate(); })); this._register(this.regex.onKeyDown(e => { @@ -340,7 +317,6 @@ export class FindInput extends Widget { if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { this.inputBox.focus(); } - this.setInputWidth(); this.validate(); })); @@ -354,20 +330,27 @@ export class FindInput extends Widget { if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { this.inputBox.focus(); } - this.setInputWidth(); this.validate(); })); this._register(this.caseSensitive.onKeyDown(e => { this._onCaseSensitiveKeyDown.fire(e); })); + if (this._showOptionButtons) { + const paddingRight = (this.caseSensitive.width() + this.wholeWords.width() + this.regex.width()) + 'px'; + this.inputBox.inputElement.style.paddingRight = paddingRight; + if (this.inputBox.mirrorElement) { + this.inputBox.mirrorElement.style.paddingRight = paddingRight; + } + } + // Arrow-Key support to navigate between options let indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode]; this.onkeydown(this.domNode, (event: IKeyboardEvent) => { if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) { let index = indexes.indexOf(document.activeElement); if (index >= 0) { - let newIndex: number; + let newIndex: number = -1; if (event.equals(KeyCode.RightArrow)) { newIndex = (index + 1) % indexes.length; } else if (event.equals(KeyCode.LeftArrow)) { @@ -389,7 +372,6 @@ export class FindInput extends Widget { } }); - this.setInputWidth(); let controls = document.createElement('div'); controls.className = 'controls'; @@ -402,19 +384,27 @@ export class FindInput extends Widget { } public validate(): void { - this.inputBox.validate(); + if (this.inputBox) { + this.inputBox.validate(); + } } public showMessage(message: InputBoxMessage): void { - this.inputBox.showMessage(message); + if (this.inputBox) { + this.inputBox.showMessage(message); + } } public clearMessage(): void { - this.inputBox.hideMessage(); + if (this.inputBox) { + this.inputBox.hideMessage(); + } } private clearValidation(): void { - this.inputBox.hideMessage(); + if (this.inputBox) { + this.inputBox.hideMessage(); + } } public dispose(): void { diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 336329a05e2..44e3deacd6c 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -8,7 +8,9 @@ import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles } from './gridview'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; +import { $ } from 'vs/base/browser/dom'; +import { LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; export { Orientation } from './gridview'; @@ -186,6 +188,7 @@ export interface IGridStyles extends IGridViewStyles { } export interface IGridOptions { styles?: IGridStyles; + proportionalLayout?: boolean; } export class Grid implements IDisposable { @@ -227,6 +230,10 @@ export class Grid implements IDisposable { this.gridview.layout(width, height); } + hasView(view: T): boolean { + return this.views.has(view); + } + addView(newView: T, size: number | Sizing, referenceView: T, direction: Direction): void { if (this.views.has(newView)) { throw new Error('Can\'t add same view twice'); @@ -255,7 +262,7 @@ export class Grid implements IDisposable { this._addView(newView, viewSize, location); } - protected _addView(newView: T, size: number | GridViewSizing, location): void { + protected _addView(newView: T, size: number | GridViewSizing, location: number[]): void { this.views.set(newView, newView.element); this.gridview.addView(newView, size, location); } @@ -450,7 +457,7 @@ export class SerializableGrid extends Grid { return { children, box }; } else if (json.type === 'leaf') { - const view = deserializer.fromJSON(json.data) as T; + const view: T = deserializer.fromJSON(json.data); return { view, box }; } @@ -474,9 +481,9 @@ export class SerializableGrid extends Grid { throw new Error('Invalid JSON: \'height\' property must be a number.'); } - const orientation = json.orientation as Orientation; - const width = json.width as number; - const height = json.height as number; + const orientation = json.orientation; + const width = json.width; + const height = json.height; const box: Box = { top: 0, left: 0, width, height }; const root = SerializableGrid.deserializeNode(json.root, orientation, box, deserializer) as GridBranchNode; @@ -646,3 +653,63 @@ export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerialize height: height || 1 }; } + +export class View implements IView { + + readonly element = $('.grid-view-view'); + + private visible = false; + private width: number | undefined; + private height: number | undefined; + private orientation: Orientation = Orientation.HORIZONTAL; + + get minimumWidth(): number { return this.visible ? this.view.minimumWidth : 0; } + get maximumWidth(): number { return this.visible ? this.view.maximumWidth : (this.orientation === Orientation.HORIZONTAL ? 0 : Number.POSITIVE_INFINITY); } + get minimumHeight(): number { return this.visible ? this.view.minimumHeight : 0; } + get maximumHeight(): number { return this.visible ? this.view.maximumHeight : (this.orientation === Orientation.VERTICAL ? 0 : Number.POSITIVE_INFINITY); } + + private onDidChangeVisibility = new Emitter<{ width: number; height: number; } | undefined>(); + readonly onDidChange: Event<{ width: number; height: number; } | undefined>; + + get priority(): LayoutPriority | undefined { return this.view.priority; } + get snapSize(): number | undefined { return this.visible ? this.view.snapSize : undefined; } + + constructor(private view: IView) { + this.show(); + this.onDidChange = Event.any(this.onDidChangeVisibility.event, Event.filter(view.onDidChange, () => this.visible)); + } + + show(): void { + if (this.visible) { + return; + } + + this.visible = true; + + this.element.appendChild(this.view.element); + this.onDidChangeVisibility.fire(typeof this.width === 'number' ? { width: this.width, height: this.height! } : undefined); + } + + hide(): void { + if (!this.visible) { + return; + } + + this.visible = false; + + this.element.removeChild(this.view.element); + this.onDidChangeVisibility.fire(undefined); + } + + layout(width: number, height: number, orientation: Orientation): void { + this.orientation = orientation; + + if (!this.visible) { + return; + } + + this.view.layout(width, height, orientation); + this.width = width; + this.height = height; + } +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index f0ac2e0984d..e4e6f8909ad 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./gridview'; -import { Event, anyEvent, Emitter, mapEvent, Relay } from 'vs/base/common/event'; +import { Event, Emitter, Relay } from 'vs/base/common/event'; import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { SplitView, IView as ISplitView, Sizing, LayoutPriority, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -21,10 +21,10 @@ export interface IView { readonly maximumWidth: number; readonly minimumHeight: number; readonly maximumHeight: number; - readonly onDidChange: Event<{ width: number; height: number; }>; + readonly onDidChange: Event<{ width: number; height: number; } | undefined>; readonly priority?: LayoutPriority; readonly snapSize?: number; - layout(width: number, height: number): void; + layout(width: number, height: number, orientation: Orientation): void; } export function orthogonal(orientation: Orientation): Orientation { @@ -147,10 +147,10 @@ class BranchNode implements ISplitView, IDisposable { this._orthogonalSize = orthogonalSize; this.element = $('.monaco-grid-branch-node'); - this.splitview = new SplitView(this.element, { orientation, styles }); + this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout }); this.splitview.layout(size); - const onDidSashReset = mapEvent(this.splitview.onDidSashReset, i => [i]); + const onDidSashReset = Event.map(this.splitview.onDidSashReset, i => [i]); this.splitviewSashResetDisposable = onDidSashReset(this._onDidSashReset.fire, this._onDidSashReset); } @@ -300,15 +300,15 @@ class BranchNode implements ISplitView, IDisposable { } private onDidChildrenChange(): void { - const onDidChildrenChange = anyEvent(...this.children.map(c => c.onDidChange)); + const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined); this.childrenChangeDisposable.dispose(); this.childrenChangeDisposable = onDidChildrenChange(this._onDidChange.fire, this._onDidChange); - const onDidChildrenSashReset = anyEvent(...this.children.map((c, i) => mapEvent(c.onDidSashReset, location => [i, ...location]))); + const onDidChildrenSashReset = Event.any(...this.children.map((c, i) => Event.map(c.onDidSashReset, location => [i, ...location]))); this.childrenSashResetDisposable.dispose(); this.childrenSashResetDisposable = onDidChildrenSashReset(this._onDidSashReset.fire, this._onDidSashReset); - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } trySet2x2(other: BranchNode): IDisposable { @@ -348,8 +348,8 @@ class BranchNode implements ISplitView, IDisposable { mySash.linkedSash = otherSash; otherSash.linkedSash = mySash; - this._onDidChange.fire(); - other._onDidChange.fire(); + this._onDidChange.fire(undefined); + other._onDidChange.fire(undefined); return toDisposable(() => { mySash.linkedSash = otherSash.linkedSash = undefined; @@ -391,7 +391,7 @@ class LeafNode implements ISplitView, IDisposable { set linkedWidthNode(node: LeafNode | undefined) { this._onDidLinkedWidthNodeChange.input = node ? node._onDidViewChange : Event.None; this._linkedWidthNode = node; - this._onDidSetLinkedNode.fire(); + this._onDidSetLinkedNode.fire(undefined); } private _onDidLinkedHeightNodeChange = new Relay(); @@ -400,7 +400,7 @@ class LeafNode implements ISplitView, IDisposable { set linkedHeightNode(node: LeafNode | undefined) { this._onDidLinkedHeightNodeChange.input = node ? node._onDidViewChange : Event.None; this._linkedHeightNode = node; - this._onDidSetLinkedNode.fire(); + this._onDidSetLinkedNode.fire(undefined); } private _onDidSetLinkedNode = new Emitter(); @@ -414,8 +414,8 @@ class LeafNode implements ISplitView, IDisposable { ) { this._orthogonalSize = orthogonalSize; - this._onDidViewChange = mapEvent(this.view.onDidChange, this.orientation === Orientation.HORIZONTAL ? e => e && e.width : e => e && e.height); - this.onDidChange = anyEvent(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event); + this._onDidViewChange = Event.map(this.view.onDidChange, e => e && (this.orientation === Orientation.VERTICAL ? e.width : e.height)); + this.onDidChange = Event.any(this._onDidViewChange, this._onDidSetLinkedNode.event, this._onDidLinkedWidthNodeChange.event, this._onDidLinkedHeightNodeChange.event); } get width(): number { @@ -480,12 +480,12 @@ class LeafNode implements ISplitView, IDisposable { layout(size: number): void { this._size = size; - return this.view.layout(this.width, this.height); + return this.view.layout(this.width, this.height, orthogonal(this.orientation)); } orthogonalLayout(size: number): void { this._orthogonalSize = size; - return this.view.layout(this.width, this.height); + return this.view.layout(this.width, this.height, orthogonal(this.orientation)); } dispose(): void { } @@ -547,7 +547,7 @@ export class GridView implements IDisposable { this._root = root; this.element.appendChild(root.element); this.onDidSashResetRelay.input = root.onDidSashReset; - this._onDidChange.input = mapEvent(root.onDidChange, () => undefined); // TODO + this._onDidChange.input = Event.map(root.onDidChange, () => undefined); // TODO } get orientation(): Orientation { @@ -802,8 +802,7 @@ export class GridView implements IDisposable { const children: GridNode[] = []; let offset = 0; - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; + for (const child of node.children) { const childOrientation = orthogonal(orientation); const childBox: Box = orientation === Orientation.HORIZONTAL ? { top: box.top, left: box.left + offset, width: child.width, height: box.height } diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index a8e3d6c3200..63c805b4237 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -3,17 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; -import * as dom from 'vs/base/browser/dom'; import * as objects from 'vs/base/common/objects'; import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; +import { escape } from 'vs/base/common/strings'; export interface IHighlight { start: number; end: number; } -export class HighlightedLabel implements IDisposable { +export class HighlightedLabel { private domNode: HTMLElement; private text: string; @@ -32,7 +31,7 @@ export class HighlightedLabel implements IDisposable { return this.domNode; } - set(text: string, highlights: IHighlight[] = [], title: string = '', escapeNewLines?: boolean) { + set(text: string | undefined, highlights: IHighlight[] = [], title: string = '', escapeNewLines?: boolean) { if (!text) { text = ''; } @@ -54,55 +53,47 @@ export class HighlightedLabel implements IDisposable { this.render(); } - private render() { - dom.clearNode(this.domNode); + private render(): void { - let htmlContent: string[] = [], - highlight: IHighlight, - pos = 0; + let htmlContent = ''; + let pos = 0; - for (let i = 0; i < this.highlights.length; i++) { - highlight = this.highlights[i]; + for (const highlight of this.highlights) { if (highlight.end === highlight.start) { continue; } if (pos < highlight.start) { - htmlContent.push(''); + htmlContent += ''; const substring = this.text.substring(pos, highlight.start); - htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); - htmlContent.push(''); + htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring); + htmlContent += ''; pos = highlight.end; } - htmlContent.push(''); + htmlContent += ''; const substring = this.text.substring(highlight.start, highlight.end); - htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); - htmlContent.push(''); + htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring); + htmlContent += ''; pos = highlight.end; } if (pos < this.text.length) { - htmlContent.push(''); + htmlContent += ''; const substring = this.text.substring(pos); - htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); - htmlContent.push(''); + htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring); + htmlContent += ''; } - this.domNode.innerHTML = htmlContent.join(''); + this.domNode.innerHTML = htmlContent; this.domNode.title = this.title; this.didEverRender = true; } - dispose() { - this.text = null!; // StrictNullOverride: nulling out ok in dispose - this.highlights = null!; // StrictNullOverride: nulling out ok in dispose - } - static escapeNewLines(text: string, highlights: IHighlight[]): string { let total = 0; let extra = 0; - return text.replace(/\r\n|\r|\n/, (match, offset) => { + return text.replace(/\r\n|\r|\n/g, (match, offset) => { extra = match === '\r\n' ? -1 : 0; offset += total; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 752437df1ba..a61b1bba525 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -7,7 +7,7 @@ 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 { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; export interface IIconLabelCreationOptions { supportHighlights?: boolean; @@ -100,13 +100,13 @@ export class IconLabel extends Disposable { this.labelDescriptionContainer = this._register(new FastLabelNode(dom.append(this.domNode.element, dom.$('.monaco-icon-label-description-container')))); if (options && options.supportHighlights) { - this.labelNode = this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !options.donotSupportOcticons)); + this.labelNode = new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !options.donotSupportOcticons); } else { this.labelNode = this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')))); } if (options && options.supportDescriptionHighlights) { - this.descriptionNodeFactory = () => this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !options.donotSupportOcticons)); + this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !options.donotSupportOcticons); } else { this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')))); } @@ -116,13 +116,7 @@ export class IconLabel extends Disposable { return this.domNode.element; } - onClick(callback: (event: MouseEvent) => void): IDisposable { - return combinedDisposable([ - dom.addDisposableListener(this.labelDescriptionContainer.element, dom.EventType.CLICK, (e: MouseEvent) => callback(e)), - ]); - } - - setValue(label?: string, description?: string, options?: IIconLabelValueOptions): void { + setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void { const classes = ['monaco-icon-label']; if (options) { if (options.extraClasses) { @@ -138,7 +132,7 @@ export class IconLabel extends Disposable { this.domNode.title = options && options.title ? options.title : ''; if (this.labelNode instanceof HighlightedLabel) { - this.labelNode.set(label || '', options ? options.matches : void 0, options && options.title ? options.title : void 0, options && options.labelEscapeNewLines); + this.labelNode.set(label || '', options ? options.matches : undefined, options && options.title ? options.title : undefined, options && options.labelEscapeNewLines); } else { this.labelNode.textContent = label || ''; } @@ -149,7 +143,7 @@ export class IconLabel extends Disposable { } if (this.descriptionNode instanceof HighlightedLabel) { - this.descriptionNode.set(description || '', options ? options.descriptionMatches : void 0); + this.descriptionNode.set(description || '', options ? options.descriptionMatches : undefined); if (options && options.descriptionTitle) { this.descriptionNode.element.title = options.descriptionTitle; } else { @@ -163,4 +157,3 @@ export class IconLabel extends Disposable { } } } - diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css index df0073541df..294ca475662 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.css +++ b/src/vs/base/browser/ui/inputbox/inputBox.css @@ -74,7 +74,6 @@ box-sizing: border-box; white-space: pre-wrap; visibility: hidden; - min-height: 26px; word-wrap: break-word; } diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 947e36a2c77..7f79949da48 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -23,41 +23,41 @@ import { IHistoryNavigationWidget } from 'vs/base/browser/history'; const $ = dom.$; export interface IInputOptions extends IInputBoxStyles { - placeholder?: string; - ariaLabel?: string; - type?: string; - validationOptions?: IInputValidationOptions; - flexibleHeight?: boolean; - actions?: IAction[]; + readonly placeholder?: string; + readonly ariaLabel?: string; + readonly type?: string; + readonly validationOptions?: IInputValidationOptions; + readonly flexibleHeight?: boolean; + readonly actions?: IAction[]; } export interface IInputBoxStyles { - inputBackground?: Color; - inputForeground?: Color; - inputBorder?: Color; - inputValidationInfoBorder?: Color; - inputValidationInfoBackground?: Color; - inputValidationInfoForeground?: Color; - inputValidationWarningBorder?: Color; - inputValidationWarningBackground?: Color; - inputValidationWarningForeground?: Color; - inputValidationErrorBorder?: Color; - inputValidationErrorBackground?: Color; - inputValidationErrorForeground?: Color; + readonly inputBackground?: Color; + readonly inputForeground?: Color; + readonly inputBorder?: Color; + readonly inputValidationInfoBorder?: Color; + readonly inputValidationInfoBackground?: Color; + readonly inputValidationInfoForeground?: Color; + readonly inputValidationWarningBorder?: Color; + readonly inputValidationWarningBackground?: Color; + readonly inputValidationWarningForeground?: Color; + readonly inputValidationErrorBorder?: Color; + readonly inputValidationErrorBackground?: Color; + readonly inputValidationErrorForeground?: Color; } export interface IInputValidator { - (value: string): IMessage; + (value: string): IMessage | null; } export interface IMessage { - content: string; - formatContent?: boolean; // defaults to false - type?: MessageType; + readonly content: string; + readonly formatContent?: boolean; // defaults to false + readonly type?: MessageType; } export interface IInputValidationOptions { - validation: IInputValidator; + validation?: IInputValidator; } export const enum MessageType { @@ -83,32 +83,32 @@ const defaultOpts = { }; export class InputBox extends Widget { - private contextViewProvider: IContextViewProvider; + private contextViewProvider?: IContextViewProvider; element: HTMLElement; private input: HTMLInputElement; private mirror: HTMLElement; - private actionbar: ActionBar; + private actionbar?: ActionBar; private options: IInputOptions; - private message: IMessage; + private message: IMessage | null; private placeholder: string; private ariaLabel: string; - private validation: IInputValidator; - private state = 'idle'; - private cachedHeight: number; + private validation?: IInputValidator; + private state: string | null = 'idle'; + private cachedHeight: number | null; - private inputBackground: Color; - private inputForeground: Color; - private inputBorder: Color; + private inputBackground?: Color; + private inputForeground?: Color; + private inputBorder?: Color; - private inputValidationInfoBorder: Color; - private inputValidationInfoBackground: Color; - private inputValidationInfoForeground: Color; - private inputValidationWarningBorder: Color; - private inputValidationWarningBackground: Color; - private inputValidationWarningForeground: Color; - private inputValidationErrorBorder: Color; - private inputValidationErrorBackground: Color; - private inputValidationErrorForeground: Color; + private inputValidationInfoBorder?: Color; + private inputValidationInfoBackground?: Color; + private inputValidationInfoForeground?: Color; + private inputValidationWarningBorder?: Color; + private inputValidationWarningBackground?: Color; + private inputValidationWarningForeground?: Color; + private inputValidationErrorBorder?: Color; + private inputValidationErrorBackground?: Color; + private inputValidationErrorForeground?: Color; private _onDidChange = this._register(new Emitter()); public readonly onDidChange: Event = this._onDidChange.event; @@ -116,7 +116,7 @@ export class InputBox extends Widget { private _onDidHeightChange = this._register(new Emitter()); public readonly onDidHeightChange: Event = this._onDidHeightChange.event; - constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) { + constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options?: IInputOptions) { super(); this.contextViewProvider = contextViewProvider; @@ -150,7 +150,7 @@ export class InputBox extends Widget { let tagName = this.options.flexibleHeight ? 'textarea' : 'input'; let wrapper = dom.append(this.element, $('.wrapper')); - this.input = dom.append(wrapper, $(tagName + '.input')); + this.input = dom.append(wrapper, $(tagName + '.input')); this.input.setAttribute('autocorrect', 'off'); this.input.setAttribute('autocapitalize', 'off'); this.input.setAttribute('spellcheck', 'false'); @@ -160,6 +160,7 @@ export class InputBox extends Widget { if (this.options.flexibleHeight) { this.mirror = dom.append(wrapper, $('div.mirror')); + this.mirror.innerHTML = ' '; } else { this.input.type = this.options.type || 'text'; this.input.setAttribute('wrap', 'off'); @@ -229,6 +230,10 @@ export class InputBox extends Widget { } } + public get mirrorElement(): HTMLElement { + return this.mirror; + } + public get inputElement(): HTMLInputElement { return this.input; } @@ -291,6 +296,9 @@ export class InputBox extends Widget { public set width(width: number) { this.input.style.width = width + 'px'; + if (this.mirror) { + this.mirror.style.width = width + 'px'; + } } public showMessage(message: IMessage, force?: boolean): void { @@ -357,7 +365,7 @@ export class InputBox extends Widget { return !errorMsg; } - private stylesForType(type: MessageType): { border: Color; background: Color; foreground: Color } { + public stylesForType(type: MessageType | undefined): { border: Color | undefined; background: Color | undefined; foreground: Color | undefined } { switch (type) { case MessageType.INFO: return { border: this.inputValidationInfoBorder, background: this.inputValidationInfoBackground, foreground: this.inputValidationInfoForeground }; case MessageType.WARNING: return { border: this.inputValidationWarningBorder, background: this.inputValidationWarningBackground, foreground: this.inputValidationWarningForeground }; @@ -365,7 +373,7 @@ export class InputBox extends Widget { } } - private classForType(type: MessageType): string { + private classForType(type: MessageType | undefined): string { switch (type) { case MessageType.INFO: return 'info'; case MessageType.WARNING: return 'warning'; @@ -387,6 +395,10 @@ export class InputBox extends Widget { getAnchor: () => this.element, anchorAlignment: AnchorAlignment.RIGHT, render: (container: HTMLElement) => { + if (!this.message) { + return null; + } + div = dom.append(container, $('.monaco-inputbox-container')); layout(); @@ -429,7 +441,7 @@ export class InputBox extends Widget { this.validate(); this.updateMirror(); - if (this.state === 'open') { + if (this.state === 'open' && this.contextViewProvider) { this.contextViewProvider.layout(); } } @@ -440,9 +452,16 @@ export class InputBox extends Widget { } const value = this.value || this.placeholder; - let lastCharCode = value.charCodeAt(value.length - 1); - let suffix = lastCharCode === 10 ? ' ' : ''; - this.mirror.textContent = value + suffix; + const lastCharCode = value.charCodeAt(value.length - 1); + const suffix = lastCharCode === 10 ? ' ' : ''; + const mirrorTextContent = value + suffix; + + if (mirrorTextContent) { + this.mirror.textContent = value + suffix; + } else { + this.mirror.innerHTML = ' '; + } + this.layout(); } @@ -498,15 +517,13 @@ export class InputBox extends Widget { public dispose(): void { this._hideMessage(); - this.element = null; - this.input = null; - this.contextViewProvider = null; + this.element = null!; // StrictNullOverride: nulling out ok in dispose + this.input = null!; // StrictNullOverride: nulling out ok in dispose + this.contextViewProvider = undefined; this.message = null; - this.placeholder = null; - this.ariaLabel = null; - this.validation = null; + this.validation = undefined; this.state = null; - this.actionbar = null; + this.actionbar = undefined; super.dispose(); } @@ -520,7 +537,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge private readonly history: HistoryNavigator; - constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, options: IHistoryInputOptions) { + constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options: IHistoryInputOptions) { super(container, contextViewProvider, options); this.history = new HistoryNavigator(options.history, 100); } @@ -571,7 +588,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge this.history.clear(); } - private getCurrentValue(): string { + private getCurrentValue(): string | null { let currentValue = this.history.current(); if (!currentValue) { currentValue = this.history.last(); @@ -580,11 +597,11 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge return currentValue; } - private getPreviousValue(): string { + private getPreviousValue(): string | null { return this.history.previous() || this.history.first(); } - private getNextValue(): string { + private getNextValue(): string | null { return this.history.next() || this.history.last(); } } diff --git a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css index dd24df5df23..e33663d34b1 100644 --- a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css +++ b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css @@ -20,6 +20,15 @@ color: #555; font-size: 11px; padding: 3px 5px; + margin: 0 2px; +} + +.monaco-keybinding > .monaco-keybinding-key:first-child { + margin-left: 0; +} + +.monaco-keybinding > .monaco-keybinding-key:last-child { + margin-right: 0; } .hc-black .monaco-keybinding > .monaco-keybinding-key, @@ -36,5 +45,5 @@ } .monaco-keybinding > .monaco-keybinding-key-chord-separator { - width: 2px; + width: 6px; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts index ecc926434d6..e9fdc86be15 100644 --- a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts +++ b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./keybindingLabel'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { equals } from 'vs/base/common/objects'; import { OperatingSystem } from 'vs/base/common/platform'; import { ResolvedKeybinding, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import * as dom from 'vs/base/browser/dom'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -26,14 +26,18 @@ export interface Matches { chordPart: PartMatches; } -export class KeybindingLabel implements IDisposable { +export interface KeybindingLabelOptions { + renderUnboundKeybindings: boolean; +} + +export class KeybindingLabel { private domNode: HTMLElement; - private keybinding: ResolvedKeybinding; - private matches: Matches; + private keybinding: ResolvedKeybinding | undefined; + private matches: Matches | undefined; private didEverRender: boolean; - constructor(container: HTMLElement, private os: OperatingSystem) { + constructor(container: HTMLElement, private os: OperatingSystem, private options?: KeybindingLabelOptions) { this.domNode = dom.append(container, $('.monaco-keybinding')); this.didEverRender = false; container.appendChild(this.domNode); @@ -43,7 +47,7 @@ export class KeybindingLabel implements IDisposable { return this.domNode; } - set(keybinding: ResolvedKeybinding, matches: Matches) { + set(keybinding: ResolvedKeybinding | undefined, matches?: Matches) { if (this.didEverRender && this.keybinding === keybinding && KeybindingLabel.areSame(this.matches, matches)) { return; } @@ -66,6 +70,8 @@ export class KeybindingLabel implements IDisposable { this.renderPart(this.domNode, chordPart, this.matches ? this.matches.chordPart : null); } this.domNode.title = this.keybinding.getAriaLabel() || ''; + } else if (this.options && this.options.renderUnboundKeybindings) { + this.renderUnbound(this.domNode); } this.didEverRender = true; @@ -98,10 +104,11 @@ export class KeybindingLabel implements IDisposable { } } - dispose() { + private renderUnbound(parent: HTMLElement): void { + dom.append(parent, $('span.monaco-keybinding-key', undefined, localize('unbound', "Unbound"))); } - private static areSame(a: Matches, b: Matches): boolean { + private static areSame(a: Matches | undefined, b: Matches | undefined): boolean { if (a === b || (!a && !b)) { return true; } diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 940a595a2d7..73756ce7811 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -8,6 +8,9 @@ height: 100%; width: 100%; white-space: nowrap; +} + +.monaco-list.mouse-support { -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: -moz-none; @@ -26,15 +29,23 @@ height: 100%; } +.monaco-list.horizontal-scrolling .monaco-list-rows { + width: auto; + min-width: 100%; +} + .monaco-list-row { position: absolute; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; - cursor: pointer; overflow: hidden; width: 100%; +} + +.monaco-list.mouse-support .monaco-list-row { + cursor: pointer; touch-action: none; } @@ -46,4 +57,138 @@ /* Focus */ .monaco-list.element-focused, .monaco-list.selection-single, .monaco-list.selection-multiple { outline: 0 !important; +} + +/* Dnd */ +.monaco-drag-image { + display: inline-block; + padding: 1px 7px; + border-radius: 10px; + font-size: 12px; + position: absolute; +} + +/* Type filter */ + +.monaco-list-type-filter { + display: flex; + align-items: center; + position: absolute; + border-radius: 2px; + padding: 0px 3px; + max-width: calc(100% - 10px); + text-overflow: ellipsis; + overflow: hidden; + text-align: right; + box-sizing: border-box; + cursor: all-scroll; + font-size: 13px; + line-height: 18px; + height: 20px; + z-index: 1; + top: 4px; +} + +.monaco-list-type-filter.dragging { + transition: top 0.2s, left 0.2s; +} + +.monaco-list-type-filter.ne { + right: 4px; +} + +.monaco-list-type-filter.nw { + left: 4px; +} + +.monaco-list-type-filter > .controls { + display: flex; + align-items: center; + box-sizing: border-box; + transition: width 0.2s; + width: 0; +} + +.monaco-list-type-filter.dragging > .controls, +.monaco-list-type-filter:hover > .controls { + width: 36px; +} + +.monaco-list-type-filter > .controls > * { + box-sizing: border-box; + width: 16px; + height: 16px; + margin: 0 0 0 2px; + flex-shrink: 0; +} + +.monaco-list-type-filter > .controls > .filter { + -webkit-appearance: none; + width: 16px; + height: 16px; + background: url("media/no-filter.svg"); + background-position: 50% 50%; + cursor: pointer; +} + +.monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter.svg"); +} + +.vs-dark .monaco-list-type-filter > .controls > .filter { + background-image: url("media/no-filter-dark.svg"); +} + +.vs-dark .monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter-dark.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .filter { + background-image: url("media/no-filter-hc.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter-hc.svg"); +} + +.monaco-list-type-filter > .controls > .clear { + border: none; + background: url("media/close.svg"); + cursor: pointer; +} + +.vs-dark .monaco-list-type-filter > .controls > .clear { + background-image: url("media/close-dark.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .clear { + background-image: url("media/close-hc.svg"); +} + +.monaco-list-type-filter-message { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + top: 0; + left: 0; + padding: 40px 1em 1em 1em; + text-align: center; + white-space: normal; + opacity: 0.7; + pointer-events: none; +} + +.monaco-list-type-filter-message:empty { + display: none; +} + +/* Electron */ + +.monaco-list-type-filter { + cursor: -webkit-grab; +} + +.monaco-list-type-filter.dragging { + cursor: -webkit-grabbing; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 1af9112d491..d18e381d3af 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -4,18 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import { GestureEvent } from 'vs/base/browser/touch'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface IListVirtualDelegate { getHeight(element: T): number; getTemplateId(element: T): string; hasDynamicHeight?(element: T): boolean; + setDynamicHeight?(element: T, height: number): void; } export interface IListRenderer { templateId: string; renderTemplate(container: HTMLElement): TTemplateData; - renderElement(element: T, index: number, templateData: TTemplateData): void; - disposeElement(element: T, index: number, templateData: TTemplateData): void; + renderElement(element: T, index: number, templateData: TTemplateData, dynamicHeightProbing?: boolean): void; + disposeElement?(element: T, index: number, templateData: TTemplateData, dynamicHeightProbing?: boolean): void; disposeTemplate(templateData: TTemplateData): void; } @@ -28,24 +31,77 @@ export interface IListEvent { export interface IListMouseEvent { browserEvent: MouseEvent; element: T | undefined; - index: number; + index: number | undefined; } export interface IListTouchEvent { browserEvent: TouchEvent; element: T | undefined; - index: number; + index: number | undefined; } export interface IListGestureEvent { browserEvent: GestureEvent; element: T | undefined; - index: number; + index: number | undefined; +} + +export interface IListDragEvent { + browserEvent: DragEvent; + element: T | undefined; + index: number | undefined; } export interface IListContextMenuEvent { browserEvent: UIEvent; element: T | undefined; - index: number; - anchor: HTMLElement | { x: number; y: number; } | undefined; + index: number | undefined; + anchor: HTMLElement | { x: number; y: number; }; +} + +export interface IIdentityProvider { + getId(element: T): { toString(): string; }; +} + +export enum ListAriaRootRole { + /** default tree structure role */ + TREE = 'tree', + + /** role='tree' can interfere with screenreaders reading nested elements inside the tree row. Use FORM in that case. */ + FORM = 'form' +} + +export interface IKeyboardNavigationLabelProvider { + + /** + * Return a keyboard navigation label which will be used by the + * list for filtering/navigating. Return `undefined` to make an + * element always match. + */ + getKeyboardNavigationLabel(element: T): { toString(): string | undefined; } | undefined; + mightProducePrintableCharacter?(event: IKeyboardEvent): boolean; +} + +export const enum ListDragOverEffect { + Copy, + Move +} + +export interface IListDragOverReaction { + accept: boolean; + effect?: ListDragOverEffect; + feedback?: number[]; // use -1 for entire list +} + +export const ListDragOverReactions = { + reject(): IListDragOverReaction { return { accept: false }; }, + accept(): IListDragOverReaction { return { accept: true }; }, +}; + +export interface IListDragAndDrop { + getDragURI(element: T): string | null; + getDragLabel?(elements: T[]): string | undefined; + onDragStart?(data: IDragAndDropData, originalEvent: DragEvent): void; + onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction; + drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index d4491cecefd..3d1fe415426 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -9,7 +9,7 @@ import { range } from 'vs/base/common/arrays'; import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent } from './list'; import { List, IListStyles, IListOptions } from './listWidget'; import { IPagedModel } from 'vs/base/common/paging'; -import { Event, mapEvent } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; export interface IPagedRenderer extends IListRenderer { @@ -35,7 +35,7 @@ class PagedRenderer implements IListRenderer { } } }; } - renderElement(index: number, _: number, data: ITemplateData): void { + renderElement(index: number, _: number, data: ITemplateData, dynamicHeightProbing?: boolean): void { if (data.disposable) { data.disposable.dispose(); } @@ -47,7 +47,7 @@ class PagedRenderer implements IListRenderer implements IListRenderer cts.cancel() }; this.renderer.renderPlaceholder(index, data.data); - promise.then(entry => this.renderer.renderElement(entry, index, data.data!)); - } - - disposeElement(): void { - // noop + promise.then(entry => this.renderer.renderElement(entry, index, data.data!, dynamicHeightProbing)); } disposeTemplate(data: ITemplateData): void { @@ -118,23 +114,23 @@ export class PagedList implements IDisposable { } get onFocusChange(): Event> { - return mapEvent(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + return Event.map(this.list.onFocusChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); } get onOpen(): Event> { - return mapEvent(this.list.onOpen, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); + return Event.map(this.list.onDidOpen, ({ elements, indexes, browserEvent }) => ({ elements: elements.map(e => this._model.get(e)), indexes, browserEvent })); } get onSelectionChange(): Event> { - return mapEvent(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + return Event.map(this.list.onSelectionChange, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); } get onPin(): Event> { - return mapEvent(this.list.onPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); + return Event.map(this.list.onPin, ({ elements, indexes }) => ({ elements: elements.map(e => this._model.get(e)), indexes })); } get onContextMenu(): Event> { - return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => ({ element: this._model.get(element!), index, anchor, browserEvent })); + return Event.map(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => (typeof element === 'undefined' ? { element, index, anchor, browserEvent } : { element: this._model.get(element), index, anchor, browserEvent })); } get model(): IPagedModel { @@ -194,8 +190,12 @@ export class PagedList implements IDisposable { return this.list.getSelection(); } - layout(height?: number): void { - this.list.layout(height); + layout(height?: number, width?: number): void { + this.list.layout(height, width); + } + + toggleKeyboardNavigation(): void { + this.list.toggleKeyboardNavigation(); } reveal(index: number, relativeTop?: number): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 0a96472dba2..a99b55931c9 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -3,35 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getOrDefault2 } from 'vs/base/common/objects'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { getOrDefault } from 'vs/base/common/objects'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import * as DOM from 'vs/base/browser/dom'; -import { Event, mapEvent, filterEvent } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { ScrollEvent, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; import { ISpliceable } from 'vs/base/common/sequence'; import { memoize } from 'vs/base/common/decorators'; -import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { Range, IRange } from 'vs/base/common/range'; - -function canUseTranslate3d(): boolean { - if (browser.isFirefox) { - return false; - } - - if (browser.getZoomLevel() !== 0) { - return false; - } - - return true; -} +import { equals, distinct } from 'vs/base/common/arrays'; +import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; +import { disposableTimeout, Delayer } from 'vs/base/common/async'; interface IItem { readonly id: string; @@ -39,55 +29,180 @@ interface IItem { readonly templateId: string; row: IRow | null; size: number; + width: number | undefined; hasDynamicHeight: boolean; - renderWidth: number | undefined; + lastDynamicHeightWidth: number | undefined; + uri: string | undefined; + dropTarget: boolean; + dragStartDisposable: IDisposable; } -export interface IListViewOptions { +export interface IListViewDragAndDrop extends IListDragAndDrop { + getDragElements(element: T): T[]; +} + +export interface IAriaSetProvider { + getSetSize(element: T, index: number, listLength: number): number; + getPosInSet(element: T, index: number): number; +} + +export interface IListViewOptions { + readonly dnd?: IListViewDragAndDrop; readonly useShadows?: boolean; readonly verticalScrollMode?: ScrollbarVisibility; readonly setRowLineHeight?: boolean; readonly supportDynamicHeights?: boolean; + readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; + readonly ariaSetProvider?: IAriaSetProvider; } const DefaultOptions = { useShadows: true, verticalScrollMode: ScrollbarVisibility.Auto, setRowLineHeight: true, - supportDynamicHeights: false + supportDynamicHeights: false, + dnd: { + getDragElements(e: T) { return [e]; }, + getDragURI() { return null; }, + onDragStart(): void { }, + onDragOver() { return false; }, + drop() { } + }, + horizontalScrolling: false }; +export class ElementsDragAndDropData implements IDragAndDropData { + + readonly elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + update(): void { } + + getData(): any { + return this.elements; + } +} + +export class ExternalElementsDragAndDropData implements IDragAndDropData { + + readonly elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + update(): void { } + + getData(): any { + return this.elements; + } +} + +export class DesktopDragAndDropData implements IDragAndDropData { + + readonly types: any[]; + readonly files: any[]; + + constructor() { + this.types = []; + this.files = []; + } + + update(dataTransfer: DataTransfer): void { + if (dataTransfer.types) { + this.types.splice(0, this.types.length, ...dataTransfer.types); + } + + if (dataTransfer.files) { + this.files.splice(0, this.files.length); + + for (let i = 0; i < dataTransfer.files.length; i++) { + const file = dataTransfer.files.item(i); + + if (file && (file.size || file.type)) { + this.files.push(file); + } + } + } + } + + getData(): any { + return { + types: this.types, + files: this.files + }; + } +} + +function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined): boolean { + if (Array.isArray(f1) && Array.isArray(f2)) { + return equals(f1, f2!); + } + + return f1 === f2; +} + export class ListView implements ISpliceable, IDisposable { + private static InstanceCount = 0; + readonly domId = `list_id_${++ListView.InstanceCount}`; + readonly domNode: HTMLElement; private items: IItem[]; private itemId: number; private rangeMap: RangeMap; private cache: RowCache; - private renderers = new Map>(); + private renderers = new Map>(); private lastRenderTop: number; private lastRenderHeight: number; private renderWidth = 0; private gesture: Gesture; private rowsContainer: HTMLElement; private scrollableElement: ScrollableElement; - private scrollHeight: number; - private didRequestScrollableElementUpdate: boolean = false; + private _scrollHeight: number; + private scrollableElementUpdateDisposable: IDisposable | null = null; + private scrollableElementWidthDelayer = new Delayer(50); private splicing = false; - private dragAndDropScrollInterval: number; - private dragAndDropScrollTimeout: number; - private dragAndDropMouseY: number; + private dragOverAnimationDisposable: IDisposable | undefined; + private dragOverAnimationStopDisposable: IDisposable = Disposable.None; + private dragOverMouseY: number; private setRowLineHeight: boolean; private supportDynamicHeights: boolean; + private horizontalScrolling: boolean; + private ariaSetProvider: IAriaSetProvider; + private scrollWidth: number | undefined; + private canUseTranslate3d: boolean | undefined = undefined; + + private dnd: IListViewDragAndDrop; + private canDrop: boolean = false; + private currentDragData: IDragAndDropData | undefined; + private currentDragFeedback: number[] | undefined; + private currentDragFeedbackDisposable: IDisposable = Disposable.None; + private onDragLeaveTimeout: IDisposable = Disposable.None; + private disposables: IDisposable[]; + private _onDidChangeContentHeight = new Emitter(); + readonly onDidChangeContentHeight: Event = Event.latch(this._onDidChangeContentHeight.event); + get contentHeight(): number { return this.rangeMap.size; } + + get onDidScroll(): Event { return this.scrollableElement.onScroll; } + constructor( container: HTMLElement, private virtualDelegate: IListVirtualDelegate, - renderers: IListRenderer[], - options: IListViewOptions = DefaultOptions + renderers: IListRenderer[], + options: IListViewOptions = DefaultOptions as IListViewOptions ) { + if (options.horizontalScrolling && options.supportDynamicHeights) { + throw new Error('Horizontal scrolling and dynamic heights not supported simultaneously'); + } + this.items = []; this.itemId = 0; this.rangeMap = new RangeMap(); @@ -104,15 +219,25 @@ export class ListView implements ISpliceable, IDisposable { this.domNode = document.createElement('div'); this.domNode.className = 'monaco-list'; + DOM.addClass(this.domNode, this.domId); + this.domNode.tabIndex = 0; + + DOM.toggleClass(this.domNode, 'mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); + + this.horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); + DOM.toggleClass(this.domNode, 'horizontal-scrolling', this.horizontalScrolling); + + this.ariaSetProvider = options.ariaSetProvider || { getSetSize: (e, i, length) => length, getPosInSet: (_, index) => index + 1 }; + this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; Gesture.addTarget(this.rowsContainer); this.scrollableElement = new ScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: true, - horizontal: ScrollbarVisibility.Hidden, - vertical: getOrDefault2(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault2(options, o => o.useShadows, DefaultOptions.useShadows) + horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, + vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), + useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows) }); this.domNode.appendChild(this.scrollableElement.getDomNode()); @@ -128,11 +253,14 @@ export class ListView implements ISpliceable, IDisposable { domEvent(this.scrollableElement.getDomNode(), 'scroll') (e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables); - const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e)); - onDragOver(this.onDragOver, this, this.disposables); + Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e))(this.onDragOver, this, this.disposables); + Event.map(domEvent(this.domNode, 'drop'), e => this.toDragEvent(e))(this.onDrop, this, this.disposables); + domEvent(this.domNode, 'dragleave')(this.onDragLeave, this, this.disposables); + domEvent(window, 'dragend')(this.onDragEnd, this, this.disposables); - this.setRowLineHeight = getOrDefault2(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); - this.supportDynamicHeights = getOrDefault2(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); + this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); + this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); + this.dnd = getOrDefault, IListViewDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); this.layout(); } @@ -148,6 +276,7 @@ export class ListView implements ISpliceable, IDisposable { return this._splice(start, deleteCount, elements); } finally { this.splicing = false; + this._onDidChangeContentHeight.fire(this.contentHeight); } } @@ -169,9 +298,13 @@ export class ListView implements ISpliceable, IDisposable { element, templateId: this.virtualDelegate.getTemplateId(element), size: this.virtualDelegate.getHeight(element), + width: undefined, hasDynamicHeight: !!this.virtualDelegate.hasDynamicHeight && this.virtualDelegate.hasDynamicHeight(element), - renderWidth: undefined, - row: null + lastDynamicHeightWidth: undefined, + row: null, + uri: undefined, + dropTarget: false, + dragStartDisposable: Disposable.None })); let deleted: IItem[]; @@ -215,29 +348,83 @@ export class ListView implements ISpliceable, IDisposable { } } - this.updateScrollHeight(); + this.eventuallyUpdateScrollDimensions(); if (this.supportDynamicHeights) { - this.rerender(this.scrollTop, this.renderHeight); + this._rerender(this.scrollTop, this.renderHeight); } return deleted.map(i => i.element); } - private updateScrollHeight(): void { - this.scrollHeight = this.getContentHeight(); - this.rowsContainer.style.height = `${this.scrollHeight}px`; + private eventuallyUpdateScrollDimensions(): void { + this._scrollHeight = this.contentHeight; + this.rowsContainer.style.height = `${this._scrollHeight}px`; - if (!this.didRequestScrollableElementUpdate) { - DOM.scheduleAtNextAnimationFrame(() => { + if (!this.scrollableElementUpdateDisposable) { + this.scrollableElementUpdateDisposable = DOM.scheduleAtNextAnimationFrame(() => { this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); - this.didRequestScrollableElementUpdate = false; + this.updateScrollWidth(); + this.scrollableElementUpdateDisposable = null; }); - - this.didRequestScrollableElementUpdate = true; } } + private eventuallyUpdateScrollWidth(): void { + if (!this.horizontalScrolling) { + return; + } + + this.scrollableElementWidthDelayer.trigger(() => this.updateScrollWidth()); + } + + private updateScrollWidth(): void { + if (!this.horizontalScrolling) { + return; + } + + if (this.items.length === 0) { + this.scrollableElement.setScrollDimensions({ scrollWidth: 0 }); + } + + let scrollWidth = 0; + + for (const item of this.items) { + if (typeof item.width !== 'undefined') { + scrollWidth = Math.max(scrollWidth, item.width); + } + } + + this.scrollWidth = scrollWidth; + this.scrollableElement.setScrollDimensions({ scrollWidth: scrollWidth + 10 }); + } + + updateWidth(index: number): void { + if (!this.horizontalScrolling || typeof this.scrollWidth === 'undefined') { + return; + } + + const item = this.items[index]; + this.measureItemWidth(item); + + if (typeof item.width !== 'undefined' && item.width > this.scrollWidth) { + this.scrollWidth = item.width; + this.scrollableElement.setScrollDimensions({ scrollWidth: this.scrollWidth + 10 }); + } + } + + rerender(): void { + if (!this.supportDynamicHeights) { + return; + } + + for (const item of this.items) { + item.lastDynamicHeightWidth = undefined; + } + + this._rerender(this.lastRenderTop, this.lastRenderHeight); + } + get length(): number { return this.items.length; } @@ -247,6 +434,25 @@ export class ListView implements ISpliceable, IDisposable { return scrollDimensions.height; } + get firstVisibleIndex(): number { + const range = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + const firstElTop = this.rangeMap.positionAt(range.start); + const nextElTop = this.rangeMap.positionAt(range.start + 1); + if (nextElTop !== -1) { + const firstElMidpoint = (nextElTop - firstElTop) / 2 + firstElTop; + if (firstElMidpoint < this.scrollTop) { + return range.start + 1; + } + } + + return range.start; + } + + get lastVisibleIndex(): number { + const range = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + return range.end - 1; + } + element(index: number): T { return this.items[index].element; } @@ -272,23 +478,37 @@ export class ListView implements ISpliceable, IDisposable { return this.rangeMap.indexAfter(position); } - layout(height?: number): void { - this.scrollableElement.setScrollDimensions({ - height: height || DOM.getContentHeight(this.domNode) - }); - } + layout(height?: number, width?: number): void { + let scrollDimensions: INewScrollDimensions = { + height: typeof height === 'number' ? height : DOM.getContentHeight(this.domNode) + }; - layoutWidth(width: number): void { - this.renderWidth = width; + if (this.scrollableElementUpdateDisposable) { + this.scrollableElementUpdateDisposable.dispose(); + this.scrollableElementUpdateDisposable = null; + scrollDimensions.scrollHeight = this.scrollHeight; + } - if (this.supportDynamicHeights) { - this.rerender(this.scrollTop, this.renderHeight); + this.scrollableElement.setScrollDimensions(scrollDimensions); + + if (typeof width !== 'undefined') { + this.renderWidth = width; + + if (this.supportDynamicHeights) { + this._rerender(this.scrollTop, this.renderHeight); + } + + if (this.horizontalScrolling) { + this.scrollableElement.setScrollDimensions({ + width: typeof width === 'number' ? width : DOM.getContentWidth(this.domNode) + }); + } } } // Render - private render(renderTop: number, renderHeight: number): void { + private render(renderTop: number, renderHeight: number, renderLeft: number, scrollWidth: number): void { const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); const renderRange = this.getRenderRange(renderTop, renderHeight); @@ -308,14 +528,33 @@ export class ListView implements ISpliceable, IDisposable { } } - if (canUseTranslate3d() && !isWindows /* Windows: translate3d breaks subpixel-antialias (ClearType) unless a background is defined */) { - const transform = `translate3d(0px, -${renderTop}px, 0px)`; + const canUseTranslate3d = !isWindows && !browser.isFirefox && browser.getZoomLevel() === 0; + + if (canUseTranslate3d) { + const transform = `translate3d(-${renderLeft}px, -${renderTop}px, 0px)`; this.rowsContainer.style.transform = transform; this.rowsContainer.style.webkitTransform = transform; + + if (canUseTranslate3d !== this.canUseTranslate3d) { + this.rowsContainer.style.left = '0'; + this.rowsContainer.style.top = '0'; + } } else { + this.rowsContainer.style.left = `-${renderLeft}px`; this.rowsContainer.style.top = `-${renderTop}px`; + + if (canUseTranslate3d !== this.canUseTranslate3d) { + this.rowsContainer.style.transform = ''; + this.rowsContainer.style.webkitTransform = ''; + } } + if (this.horizontalScrolling) { + this.rowsContainer.style.width = `${Math.max(scrollWidth, this.renderWidth)}px`; + } + + this.canUseTranslate3d = canUseTranslate3d; + this.lastRenderTop = renderTop; this.lastRenderHeight = renderHeight; } @@ -327,6 +566,7 @@ export class ListView implements ISpliceable, IDisposable { if (!item.row) { item.row = this.cache.alloc(item.templateId); + item.row!.domNode!.setAttribute('role', 'treeitem'); } if (!item.row.domNode!.parentElement) { @@ -340,7 +580,48 @@ export class ListView implements ISpliceable, IDisposable { this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); - renderer.renderElement(item.element, index, item.row.templateData); + + if (!renderer) { + throw new Error(`No renderer found for template id ${item.templateId}`); + } + + if (renderer) { + renderer.renderElement(item.element, index, item.row.templateData); + } + + const uri = this.dnd.getDragURI(item.element); + item.dragStartDisposable.dispose(); + item.row.domNode!.draggable = !!uri; + + if (uri) { + const onDragStart = domEvent(item.row.domNode!, 'dragstart'); + item.dragStartDisposable = onDragStart(event => this.onDragStart(item.element, uri, event)); + } + + if (this.horizontalScrolling) { + this.measureItemWidth(item); + this.eventuallyUpdateScrollWidth(); + } + } + + private measureItemWidth(item: IItem): void { + if (!item.row || !item.row.domNode) { + return; + } + + item.row.domNode.style.width = 'fit-content'; + item.width = DOM.getContentWidth(item.row.domNode); + const style = window.getComputedStyle(item.row.domNode); + + if (style.paddingLeft) { + item.width += parseFloat(style.paddingLeft); + } + + if (style.paddingRight) { + item.width += parseFloat(style.paddingRight); + } + + item.row.domNode.style.width = ''; } private updateItemInDOM(item: IItem, index: number): void { @@ -353,24 +634,28 @@ export class ListView implements ISpliceable, IDisposable { item.row!.domNode!.setAttribute('data-index', `${index}`); item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); - item.row!.domNode!.setAttribute('aria-setsize', `${this.length}`); - item.row!.domNode!.setAttribute('aria-posinset', `${index + 1}`); + item.row!.domNode!.setAttribute('aria-setsize', String(this.ariaSetProvider.getSetSize(item.element, index, this.length))); + item.row!.domNode!.setAttribute('aria-posinset', String(this.ariaSetProvider.getPosInSet(item.element, index))); + item.row!.domNode!.setAttribute('id', this.getElementDomId(index)); + + DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget); } private removeItemFromDOM(index: number): void { const item = this.items[index]; - const renderer = this.renderers.get(item.templateId); + item.dragStartDisposable.dispose(); - if (renderer.disposeElement) { + const renderer = this.renderers.get(item.templateId); + if (renderer && renderer.disposeElement) { renderer.disposeElement(item.element, index, item.row!.templateData); } this.cache.release(item.row!); item.row = null; - } - getContentHeight(): number { - return this.rangeMap.size; + if (this.horizontalScrolling) { + this.eventuallyUpdateScrollWidth(); + } } getScrollTop(): number { @@ -379,6 +664,12 @@ export class ListView implements ISpliceable, IDisposable { } setScrollTop(scrollTop: number): void { + if (this.scrollableElementUpdateDisposable) { + this.scrollableElementUpdateDisposable.dispose(); + this.scrollableElementUpdateDisposable = null; + this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); + } + this.scrollableElement.setScrollPosition({ scrollTop }); } @@ -390,50 +681,61 @@ export class ListView implements ISpliceable, IDisposable { this.setScrollTop(scrollTop); } + get scrollHeight(): number { + return this._scrollHeight + (this.horizontalScrolling ? 10 : 0); + } + // Events - @memoize get onMouseClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseDblClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseMiddleClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.index >= 0 && e.browserEvent.button === 1); } - @memoize get onMouseUp(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseDown(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseOver(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseMove(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseOut(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onContextMenu(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onTouchStart(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)), e => e.index >= 0); } - @memoize get onTap(): Event> { return filterEvent(mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)), e => e.index >= 0); } + @memoize get onMouseClick(): Event> { return Event.map(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)); } + @memoize get onMouseDblClick(): Event> { return Event.map(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)); } + @memoize get onMouseMiddleClick(): Event> { return Event.filter(Event.map(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); } + @memoize get onMouseUp(): Event> { return Event.map(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)); } + @memoize get onMouseDown(): Event> { return Event.map(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)); } + @memoize get onMouseOver(): Event> { return Event.map(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)); } + @memoize get onMouseMove(): Event> { return Event.map(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)); } + @memoize get onMouseOut(): Event> { return Event.map(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)); } + @memoize get onContextMenu(): Event> { return Event.map(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)); } + @memoize get onTouchStart(): Event> { return Event.map(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)); } + @memoize get onTap(): Event> { return Event.map(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)); } private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent { const index = this.getItemIndexFromEventTarget(browserEvent.target || null); - const item = index < 0 ? undefined : this.items[index]; + const item = typeof index === 'undefined' ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toTouchEvent(browserEvent: TouchEvent): IListTouchEvent { const index = this.getItemIndexFromEventTarget(browserEvent.target || null); - const item = index < 0 ? undefined : this.items[index]; + const item = typeof index === 'undefined' ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toGestureEvent(browserEvent: GestureEvent): IListGestureEvent { const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget || null); - const item = index < 0 ? undefined : this.items[index]; + const item = typeof index === 'undefined' ? undefined : this.items[index]; + const element = item && item.element; + return { browserEvent, index, element }; + } + + private toDragEvent(browserEvent: DragEvent): IListDragEvent { + const index = this.getItemIndexFromEventTarget(browserEvent.target || null); + const item = typeof index === 'undefined' ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private onScroll(e: ScrollEvent): void { try { - this.render(e.scrollTop, e.height); + this.render(e.scrollTop, e.height, e.scrollLeft, e.scrollWidth); if (this.supportDynamicHeights) { - this.rerender(e.scrollTop, e.height); + this._rerender(e.scrollTop, e.height); } } catch (err) { - console.log('Got bad scroll event:', e); + console.error('Got bad scroll event:', e); throw err; } } @@ -445,61 +747,226 @@ export class ListView implements ISpliceable, IDisposable { this.scrollTop -= event.translationY; } - private onDragOver(event: DragMouseEvent): void { - this.setupDragAndDropScrollInterval(); - this.dragAndDropMouseY = event.posy; - } + // DND - private setupDragAndDropScrollInterval(): void { - const viewTop = DOM.getTopLeftOffset(this.domNode).top; + private onDragStart(element: T, uri: string, event: DragEvent): void { + if (!event.dataTransfer) { + return; + } - if (!this.dragAndDropScrollInterval) { - this.dragAndDropScrollInterval = window.setInterval(() => { - if (this.dragAndDropMouseY === undefined) { - return; - } + const elements = this.dnd.getDragElements(element); - var diff = this.dragAndDropMouseY - viewTop; - var scrollDiff = 0; - var upperLimit = this.renderHeight - 35; + event.dataTransfer.effectAllowed = 'copyMove'; + event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri])); - if (diff < 35) { - scrollDiff = Math.max(-14, 0.2 * (diff - 35)); - } else if (diff > upperLimit) { - scrollDiff = Math.min(14, 0.2 * (diff - upperLimit)); - } + if (event.dataTransfer.setDragImage) { + let label: string | undefined; - this.scrollTop += scrollDiff; - }, 10); + if (this.dnd.getDragLabel) { + label = this.dnd.getDragLabel(elements); + } - this.cancelDragAndDropScrollTimeout(); + if (typeof label === 'undefined') { + label = String(elements.length); + } - this.dragAndDropScrollTimeout = window.setTimeout(() => { - this.cancelDragAndDropScrollInterval(); - this.dragAndDropScrollTimeout = -1; - }, 1000); + const dragImage = DOM.$('.monaco-drag-image'); + dragImage.textContent = label; + document.body.appendChild(dragImage); + event.dataTransfer.setDragImage(dragImage, -10, -10); + setTimeout(() => document.body.removeChild(dragImage), 0); + } + + this.currentDragData = new ElementsDragAndDropData(elements); + StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); + + if (this.dnd.onDragStart) { + this.dnd.onDragStart(this.currentDragData, event); } } - private cancelDragAndDropScrollInterval(): void { - if (this.dragAndDropScrollInterval) { - window.clearInterval(this.dragAndDropScrollInterval); - this.dragAndDropScrollInterval = -1; + private onDragOver(event: IListDragEvent): boolean { + event.browserEvent.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + + this.onDragLeaveTimeout.dispose(); + + if (StaticDND.CurrentDragAndDropData && StaticDND.CurrentDragAndDropData.getData() === 'vscode-ui') { + return false; } - this.cancelDragAndDropScrollTimeout(); + this.setupDragAndDropScrollTopAnimation(event.browserEvent); + + if (!event.browserEvent.dataTransfer) { + return false; + } + + // Drag over from outside + if (!this.currentDragData) { + if (StaticDND.CurrentDragAndDropData) { + // Drag over from another list + this.currentDragData = StaticDND.CurrentDragAndDropData; + + } else { + // Drag over from the desktop + if (!event.browserEvent.dataTransfer.types) { + return false; + } + + this.currentDragData = new DesktopDragAndDropData(); + } + } + + const result = this.dnd.onDragOver(this.currentDragData, event.element, event.index, event.browserEvent); + this.canDrop = typeof result === 'boolean' ? result : result.accept; + + if (!this.canDrop) { + this.currentDragFeedback = undefined; + this.currentDragFeedbackDisposable.dispose(); + return false; + } + + event.browserEvent.dataTransfer.dropEffect = (typeof result !== 'boolean' && result.effect === ListDragOverEffect.Copy) ? 'copy' : 'move'; + + let feedback: number[]; + + if (typeof result !== 'boolean' && result.feedback) { + feedback = result.feedback; + } else { + if (typeof event.index === 'undefined') { + feedback = [-1]; + } else { + feedback = [event.index]; + } + } + + // sanitize feedback list + feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort(); + feedback = feedback[0] === -1 ? [-1] : feedback; + + if (feedback.length === 0) { + throw new Error('Invalid empty feedback list'); + } + + if (equalsDragFeedback(this.currentDragFeedback, feedback)) { + return true; + } + + this.currentDragFeedback = feedback; + this.currentDragFeedbackDisposable.dispose(); + + if (feedback[0] === -1) { // entire list feedback + DOM.addClass(this.domNode, 'drop-target'); + this.currentDragFeedbackDisposable = toDisposable(() => DOM.removeClass(this.domNode, 'drop-target')); + } else { + for (const index of feedback) { + const item = this.items[index]!; + item.dropTarget = true; + + if (item.row && item.row.domNode) { + DOM.addClass(item.row.domNode, 'drop-target'); + } + } + + this.currentDragFeedbackDisposable = toDisposable(() => { + for (const index of feedback) { + const item = this.items[index]!; + item.dropTarget = false; + + if (item.row && item.row.domNode) { + DOM.removeClass(item.row.domNode, 'drop-target'); + } + } + }); + } + + return true; } - private cancelDragAndDropScrollTimeout(): void { - if (this.dragAndDropScrollTimeout) { - window.clearTimeout(this.dragAndDropScrollTimeout); - this.dragAndDropScrollTimeout = -1; + private onDragLeave(): void { + this.onDragLeaveTimeout.dispose(); + this.onDragLeaveTimeout = disposableTimeout(() => this.clearDragOverFeedback(), 100); + } + + private onDrop(event: IListDragEvent): void { + if (!this.canDrop) { + return; + } + + const dragData = this.currentDragData; + this.teardownDragAndDropScrollTopAnimation(); + this.clearDragOverFeedback(); + this.currentDragData = undefined; + StaticDND.CurrentDragAndDropData = undefined; + + if (!dragData || !event.browserEvent.dataTransfer) { + return; + } + + event.browserEvent.preventDefault(); + dragData.update(event.browserEvent.dataTransfer); + this.dnd.drop(dragData, event.element, event.index, event.browserEvent); + } + + private onDragEnd(): void { + this.canDrop = false; + this.teardownDragAndDropScrollTopAnimation(); + this.clearDragOverFeedback(); + this.currentDragData = undefined; + StaticDND.CurrentDragAndDropData = undefined; + } + + private clearDragOverFeedback(): void { + this.currentDragFeedback = undefined; + this.currentDragFeedbackDisposable.dispose(); + this.currentDragFeedbackDisposable = Disposable.None; + } + + // DND scroll top animation + + private setupDragAndDropScrollTopAnimation(event: DragEvent): void { + if (!this.dragOverAnimationDisposable) { + const viewTop = DOM.getTopLeftOffset(this.domNode).top; + this.dragOverAnimationDisposable = DOM.animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); + } + + this.dragOverAnimationStopDisposable.dispose(); + this.dragOverAnimationStopDisposable = disposableTimeout(() => { + if (this.dragOverAnimationDisposable) { + this.dragOverAnimationDisposable.dispose(); + this.dragOverAnimationDisposable = undefined; + } + }, 1000); + + this.dragOverMouseY = event.pageY; + } + + private animateDragAndDropScrollTop(viewTop: number): void { + if (this.dragOverMouseY === undefined) { + return; + } + + const diff = this.dragOverMouseY - viewTop; + const upperLimit = this.renderHeight - 35; + + if (diff < 35) { + this.scrollTop += Math.max(-14, Math.floor(0.3 * (diff - 35))); + } else if (diff > upperLimit) { + this.scrollTop += Math.min(14, Math.floor(0.3 * (diff - upperLimit))); + } + } + + private teardownDragAndDropScrollTopAnimation(): void { + this.dragOverAnimationStopDisposable.dispose(); + + if (this.dragOverAnimationDisposable) { + this.dragOverAnimationDisposable.dispose(); + this.dragOverAnimationDisposable = undefined; } } // Util - private getItemIndexFromEventTarget(target: EventTarget | null): number { + private getItemIndexFromEventTarget(target: EventTarget | null): number | undefined { let element: HTMLElement | null = target as (HTMLElement | null); while (element instanceof HTMLElement && element !== this.rowsContainer) { @@ -516,7 +983,7 @@ export class ListView implements ISpliceable, IDisposable { element = element.parentElement; } - return -1; + return undefined; } private getRenderRange(renderTop: number, renderHeight: number): IRange { @@ -530,17 +997,20 @@ export class ListView implements ISpliceable, IDisposable { * Given a stable rendered state, checks every rendered element whether it needs * to be probed for dynamic height. Adjusts scroll height and top if necessary. */ - private rerender(renderTop: number, renderHeight: number): void { + private _rerender(renderTop: number, renderHeight: number): void { const previousRenderRange = this.getRenderRange(renderTop, renderHeight); // Let's remember the second element's position, this helps in scrolling up // and preserving a linear upwards scroll movement - let secondElementIndex: number | undefined; - let secondElementTopDelta: number | undefined; + let anchorElementIndex: number | undefined; + let anchorElementTopDelta: number | undefined; - if (previousRenderRange.end - previousRenderRange.start > 1) { - secondElementIndex = previousRenderRange.start + 1; - secondElementTopDelta = this.elementTop(secondElementIndex) - renderTop; + if (renderTop === this.elementTop(previousRenderRange.start)) { + anchorElementIndex = previousRenderRange.start; + anchorElementTopDelta = 0; + } else if (previousRenderRange.end - previousRenderRange.start > 1) { + anchorElementIndex = previousRenderRange.start + 1; + anchorElementTopDelta = this.elementTop(anchorElementIndex) - renderTop; } let heightDiff = 0; @@ -563,7 +1033,7 @@ export class ListView implements ISpliceable, IDisposable { if (!didChange) { if (heightDiff !== 0) { - this.updateScrollHeight(); + this.eventuallyUpdateScrollDimensions(); } const unrenderRanges = Range.relativeComplement(previousRenderRange, renderRange); @@ -576,38 +1046,62 @@ export class ListView implements ISpliceable, IDisposable { } } + const renderRanges = Range.relativeComplement(renderRange, previousRenderRange); + + for (const range of renderRanges) { + for (let i = range.start; i < range.end; i++) { + const afterIndex = i + 1; + const beforeRow = afterIndex < this.items.length ? this.items[afterIndex].row : null; + const beforeElement = beforeRow ? beforeRow.domNode : null; + this.insertItemInDOM(i, beforeElement); + } + } + for (let i = renderRange.start; i < renderRange.end; i++) { if (this.items[i].row) { this.updateItemInDOM(this.items[i], i); } } - if (typeof secondElementIndex === 'number') { - this.scrollTop = this.elementTop(secondElementIndex) - secondElementTopDelta!; + if (typeof anchorElementIndex === 'number') { + this.scrollTop = this.elementTop(anchorElementIndex) - anchorElementTopDelta!; } + this._onDidChangeContentHeight.fire(this.contentHeight); return; } } - } private probeDynamicHeight(index: number): number { const item = this.items[index]; - if (!item.hasDynamicHeight || item.renderWidth === this.renderWidth) { + if (!item.hasDynamicHeight || item.lastDynamicHeightWidth === this.renderWidth) { return 0; } const size = item.size; - const renderer = this.renderers.get(item.templateId); const row = this.cache.alloc(item.templateId); row.domNode!.style.height = ''; this.rowsContainer.appendChild(row.domNode!); - renderer.renderElement(item.element, index, row.templateData); + + const renderer = this.renderers.get(item.templateId); + if (renderer) { + renderer.renderElement(item.element, index, row.templateData, true); + + if (renderer.disposeElement) { + renderer.disposeElement(item.element, index, row.templateData, true); + } + } + item.size = row.domNode!.offsetHeight; - item.renderWidth = this.renderWidth; + + if (this.virtualDelegate.setDynamicHeight) { + this.virtualDelegate.setDynamicHeight(item.element, item.size); + } + + item.lastDynamicHeightWidth = this.renderWidth; this.rowsContainer.removeChild(row.domNode!); this.cache.release(row); @@ -634,6 +1128,10 @@ export class ListView implements ISpliceable, IDisposable { return nextToLastItem.row.domNode; } + getElementDomId(index: number): string { + return `${this.domId}_${index}`; + } + // Dispose dispose() { @@ -641,7 +1139,9 @@ export class ListView implements ISpliceable, IDisposable { for (const item of this.items) { if (item.row) { const renderer = this.renderers.get(item.row.templateId); - renderer.disposeTemplate(item.row.templateData); + if (renderer) { + renderer.disposeTemplate(item.row.templateData); + } } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 9a0457d48a6..5babdbf0d72 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -7,27 +7,25 @@ import 'vs/css!./list'; import { localize } from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { isNumber } from 'vs/base/common/types'; -import { range, firstIndex } from 'vs/base/common/arrays'; +import { range, firstIndex, binarySearch } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; import * as DOM from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { Gesture } from 'vs/base/browser/touch'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { Event, Emitter, EventBufferer, chain, mapEvent, anyEvent } from 'vs/base/common/event'; +import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list'; -import { ListView, IListViewOptions } from './listView'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListAriaRootRole } from './list'; +import { ListView, IListViewOptions, IListViewDragAndDrop, IAriaSetProvider } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; - -export interface IIdentityProvider { - (element: T): R; -} +import { matchesPrefix } from 'vs/base/common/filters'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; interface ITraitChangeEvent { indexes: number[]; @@ -70,15 +68,10 @@ class TraitRenderer implements IListRenderer this.trait.renderIndex(index, templateData); } - disposeElement(): void { - // noop - } - splice(start: number, deleteCount: number, insertCount: number): void { const rendered: IRenderedContainer[] = []; - for (let i = 0; i < this.renderedElements.length; i++) { - const renderedElement = this.renderedElements[i]; + for (const renderedElement of this.renderedElements) { if (renderedElement.index < start) { rendered.push(renderedElement); @@ -114,10 +107,8 @@ class TraitRenderer implements IListRenderer class Trait implements ISpliceable, IDisposable { - /** - * Sorted indexes which have this trait. - */ - private indexes: number[]; + private indexes: number[] = []; + private sortedIndexes: number[] = []; private _onChange = new Emitter(); get onChange(): Event { return this._onChange.event; } @@ -129,21 +120,19 @@ class Trait implements ISpliceable, IDisposable { return new TraitRenderer(this); } - constructor(private _trait: string) { - this.indexes = []; - } + constructor(private _trait: string) { } splice(start: number, deleteCount: number, elements: boolean[]): void { const diff = elements.length - deleteCount; const end = start + deleteCount; const indexes = [ - ...this.indexes.filter(i => i < start), + ...this.sortedIndexes.filter(i => i < start), ...elements.map((hasTrait, i) => hasTrait ? i + start : -1).filter(i => i !== -1), - ...this.indexes.filter(i => i >= end).map(i => i + diff) + ...this.sortedIndexes.filter(i => i >= end).map(i => i + diff) ]; this.renderer.splice(start, deleteCount, elements.length); - this.set(indexes); + this._set(indexes, indexes); } renderIndex(index: number, container: HTMLElement): void { @@ -161,10 +150,17 @@ class Trait implements ISpliceable, IDisposable { * @return The old indexes which had this trait. */ set(indexes: number[], browserEvent?: UIEvent): number[] { - const result = this.indexes; - this.indexes = indexes; + return this._set(indexes, [...indexes].sort(numericSort), browserEvent); + } - const toRender = disjunction(result, indexes); + private _set(indexes: number[], sortedIndexes: number[], browserEvent?: UIEvent): number[] { + const result = this.indexes; + const sortedResult = this.sortedIndexes; + + this.indexes = indexes; + this.sortedIndexes = sortedIndexes; + + const toRender = disjunction(sortedResult, indexes); this.renderer.renderIndexes(toRender); this._onChange.fire({ indexes, browserEvent }); @@ -176,7 +172,7 @@ class Trait implements ISpliceable, IDisposable { } contains(index: number): boolean { - return this.indexes.some(i => i === index); + return binarySearch(this.sortedIndexes, index, numericSort) >= 0; } dispose() { @@ -186,16 +182,12 @@ class Trait implements ISpliceable, IDisposable { class FocusTrait extends Trait { - constructor( - private getDomId: IIdentityProvider - ) { + constructor() { super('focused'); } renderIndex(index: number, container: HTMLElement): void { super.renderIndex(index, container); - container.setAttribute('role', 'treeitem'); - container.setAttribute('id', this.getDomId(index)); if (this.contains(index)) { container.setAttribute('aria-selected', 'true'); @@ -215,17 +207,16 @@ class TraitSpliceable implements ISpliceable { constructor( private trait: Trait, private view: ListView, - private getId?: IIdentityProvider + private identityProvider?: IIdentityProvider ) { } splice(start: number, deleteCount: number, elements: T[]): void { - if (!this.getId) { - return this.trait.splice(start, deleteCount, elements.map(e => false)); + if (!this.identityProvider) { + return this.trait.splice(start, deleteCount, elements.map(() => false)); } - const getId = this.getId; - const pastElementsWithTrait = this.trait.get().map(i => getId(this.view.element(i))); - const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(getId(e)) > -1); + const pastElementsWithTrait = this.trait.get().map(i => this.identityProvider!.getId(this.view.element(i)).toString()); + const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.identityProvider!.getId(e).toString()) > -1); this.trait.splice(start, deleteCount, elementsWithTrait); } @@ -250,7 +241,7 @@ class KeyboardController implements IDisposable { this.openController = options.openController || DefaultOpenController; - const onKeyDown = chain(domEvent(view.domNode, 'keydown')) + const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -327,6 +318,122 @@ class KeyboardController implements IDisposable { } } +enum TypeLabelControllerState { + Idle, + Typing +} + +export function mightProducePrintableCharacter(event: IKeyboardEvent): boolean { + if (event.ctrlKey || event.metaKey || event.altKey) { + return false; + } + + return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z) + || (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9) + || (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE); +} + +class TypeLabelController implements IDisposable { + + private enabled = false; + private state: TypeLabelControllerState = TypeLabelControllerState.Idle; + + private automaticKeyboardNavigation = true; + private triggered = false; + + private enabledDisposables: IDisposable[] = []; + private disposables: IDisposable[] = []; + + constructor( + private list: List, + private view: ListView, + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider + ) { + this.updateOptions(list.options); + } + + updateOptions(options: IListOptions): void { + const enableKeyboardNavigation = typeof options.enableKeyboardNavigation === 'undefined' ? true : !!options.enableKeyboardNavigation; + + if (enableKeyboardNavigation) { + this.enable(); + } else { + this.disable(); + } + + if (typeof options.automaticKeyboardNavigation !== 'undefined') { + this.automaticKeyboardNavigation = options.automaticKeyboardNavigation; + } + } + + toggle(): void { + this.triggered = !this.triggered; + } + + private enable(): void { + if (this.enabled) { + return; + } + + const onChar = Event.chain(domEvent(this.view.domNode, 'keydown')) + .filter(e => !isInputElement(e.target as HTMLElement)) + .filter(() => this.automaticKeyboardNavigation || this.triggered) + .map(event => new StandardKeyboardEvent(event)) + .filter(this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e)) + .forEach(e => { e.stopPropagation(); e.preventDefault(); }) + .map(event => event.browserEvent.key) + .event; + + const onClear = Event.debounce(onChar, () => null, 800); + const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i)); + + onInput(this.onInput, this, this.enabledDisposables); + + this.enabled = true; + this.triggered = false; + } + + private disable(): void { + if (!this.enabled) { + return; + } + + this.enabledDisposables = dispose(this.enabledDisposables); + this.enabled = false; + this.triggered = false; + } + + private onInput(word: string | null): void { + if (!word) { + this.state = TypeLabelControllerState.Idle; + this.triggered = false; + return; + } + + const focus = this.list.getFocus(); + const start = focus.length > 0 ? focus[0] : 0; + const delta = this.state === TypeLabelControllerState.Idle ? 1 : 0; + this.state = TypeLabelControllerState.Typing; + + for (let i = 0; i < this.list.length; i++) { + const index = (start + i + delta) % this.list.length; + const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(this.view.element(index)); + const labelStr = label && label.toString(); + + if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { + this.list.setFocus([index]); + this.list.reveal(index); + return; + } + } + } + + dispose() { + this.disable(); + this.disposables = dispose(this.disposables); + } +} + class DOMFocusController implements IDisposable { private disposables: IDisposable[] = []; @@ -337,7 +444,7 @@ class DOMFocusController implements IDisposable { ) { this.disposables = []; - const onKeyDown = chain(domEvent(view.domNode, 'keydown')) + const onKeyDown = Event.chain(domEvent(view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -395,7 +502,7 @@ function isMouseRightClick(event: UIEvent): boolean { return event instanceof MouseEvent && event.button === 2; } -const DefaultMultipleSelectionContoller = { +const DefaultMultipleSelectionController = { isSelectionSingleChangeEvent, isSelectionRangeChangeEvent }; @@ -410,73 +517,38 @@ const DefaultOpenController: IOpenController = { } }; -class MouseController implements IDisposable { +export class MouseController implements IDisposable { private multipleSelectionSupport: boolean; - private multipleSelectionController: IMultipleSelectionController; + readonly multipleSelectionController: IMultipleSelectionController; private openController: IOpenController; - private didJustPressContextMenuKey: boolean = false; + private mouseSupport: boolean; private disposables: IDisposable[] = []; - @memoize get onContextMenu(): Event> { - const fromKeydown = chain(domEvent(this.view.domNode, 'keydown')) - .map(e => new StandardKeyboardEvent(e)) - .filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) - .filter(e => { e.preventDefault(); e.stopPropagation(); return false; }) - .map(event => { - const index = this.list.getFocus()[0]; - const element = this.view.element(index); - const anchor = this.view.domElement(index) || undefined; - return { index, element, anchor, browserEvent: event.browserEvent }; - }) - .event; - - const fromKeyup = chain(domEvent(this.view.domNode, 'keyup')) - .filter(() => { - const didJustPressContextMenuKey = this.didJustPressContextMenuKey; - this.didJustPressContextMenuKey = false; - return didJustPressContextMenuKey; - }) - .filter(() => this.list.getFocus().length > 0) - .map(browserEvent => { - const index = this.list.getFocus()[0]; - const element = this.view.element(index); - const anchor = this.view.domElement(index) || undefined; - return { index, element, anchor, browserEvent }; - }) - .filter(({ anchor }) => !!anchor) - .event; - - const fromMouse = chain(this.view.onContextMenu) - .filter(() => !this.didJustPressContextMenuKey) - .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY }, browserEvent })) - .event; - - return anyEvent>(fromKeydown, fromKeyup, fromMouse); - } - - constructor( - private list: List, - private view: ListView, - private options: IListOptions = {} - ) { - this.multipleSelectionSupport = !(options.multipleSelectionSupport === false); + constructor(protected list: List) { + this.multipleSelectionSupport = !(list.options.multipleSelectionSupport === false); if (this.multipleSelectionSupport) { - this.multipleSelectionController = options.multipleSelectionController || DefaultMultipleSelectionContoller; + this.multipleSelectionController = list.options.multipleSelectionController || DefaultMultipleSelectionController; } - this.openController = options.openController || DefaultOpenController; + this.openController = list.options.openController || DefaultOpenController; + this.mouseSupport = typeof list.options.mouseSupport === 'undefined' || !!list.options.mouseSupport; - view.onMouseDown(this.onMouseDown, this, this.disposables); - view.onMouseClick(this.onPointer, this, this.disposables); - view.onMouseDblClick(this.onDoubleClick, this, this.disposables); - view.onTouchStart(this.onMouseDown, this, this.disposables); - view.onTap(this.onPointer, this, this.disposables); - Gesture.addTarget(view.domNode); + if (this.mouseSupport) { + list.onMouseDown(this.onMouseDown, this, this.disposables); + list.onContextMenu(this.onContextMenu, this, this.disposables); + list.onMouseDblClick(this.onDoubleClick, this, this.disposables); + list.onTouchStart(this.onMouseDown, this, this.disposables); + Gesture.addTarget(list.getHTMLElement()); + } + + list.onMouseClick(this.onPointer, this, this.disposables); + list.onMouseMiddleClick(this.onPointer, this, this.disposables); + list.onTap(this.onPointer, this, this.disposables); } - private isSelectionSingleChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { + protected isSelectionSingleChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { if (this.multipleSelectionController) { return this.multipleSelectionController.isSelectionSingleChangeEvent(event); } @@ -484,7 +556,7 @@ class MouseController implements IDisposable { return platform.isMacintosh ? event.browserEvent.metaKey : event.browserEvent.ctrlKey; } - private isSelectionRangeChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { + protected isSelectionRangeChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { if (this.multipleSelectionController) { return this.multipleSelectionController.isSelectionRangeChangeEvent(event); } @@ -497,31 +569,48 @@ class MouseController implements IDisposable { } private onMouseDown(e: IListMouseEvent | IListTouchEvent): void { - if (this.options.focusOnMouseDown === false) { - e.browserEvent.preventDefault(); - e.browserEvent.stopPropagation(); - } else if (document.activeElement !== e.browserEvent.target) { - this.view.domNode.focus(); + if (document.activeElement !== e.browserEvent.target) { + this.list.domFocus(); + } + } + + private onContextMenu(e: IListContextMenuEvent): void { + const focus = typeof e.index === 'undefined' ? [] : [e.index]; + this.list.setFocus(focus, e.browserEvent); + } + + protected onPointer(e: IListMouseEvent): void { + if (!this.mouseSupport) { + return; + } + + if (isInputElement(e.browserEvent.target as HTMLElement)) { + return; } let reference = this.list.getFocus()[0]; const selection = this.list.getSelection(); reference = reference === undefined ? selection[0] : reference; - if (this.multipleSelectionSupport && this.isSelectionRangeChangeEvent(e)) { - return this.changeSelection(e, reference); + const focus = e.index; + + if (typeof focus === 'undefined') { + this.list.setFocus([], e.browserEvent); + this.list.setSelection([], e.browserEvent); + return; } - const focus = e.index; - if (selection.every(s => s !== focus)) { - this.list.setFocus([focus], e.browserEvent); + if (this.multipleSelectionSupport && this.isSelectionRangeChangeEvent(e)) { + return this.changeSelection(e, reference); } if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { return this.changeSelection(e, reference); } - if (this.options.selectOnMouseDown && !isMouseRightClick(e.browserEvent)) { + this.list.setFocus([focus], e.browserEvent); + + if (!isMouseRightClick(e.browserEvent)) { this.list.setSelection([focus], e.browserEvent); if (this.openController.shouldOpen(e.browserEvent)) { @@ -530,22 +619,11 @@ class MouseController implements IDisposable { } } - private onPointer(e: IListMouseEvent): void { - if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { + private onDoubleClick(e: IListMouseEvent): void { + if (isInputElement(e.browserEvent.target as HTMLElement)) { return; } - if (!this.options.selectOnMouseDown) { - const focus = this.list.getFocus(); - this.list.setSelection(focus, e.browserEvent); - - if (this.openController.shouldOpen(e.browserEvent)) { - this.list.open(focus, e.browserEvent); - } - } - } - - private onDoubleClick(e: IListMouseEvent): void { if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { return; } @@ -556,7 +634,7 @@ class MouseController implements IDisposable { } private changeSelection(e: IListMouseEvent | IListTouchEvent, reference: number | undefined): void { - const focus = e.index; + const focus = e.index!; if (this.isSelectionRangeChangeEvent(e) && reference !== undefined) { const min = Math.min(reference, focus); @@ -612,9 +690,14 @@ export interface IAccessibilityProvider { * Returning null will not disable ARIA for the element. Instead it is up to the screen reader * to compute a meaningful label based on the contents of the element in the DOM * - * See also: https://www.w3.org/TR/wai-aria/states_and_properties#aria-label + * See also: https://www.w3.org/TR/wai-aria/#aria-label */ getAriaLabel(element: T): string | null; + + /** + * https://www.w3.org/TR/wai-aria/#aria-level + */ + getAriaLevel?(element: T): number | undefined; } export class DefaultStyleController implements IStyleController { @@ -644,11 +727,17 @@ export class DefaultStyleController implements IStyleController { } if (styles.listFocusAndSelectionBackground) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; }`); + content.push(` + .monaco-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } + `); } if (styles.listFocusAndSelectionForeground) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; }`); + content.push(` + .monaco-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } + `); } if (styles.listInactiveFocusBackground) { @@ -666,11 +755,11 @@ export class DefaultStyleController implements IStyleController { } if (styles.listHoverBackground) { - content.push(`.monaco-list${suffix} .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`); + content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover:not(.selected):not(.focused) { background-color: ${styles.listHoverBackground}; }`); } if (styles.listHoverForeground) { - content.push(`.monaco-list${suffix} .monaco-list-row:hover { color: ${styles.listHoverForeground}; }`); + content.push(`.monaco-list${suffix} .monaco-list-row:hover:not(.selected):not(.focused) { color: ${styles.listHoverForeground}; }`); } if (styles.listSelectionOutline) { @@ -678,7 +767,10 @@ export class DefaultStyleController implements IStyleController { } if (styles.listFocusOutline) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`); + content.push(` + .monaco-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } + `); } if (styles.listInactiveFocusOutline) { @@ -689,6 +781,29 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list${suffix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`); } + if (styles.listDropBackground) { + content.push(` + .monaco-list${suffix}.drop-target, + .monaco-list${suffix} .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } + `); + } + + if (styles.listFilterWidgetBackground) { + content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`); + } + + if (styles.listFilterWidgetOutline) { + content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`); + } + + if (styles.listFilterWidgetNoMatchesOutline) { + content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`); + } + + if (styles.listMatchesShadow) { + content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); + } + const newStyles = content.join('\n'); if (newStyles !== this.styleElement.innerHTML) { this.styleElement.innerHTML = newStyles; @@ -696,19 +811,29 @@ export class DefaultStyleController implements IStyleController { } } -export interface IListOptions extends IListViewOptions, IListStyles { - identityProvider?: IIdentityProvider; - ariaLabel?: string; - mouseSupport?: boolean; - selectOnMouseDown?: boolean; - focusOnMouseDown?: boolean; - keyboardSupport?: boolean; - verticalScrollMode?: ScrollbarVisibility; - multipleSelectionSupport?: boolean; - multipleSelectionController?: IMultipleSelectionController; - openController?: IOpenController; - styleController?: IStyleController; - accessibilityProvider?: IAccessibilityProvider; +export interface IListOptions extends IListStyles { + readonly identityProvider?: IIdentityProvider; + readonly dnd?: IListDragAndDrop; + readonly enableKeyboardNavigation?: boolean; + readonly automaticKeyboardNavigation?: boolean; + readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; + readonly ariaRole?: ListAriaRootRole; + readonly ariaLabel?: string; + readonly keyboardSupport?: boolean; + readonly multipleSelectionSupport?: boolean; + readonly multipleSelectionController?: IMultipleSelectionController; + readonly openController?: IOpenController; + readonly styleController?: IStyleController; + readonly accessibilityProvider?: IAccessibilityProvider; + + // list view options + readonly useShadows?: boolean; + readonly verticalScrollMode?: ScrollbarVisibility; + readonly setRowLineHeight?: boolean; + readonly supportDynamicHeights?: boolean; + readonly mouseSupport?: boolean; + readonly horizontalScrolling?: boolean; + readonly ariaSetProvider?: IAriaSetProvider; } export interface IListStyles { @@ -728,6 +853,10 @@ export interface IListStyles { listInactiveFocusOutline?: Color; listSelectionOutline?: Color; listHoverOutline?: Color; + listFilterWidgetBackground?: Color; + listFilterWidgetOutline?: Color; + listFilterWidgetNoMatchesOutline?: Color; + listMatchesShadow?: Color; } const defaultStyles: IListStyles = { @@ -741,10 +870,17 @@ const defaultStyles: IListStyles = { listDropBackground: Color.fromHex('#383B3D') }; -const DefaultOptions: IListOptions = { +const DefaultOptions = { keyboardSupport: true, mouseSupport: true, - multipleSelectionSupport: true + multipleSelectionSupport: true, + dnd: { + getDragURI() { return null; }, + onDragStart(): void { }, + onDragOver() { return false; }, + drop() { } + }, + ariaRootRole: ListAriaRootRole.TREE }; // TODO@Joao: move these utils into a SortedArray class @@ -773,7 +909,7 @@ function getContiguousRangeContaining(range: number[], value: number): number[] /** * Given two sorted collections of numbers, returns the intersection - * betweem them (OR). + * between them (OR). */ function disjunction(one: number[], other: number[]): number[] { const result: number[] = []; @@ -832,7 +968,7 @@ class PipelineRenderer implements IListRenderer { constructor( private _templateId: string, - private renderers: IListRenderer[] + private renderers: IListRenderer[] ) { } get templateId(): string { @@ -843,19 +979,23 @@ class PipelineRenderer implements IListRenderer { return this.renderers.map(r => r.renderTemplate(container)); } - renderElement(element: T, index: number, templateData: any[]): void { + renderElement(element: T, index: number, templateData: any[], dynamicHeightProbing?: boolean): void { let i = 0; for (const renderer of this.renderers) { - renderer.renderElement(element, index, templateData[i++]); + renderer.renderElement(element, index, templateData[i++], dynamicHeightProbing); } } - disposeElement(element: T, index: number, templateData: any[]): void { + disposeElement(element: T, index: number, templateData: any[], dynamicHeightProbing?: boolean): void { let i = 0; for (const renderer of this.renderers) { - renderer.disposeElement(element, index, templateData[i++]); + if (renderer.disposeElement) { + renderer.disposeElement(element, index, templateData[i], dynamicHeightProbing); + } + + i += 1; } } @@ -872,9 +1012,7 @@ class AccessibiltyRenderer implements IListRenderer { templateId: string = 'a18n'; - constructor(private accessibilityProvider: IAccessibilityProvider) { - - } + constructor(private accessibilityProvider: IAccessibilityProvider) { } renderTemplate(container: HTMLElement): HTMLElement { return container; @@ -888,10 +1026,14 @@ class AccessibiltyRenderer implements IListRenderer { } else { container.removeAttribute('aria-label'); } - } - disposeElement(element: T, index: number, container: HTMLElement): void { - // noop + const ariaLevel = this.accessibilityProvider.getAriaLevel && this.accessibilityProvider.getAriaLevel(element); + + if (typeof ariaLevel === 'number') { + container.setAttribute('aria-level', `${ariaLevel}`); + } else { + container.removeAttribute('aria-level'); + } } disposeTemplate(templateData: any): void { @@ -899,39 +1041,78 @@ class AccessibiltyRenderer implements IListRenderer { } } -export class List implements ISpliceable, IDisposable { +class ListViewDragAndDrop implements IListViewDragAndDrop { - private static InstanceCount = 0; - private idPrefix = `list_id_${++List.InstanceCount}`; + constructor(private list: List, private dnd: IListDragAndDrop) { } + + getDragElements(element: T): T[] { + const selection = this.list.getSelectedElements(); + const elements = selection.indexOf(element) > -1 ? selection : [element]; + return elements; + } + + getDragURI(element: T): string | null { + return this.dnd.getDragURI(element); + } + + getDragLabel?(elements: T[]): string | undefined { + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(elements); + } + + return undefined; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + if (this.dnd.onDragStart) { + this.dnd.onDragStart(data, originalEvent); + } + } + + onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { + return this.dnd.onDragOver(data, targetElement, targetIndex, originalEvent); + } + + drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void { + this.dnd.drop(data, targetElement, targetIndex, originalEvent); + } +} + +export interface IListOptionsUpdate { + readonly enableKeyboardNavigation?: boolean; + readonly automaticKeyboardNavigation?: boolean; +} + +export class List implements ISpliceable, IDisposable { private focus: Trait; private selection: Trait; private eventBufferer = new EventBufferer(); private view: ListView; private spliceable: ISpliceable; - protected disposables: IDisposable[]; private styleElement: HTMLStyleElement; private styleController: IStyleController; - private mouseController: MouseController; + private typeLabelController?: TypeLabelController; + + protected disposables: IDisposable[]; @memoize get onFocusChange(): Event> { - return mapEvent(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); + return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); } @memoize get onSelectionChange(): Event> { - return mapEvent(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e)); + return Event.map(this.eventBufferer.wrapEvent(this.selection.onChange), e => this.toListEvent(e)); } - readonly onContextMenu: Event> = Event.None; - - private _onOpen = new Emitter>(); - readonly onOpen: Event> = this._onOpen.event; + private _onDidOpen = new Emitter>(); + readonly onDidOpen: Event> = this._onDidOpen.event; private _onPin = new Emitter(); @memoize get onPin(): Event> { - return mapEvent(this._onPin.event, indexes => this.toListEvent({ indexes })); + return Event.map(this._onPin.event, indexes => this.toListEvent({ indexes })); } + get onDidScroll(): Event { return this.view.onDidScroll; } get onMouseClick(): Event> { return this.view.onMouseClick; } get onMouseDblClick(): Event> { return this.view.onMouseDblClick; } get onMouseMiddleClick(): Event> { return this.view.onMouseMiddleClick; } @@ -943,6 +1124,37 @@ export class List implements ISpliceable, IDisposable { get onTouchStart(): Event> { return this.view.onTouchStart; } get onTap(): Event> { return this.view.onTap; } + private didJustPressContextMenuKey: boolean = false; + @memoize get onContextMenu(): Event> { + const fromKeydown = Event.chain(domEvent(this.view.domNode, 'keydown')) + .map(e => new StandardKeyboardEvent(e)) + .filter(e => this.didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) + .filter(e => { e.preventDefault(); e.stopPropagation(); return false; }) + .event as Event; + + const fromKeyup = Event.chain(domEvent(this.view.domNode, 'keyup')) + .filter(() => { + const didJustPressContextMenuKey = this.didJustPressContextMenuKey; + this.didJustPressContextMenuKey = false; + return didJustPressContextMenuKey; + }) + .filter(() => this.getFocus().length > 0 && !!this.view.domElement(this.getFocus()[0])) + .map(browserEvent => { + const index = this.getFocus()[0]; + const element = this.view.element(index); + const anchor = this.view.domElement(index) as HTMLElement; + return { index, element, anchor, browserEvent }; + }) + .event; + + const fromMouse = Event.chain(this.view.onContextMenu) + .filter(() => !this.didJustPressContextMenuKey) + .map(({ element, index, browserEvent }) => ({ element, index, anchor: { x: browserEvent.clientX + 1, y: browserEvent.clientY }, browserEvent })) + .event; + + return Event.any>(fromKeydown, fromKeyup, fromMouse); + } + get onKeyDown(): Event { return domEvent(this.view.domNode, 'keydown'); } get onKeyUp(): Event { return domEvent(this.view.domNode, 'keyup'); } get onKeyPress(): Event { return domEvent(this.view.domNode, 'keypress'); } @@ -956,63 +1168,88 @@ export class List implements ISpliceable, IDisposable { constructor( container: HTMLElement, virtualDelegate: IListVirtualDelegate, - renderers: IListRenderer[], - options: IListOptions = DefaultOptions + renderers: IListRenderer[], + private _options: IListOptions = DefaultOptions ) { - this.focus = new FocusTrait(i => this.getElementDomId(i)); + this.focus = new FocusTrait(); this.selection = new Trait('selected'); - mixin(options, defaultStyles, false); + mixin(_options, defaultStyles, false); const baseRenderers: IListRenderer[] = [this.focus.renderer, this.selection.renderer]; - if (options.accessibilityProvider) { - baseRenderers.push(new AccessibiltyRenderer(options.accessibilityProvider)); + if (_options.accessibilityProvider) { + baseRenderers.push(new AccessibiltyRenderer(_options.accessibilityProvider)); } renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); - this.view = new ListView(container, virtualDelegate, renderers, options); - this.view.domNode.setAttribute('role', 'tree'); - DOM.addClass(this.view.domNode, this.idPrefix); - this.view.domNode.tabIndex = 0; + const viewOptions: IListViewOptions = { + ..._options, + dnd: _options.dnd && new ListViewDragAndDrop(this, _options.dnd) + }; + + this.view = new ListView(container, virtualDelegate, renderers, viewOptions); + + if (typeof _options.ariaRole !== 'string') { + this.view.domNode.setAttribute('role', ListAriaRootRole.TREE); + } else { + this.view.domNode.setAttribute('role', _options.ariaRole); + } this.styleElement = DOM.createStyleSheet(this.view.domNode); - this.styleController = options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix); + this.styleController = _options.styleController || new DefaultStyleController(this.styleElement, this.view.domId); this.spliceable = new CombinedSpliceable([ - new TraitSpliceable(this.focus, this.view, options.identityProvider), - new TraitSpliceable(this.selection, this.view, options.identityProvider), + new TraitSpliceable(this.focus, this.view, _options.identityProvider), + new TraitSpliceable(this.selection, this.view, _options.identityProvider), this.view ]); this.disposables = [this.focus, this.selection, this.view, this._onDidDispose]; - this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null!); - this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null!); + this.onDidFocus = Event.map(domEvent(this.view.domNode, 'focus', true), () => null!); + this.onDidBlur = Event.map(domEvent(this.view.domNode, 'blur', true), () => null!); this.disposables.push(new DOMFocusController(this, this.view)); - if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) { - const controller = new KeyboardController(this, this.view, options); + if (typeof _options.keyboardSupport !== 'boolean' || _options.keyboardSupport) { + const controller = new KeyboardController(this, this.view, _options); this.disposables.push(controller); } - if (typeof options.mouseSupport !== 'boolean' || options.mouseSupport) { - this.mouseController = new MouseController(this, this.view, options); - this.disposables.push(this.mouseController); - this.onContextMenu = this.mouseController.onContextMenu; + if (_options.keyboardNavigationLabelProvider) { + this.typeLabelController = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider); + this.disposables.push(this.typeLabelController); } + this.disposables.push(this.createMouseController(_options)); + this.onFocusChange(this._onFocusChange, this, this.disposables); this.onSelectionChange(this._onSelectionChange, this, this.disposables); - if (options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", options.ariaLabel)); + if (_options.ariaLabel) { + this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel)); } - this.style(options); + this.style(_options); + } + + protected createMouseController(options: IListOptions): MouseController { + return new MouseController(this); + } + + updateOptions(optionsUpdate: IListOptionsUpdate = {}): void { + this._options = { ...this._options, ...optionsUpdate }; + + if (this.typeLabelController) { + this.typeLabelController.updateOptions(this._options); + } + } + + get options(): IListOptions { + return this._options; } splice(start: number, deleteCount: number, elements: T[] = []): void { @@ -1031,12 +1268,28 @@ export class List implements ISpliceable, IDisposable { this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements)); } + updateWidth(index: number): void { + this.view.updateWidth(index); + } + + rerender(): void { + this.view.rerender(); + } + + element(index: number): T { + return this.view.element(index); + } + get length(): number { return this.view.length; } get contentHeight(): number { - return this.view.getContentHeight(); + return this.view.contentHeight; + } + + get onDidChangeContentHeight(): Event { + return this.view.onDidChangeContentHeight; } get scrollTop(): number { @@ -1047,16 +1300,34 @@ export class List implements ISpliceable, IDisposable { this.view.setScrollTop(scrollTop); } + get scrollHeight(): number { + return this.view.scrollHeight; + } + + get renderHeight(): number { + return this.view.renderHeight; + } + + get firstVisibleIndex(): number { + return this.view.firstVisibleIndex; + } + + get lastVisibleIndex(): number { + return this.view.lastVisibleIndex; + } + domFocus(): void { this.view.domNode.focus(); } - layout(height?: number): void { - this.view.layout(height); + layout(height?: number, width?: number): void { + this.view.layout(height, width); } - layoutWidth(width: number): void { - this.view.layoutWidth(width); + toggleKeyboardNavigation(): void { + if (this.typeLabelController) { + this.typeLabelController.toggle(); + } } setSelection(indexes: number[], browserEvent?: UIEvent): void { @@ -1066,7 +1337,6 @@ export class List implements ISpliceable, IDisposable { } } - indexes = indexes.sort(numericSort); this.selection.set(indexes, browserEvent); } @@ -1085,45 +1355,57 @@ export class List implements ISpliceable, IDisposable { } } - indexes = indexes.sort(numericSort); this.focus.set(indexes, browserEvent); } - focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { + focusNext(n = 1, loop = false, browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } + const focus = this.focus.get(); - let index = focus.length > 0 ? focus[0] + n : 0; - this.setFocus(loop ? [index % this.length] : [Math.min(index, this.length - 1)], browserEvent); + const index = this.findNextIndex(focus.length > 0 ? focus[0] + n : 0, loop, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } + const focus = this.focus.get(); - let index = focus.length > 0 ? focus[0] - n : 0; - if (loop && index < 0) { index = (this.length + (index % this.length)) % this.length; } - this.setFocus([Math.max(index, 0)], browserEvent); + const index = this.findPreviousIndex(focus.length > 0 ? focus[0] - n : 0, loop, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusNextPage(browserEvent?: UIEvent): void { + focusNextPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; const lastPageElement = this.view.element(lastPageIndex); const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== lastPageElement) { - this.setFocus([lastPageIndex], browserEvent); + const lastGoodPageIndex = this.findPreviousIndex(lastPageIndex, false, filter); + + if (lastGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(lastGoodPageIndex)) { + this.setFocus([lastGoodPageIndex], browserEvent); + } else { + this.setFocus([lastPageIndex], browserEvent); + } } else { const previousScrollTop = this.view.getScrollTop(); this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusNextPage(browserEvent), 0); + setTimeout(() => this.focusNextPage(browserEvent, filter), 0); } } } - focusPreviousPage(browserEvent?: UIEvent): void { + focusPreviousPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { let firstPageIndex: number; const scrollTop = this.view.getScrollTop(); @@ -1137,26 +1419,78 @@ export class List implements ISpliceable, IDisposable { const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== firstPageElement) { - this.setFocus([firstPageIndex], browserEvent); + const firstGoodPageIndex = this.findNextIndex(firstPageIndex, false, filter); + + if (firstGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(firstGoodPageIndex)) { + this.setFocus([firstGoodPageIndex], browserEvent); + } else { + this.setFocus([firstPageIndex], browserEvent); + } } else { const previousScrollTop = scrollTop; this.view.setScrollTop(scrollTop - this.view.renderHeight); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusPreviousPage(browserEvent), 0); + setTimeout(() => this.focusPreviousPage(browserEvent, filter), 0); } } } - focusLast(browserEvent?: UIEvent): void { + focusLast(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } - this.setFocus([this.length - 1], browserEvent); + + const index = this.findPreviousIndex(this.length - 1, false, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusFirst(browserEvent?: UIEvent): void { + focusFirst(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } - this.setFocus([0], browserEvent); + + const index = this.findNextIndex(0, false, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } + } + + private findNextIndex(index: number, loop = false, filter?: (element: T) => boolean): number { + for (let i = 0; i < this.length; i++) { + if (index >= this.length && !loop) { + return -1; + } + + index = index % this.length; + + if (!filter || filter(this.element(index))) { + return index; + } + + index++; + } + + return -1; + } + + private findPreviousIndex(index: number, loop = false, filter?: (element: T) => boolean): number { + for (let i = 0; i < this.length; i++) { + if (index < 0 && !loop) { + return -1; + } + + index = (this.length + (index % this.length)) % this.length; + + if (!filter || filter(this.element(index))) { + return index; + } + + index--; + } + + return -1; } getFocus(): number[] { @@ -1214,10 +1548,6 @@ export class List implements ISpliceable, IDisposable { return Math.abs((scrollTop - elementTop) / m); } - private getElementDomId(index: number): string { - return `${this.idPrefix}_${index}`; - } - isDOMFocused(): boolean { return this.view.domNode === document.activeElement; } @@ -1233,7 +1563,7 @@ export class List implements ISpliceable, IDisposable { } } - this._onOpen.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent }); + this._onDidOpen.fire({ indexes, elements: indexes.map(i => this.view.element(i)), browserEvent }); } pin(indexes: number[]): void { @@ -1258,7 +1588,7 @@ export class List implements ISpliceable, IDisposable { const focus = this.focus.get(); if (focus.length > 0) { - this.view.domNode.setAttribute('aria-activedescendant', this.getElementDomId(focus[0])); + this.view.domNode.setAttribute('aria-activedescendant', this.view.getElementDomId(focus[0])); } else { this.view.domNode.removeAttribute('aria-activedescendant'); } @@ -1279,7 +1609,7 @@ export class List implements ISpliceable, IDisposable { this._onDidDispose.fire(); this.disposables = dispose(this.disposables); - this._onOpen.dispose(); + this._onDidOpen.dispose(); this._onPin.dispose(); this._onDidDispose.dispose(); } diff --git a/src/vs/workbench/electron-browser/media/remove-dark.svg b/src/vs/base/browser/ui/list/media/close-dark.svg similarity index 100% rename from src/vs/workbench/electron-browser/media/remove-dark.svg rename to src/vs/base/browser/ui/list/media/close-dark.svg diff --git a/src/vs/base/browser/ui/list/media/close-hc.svg b/src/vs/base/browser/ui/list/media/close-hc.svg new file mode 100644 index 00000000000..c20895c60aa --- /dev/null +++ b/src/vs/base/browser/ui/list/media/close-hc.svg @@ -0,0 +1,33 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/vs/workbench/parts/feedback/electron-browser/media/close.svg b/src/vs/base/browser/ui/list/media/close.svg similarity index 100% rename from src/vs/workbench/parts/feedback/electron-browser/media/close.svg rename to src/vs/base/browser/ui/list/media/close.svg diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg new file mode 100644 index 00000000000..0925909a117 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/filter-hc.svg b/src/vs/base/browser/ui/list/media/filter-hc.svg new file mode 100644 index 00000000000..0e2724f52e2 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter-hc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/filter.svg b/src/vs/base/browser/ui/list/media/filter.svg new file mode 100644 index 00000000000..f8c011064b1 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg new file mode 100644 index 00000000000..5f495c006dc --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-hc.svg b/src/vs/base/browser/ui/list/media/no-filter-hc.svg new file mode 100644 index 00000000000..31d8f70f05a --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter-hc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter.svg b/src/vs/base/browser/ui/list/media/no-filter.svg new file mode 100644 index 00000000000..760cc3c4403 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 45b164c5392..73c6b7419ed 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -84,7 +84,7 @@ export function consolidate(groups: IRangedGroup[]): IRangedGroup[] { * collection. */ function concat(...groups: IRangedGroup[][]): IRangedGroup[] { - return consolidate(groups.reduce((r, g) => r.concat(g), [] as IRangedGroup[])); + return consolidate(groups.reduce((r, g) => r.concat(g), [])); } export class RangeMap { diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index b7007c2fc0f..dd54c73a0a7 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -38,7 +38,7 @@ export class RowCache implements IDisposable { if (!result) { const domNode = $('.monaco-list-row'); - const renderer = this.renderers.get(templateId); + const renderer = this.getRenderer(templateId); const templateData = renderer.renderTemplate(domNode); result = { domNode, templateId, templateData }; } @@ -86,7 +86,7 @@ export class RowCache implements IDisposable { this.cache.forEach((cachedRows, templateId) => { for (const cachedRow of cachedRows) { - const renderer = this.renderers.get(templateId); + const renderer = this.getRenderer(templateId); renderer.disposeTemplate(cachedRow.templateData); cachedRow.domNode = null; cachedRow.templateData = null; @@ -101,4 +101,12 @@ export class RowCache implements IDisposable { this.cache.clear(); this.renderers = null!; // StrictNullOverride: nulling out ok in dispose } + + private getRenderer(templateId: string): IListRenderer { + const renderer = this.renderers.get(templateId); + if (!renderer) { + throw new Error(`No renderer found for ${templateId}`); + } + return renderer; + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/ellipsis.svg b/src/vs/base/browser/ui/menu/ellipsis.svg similarity index 100% rename from src/vs/workbench/parts/preferences/browser/media/ellipsis.svg rename to src/vs/base/browser/ui/menu/ellipsis.svg diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index ecbb197f55c..f1cdb6f3e45 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -30,6 +30,7 @@ display: flex; height: 2em; align-items: center; + position: relative; } .monaco-menu .monaco-action-bar.vertical .action-label { @@ -74,6 +75,16 @@ margin: 0; } +.monaco-menu .monaco-action-bar.vertical .action-item { + position: static; + overflow: visible; +} + + +.monaco-menu .monaco-action-bar.vertical .action-item .monaco-submenu { + position: absolute; +} + .monaco-menu .monaco-action-bar.vertical .action-label.separator { padding: 0.5em 0 0 0; margin-bottom: 0.5em; @@ -106,7 +117,6 @@ /* Context Menu */ .context-view.monaco-menu-container { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; -webkit-animation: fadeIn 0.083s linear; @@ -134,4 +144,62 @@ .hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused { background: none; +} + +/* Menubar styles */ + +.menubar { + display: flex; + flex-shrink: 1; + box-sizing: border-box; + height: 30px; + overflow: hidden; + flex-wrap: wrap; +} + +.fullscreen .menubar { + margin: 0px; + padding: 0px 5px; +} + +.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; + outline: 0; +} + +.menubar .menubar-menu-items-holder { + position: absolute; + left: 0px; + opacity: 1; + z-index: 2000; +} + +.menubar .menubar-menu-items-holder.monaco-menu-container { + outline: 0; + border: none; +} + +.menubar .menubar-menu-items-holder.monaco-menu-container :focus { + outline: 0; +} + +.menubar .toolbar-toggle-more { + background-position: center; + background-repeat: no-repeat; + background-size: 14px; + width: 20px; + height: 100%; +} + +.menubar .toolbar-toggle-more { + display: inline-block; + padding: 0; + -webkit-mask: url('ellipsis.svg') no-repeat 50% 55%/14px 14px; + mask: url('ellipsis.svg') no-repeat 50% 55%/14px 14px; } \ 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 d3444151c1e..511dd9f0615 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -7,23 +7,41 @@ import 'vs/css!./menu'; import * as nls from 'vs/nls'; 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, KeyCodeUtils } from 'vs/base/common/keyCodes'; -import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, getClientArea, removeClasses } from 'vs/base/browser/dom'; +import { ActionBar, IActionViewItemProvider, ActionsOrientation, Separator, ActionViewItem, IActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; +import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; +import { Event, Emitter } from 'vs/base/common/event'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { isLinux } from 'vs/base/common/platform'; -export const MENU_MNEMONIC_REGEX: RegExp = /\(&{1,2}(.)\)|&{1,2}(.)/; -export const MENU_ESCAPED_MNEMONIC_REGEX: RegExp = /(?:&){1,2}(.)/; +function createMenuMnemonicRegExp() { + try { + return new RegExp('\\(&([^\\s&])\\)|(? ResolvedKeybinding; + getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined; ariaLabel?: string; enableMnemonics?: boolean; anchorAlignment?: AnchorAlignment; @@ -41,7 +59,7 @@ export interface IMenuStyles { } export class SubmenuAction extends Action { - constructor(label: string, public entries: (SubmenuAction | IAction)[], cssClass?: string) { + constructor(label: string, public entries: Array, cssClass?: string) { super(!!cssClass ? cssClass : 'submenu', label, '', true); } } @@ -52,80 +70,122 @@ interface ISubMenuData { } export class Menu extends ActionBar { - private mnemonics: Map>; + private mnemonics: Map>; private menuDisposables: IDisposable[]; + private scrollableElement: DomScrollableElement; + private menuElement: HTMLElement; + private scrollTopHold: number | undefined; + + private readonly _onScroll: Emitter; 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); + const menuElement = document.createElement('div'); + addClass(menuElement, 'monaco-menu'); + menuElement.setAttribute('role', 'presentation'); - super(menuContainer, { + super(menuElement, { orientation: ActionsOrientation.VERTICAL, - actionItemProvider: action => this.doGetActionItem(action, options, parentData), + actionViewItemProvider: action => this.doGetActionViewItem(action, options, parentData), context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, triggerKeys: { keys: [KeyCode.Enter], keyDown: true } }); + this.menuElement = menuElement; + + this._onScroll = this._register(new Emitter()); + this.actionsList.setAttribute('role', 'menu'); this.actionsList.tabIndex = 0; this.menuDisposables = []; + addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { + const event = new StandardKeyboardEvent(e); + + // Stop tab navigation of menus + if (event.equals(KeyCode.Tab)) { + EventHelper.stop(e, true); + } + }); + if (options.enableMnemonics) { - this.menuDisposables.push(addDisposableListener(menuContainer, EventType.KEY_DOWN, (e) => { - const key = KeyCodeUtils.fromString(e.key); + this.menuDisposables.push(addDisposableListener(menuElement, EventType.KEY_DOWN, (e) => { + const key = e.key.toLocaleLowerCase(); if (this.mnemonics.has(key)) { EventHelper.stop(e, true); - const actions = this.mnemonics.get(key); + const actions = this.mnemonics.get(key)!; if (actions.length === 1) { - if (actions[0] instanceof SubmenuActionItem) { + if (actions[0] instanceof SubmenuMenuActionViewItem) { this.focusItemByElement(actions[0].container); } - actions[0].onClick(event); + actions[0].onClick(e); } if (actions.length > 1) { const action = actions.shift(); - this.focusItemByElement(action.container); + if (action) { + this.focusItemByElement(action.container); + actions.push(action); + } - actions.push(action); this.mnemonics.set(key, actions); } } })); } + if (isLinux) { + this._register(addDisposableListener(menuElement, EventType.KEY_DOWN, e => { + const event = new StandardKeyboardEvent(e); + + if (event.equals(KeyCode.Home) || event.equals(KeyCode.PageUp)) { + this.focusedItem = this.viewItems.length - 1; + this.focusNext(); + EventHelper.stop(e, true); + } else if (event.equals(KeyCode.End) || event.equals(KeyCode.PageDown)) { + this.focusedItem = 0; + this.focusPrevious(); + EventHelper.stop(e, true); + } + })); + } + this._register(addDisposableListener(this.domNode, EventType.MOUSE_OUT, e => { - let relatedTarget = (e as MouseEvent).relatedTarget as HTMLElement; + let relatedTarget = e.relatedTarget as HTMLElement; if (!isAncestor(relatedTarget, this.domNode)) { this.focusedItem = undefined; + this.scrollTopHold = this.menuElement.scrollTop; this.updateFocus(); e.stopPropagation(); } })); + this._register(addDisposableListener(this.domNode, EventType.MOUSE_UP, e => { + // Absorb clicks in menu dead space https://github.com/Microsoft/vscode/issues/63575 + EventHelper.stop(e, true); + })); + this._register(addDisposableListener(this.actionsList, 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) { + while (target.parentElement !== this.actionsList && target.parentElement !== null) { target = target.parentElement; } if (hasClass(target, 'action-item')) { const lastFocusedItem = this.focusedItem; + this.scrollTopHold = this.menuElement.scrollTop; this.setFocusedItem(target); if (lastFocusedItem !== this.focusedItem) { @@ -138,11 +198,41 @@ export class Menu extends ActionBar { parent: this }; - this.mnemonics = new Map>(); + this.mnemonics = new Map>(); this.push(actions, { icon: true, label: true, isMenu: true }); - this.items.filter(item => !(item instanceof MenuSeparatorActionItem)).forEach((item: MenuActionItem, index: number, array: any[]) => { + // Scroll Logic + this.scrollableElement = this._register(new DomScrollableElement(menuElement, { + alwaysConsumeMouseWheel: true, + horizontal: ScrollbarVisibility.Hidden, + vertical: ScrollbarVisibility.Visible, + verticalScrollbarSize: 7, + handleMouseWheel: true, + useShadows: true + })); + + const scrollElement = this.scrollableElement.getDomNode(); + scrollElement.style.position = null; + + menuElement.style.maxHeight = `${Math.max(10, window.innerHeight - container.getBoundingClientRect().top - 30)}px`; + + this.scrollableElement.onScroll(() => { + this._onScroll.fire(); + }, this, this.menuDisposables); + + this._register(addDisposableListener(this.menuElement, EventType.SCROLL, (e: ScrollEvent) => { + if (this.scrollTopHold !== undefined) { + this.menuElement.scrollTop = this.scrollTopHold; + this.scrollTopHold = undefined; + } + this.scrollableElement.scanDomNode(); + })); + + container.appendChild(this.scrollableElement.getDomNode()); + this.scrollableElement.scanDomNode(); + + this.viewItems.filter(item => !(item instanceof MenuSeparatorActionViewItem)).forEach((item: BaseMenuActionViewItem, index: number, array: any[]) => { item.updatePositionInSet(index + 1, array.length); }); } @@ -160,15 +250,41 @@ export class Menu extends ActionBar { this.domNode.style.backgroundColor = bgColor; container.style.boxShadow = shadow; - if (this.items) { - this.items.forEach(item => { - if (item instanceof MenuActionItem || item instanceof MenuSeparatorActionItem) { + if (this.viewItems) { + this.viewItems.forEach(item => { + if (item instanceof BaseMenuActionViewItem || item instanceof MenuSeparatorActionViewItem) { item.style(style); } }); } } + getContainer(): HTMLElement { + return this.scrollableElement.getDomNode(); + } + + get onScroll(): Event { + return this._onScroll.event; + } + + get scrollOffset(): number { + return this.menuElement.scrollTop; + } + + trigger(index: number): void { + if (index <= this.viewItems.length && index >= 0) { + const item = this.viewItems[index]; + if (item instanceof SubmenuMenuActionViewItem) { + super.focus(index); + item.open(true); + } else if (item instanceof BaseMenuActionViewItem) { + super.run(item._action, item._context); + } else { + return; + } + } + } + private focusItemByElement(element: HTMLElement) { const lastFocusedItem = this.focusedItem; this.setFocusedItem(element); @@ -188,66 +304,66 @@ export class Menu extends ActionBar { } } - private doGetActionItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): BaseActionItem { + private doGetActionViewItem(action: IAction, options: IMenuOptions, parentData: ISubMenuData): BaseActionViewItem { if (action instanceof Separator) { - return new MenuSeparatorActionItem(options.context, action, { icon: true }); + return new MenuSeparatorActionViewItem(options.context, action, { icon: true }); } else if (action instanceof SubmenuAction) { - const menuActionItem = new SubmenuActionItem(action, action.entries, parentData, options); + const menuActionViewItem = new SubmenuMenuActionViewItem(action, action.entries, parentData, options); if (options.enableMnemonics) { - const mnemonic = menuActionItem.getMnemonic(); - if (mnemonic && menuActionItem.isEnabled()) { - let actionItems: MenuActionItem[] = []; + const mnemonic = menuActionViewItem.getMnemonic(); + if (mnemonic && menuActionViewItem.isEnabled()) { + let actionViewItems: BaseMenuActionViewItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic); + actionViewItems = this.mnemonics.get(mnemonic)!; } - actionItems.push(menuActionItem); + actionViewItems.push(menuActionViewItem); - this.mnemonics.set(mnemonic, actionItems); + this.mnemonics.set(mnemonic, actionViewItems); } } - return menuActionItem; + return menuActionViewItem; } else { const menuItemOptions: IMenuItemOptions = { enableMnemonics: options.enableMnemonics }; if (options.getKeyBinding) { const keybinding = options.getKeyBinding(action); if (keybinding) { - menuItemOptions.keybinding = keybinding.getLabel(); + const keybindingLabel = keybinding.getLabel(); + + if (keybindingLabel) { + menuItemOptions.keybinding = keybindingLabel; + } } } - const menuActionItem = new MenuActionItem(options.context, action, menuItemOptions); + const menuActionViewItem = new BaseMenuActionViewItem(options.context, action, menuItemOptions); if (options.enableMnemonics) { - const mnemonic = menuActionItem.getMnemonic(); - if (mnemonic && menuActionItem.isEnabled()) { - let actionItems: MenuActionItem[] = []; + const mnemonic = menuActionViewItem.getMnemonic(); + if (mnemonic && menuActionViewItem.isEnabled()) { + let actionViewItems: BaseMenuActionViewItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic); + actionViewItems = this.mnemonics.get(mnemonic)!; } - actionItems.push(menuActionItem); + actionViewItems.push(menuActionViewItem); - this.mnemonics.set(mnemonic, actionItems); + this.mnemonics.set(mnemonic, actionViewItems); } } - return menuActionItem; + return menuActionViewItem; } } - - public focus(selectFirst = true) { - super.focus(selectFirst); - } } -interface IMenuItemOptions extends IActionItemOptions { +interface IMenuItemOptions extends IActionViewItemOptions { enableMnemonics?: boolean; } -class MenuActionItem extends BaseActionItem { +class BaseMenuActionViewItem extends BaseActionViewItem { public container: HTMLElement; @@ -256,7 +372,7 @@ class MenuActionItem extends BaseActionItem { private label: HTMLElement; private check: HTMLElement; - private mnemonic: KeyCode; + private mnemonic: string; private cssClass: string; protected menuStyle: IMenuStyles; @@ -275,7 +391,7 @@ class MenuActionItem extends BaseActionItem { if (label) { let matches = MENU_MNEMONIC_REGEX.exec(label); if (matches) { - this.mnemonic = KeyCodeUtils.fromString((!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase()); + this.mnemonic = (!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase(); } } } @@ -284,6 +400,10 @@ class MenuActionItem extends BaseActionItem { render(container: HTMLElement): void { super.render(container); + if (!this.element) { + return; + } + this.container = container; this.item = append(this.element, $('a.action-menu-item')); @@ -343,13 +463,16 @@ class MenuActionItem extends BaseActionItem { label = cleanLabel; } - this.label.setAttribute('aria-label', cleanLabel); + this.label.setAttribute('aria-label', cleanLabel.replace(/&&/g, '&')); const matches = MENU_MNEMONIC_REGEX.exec(label); if (matches) { label = strings.escape(label).replace(MENU_ESCAPED_MNEMONIC_REGEX, ''); + label = label.replace(/&&/g, '&'); this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[2]).toLocaleLowerCase()); + } else { + label = label.replace(/&&/g, '&'); } } @@ -381,7 +504,7 @@ class MenuActionItem extends BaseActionItem { removeClasses(this.item, this.cssClass); } if (this.options.icon) { - this.cssClass = this.getAction().class; + this.cssClass = this.getAction().class || ''; addClass(this.label, 'icon'); if (this.cssClass) { addClasses(this.label, this.cssClass); @@ -394,11 +517,17 @@ class MenuActionItem extends BaseActionItem { updateEnabled(): void { if (this.getAction().enabled) { - removeClass(this.element, 'disabled'); + if (this.element) { + removeClass(this.element, 'disabled'); + } + removeClass(this.item, 'disabled'); this.item.tabIndex = 0; } else { - addClass(this.element, 'disabled'); + if (this.element) { + addClass(this.element, 'disabled'); + } + addClass(this.item, 'disabled'); removeTabIndexAndUpdateFocus(this.item); } @@ -416,12 +545,16 @@ class MenuActionItem extends BaseActionItem { } } - getMnemonic(): KeyCode { + getMnemonic(): string { return this.mnemonic; } protected applyStyle(): void { - const isSelected = hasClass(this.element, 'focused'); + if (!this.menuStyle) { + return; + } + + const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; const border = isSelected && this.menuStyle.selectionBorderColor ? `1px solid ${this.menuStyle.selectionBorderColor}` : null; @@ -438,9 +571,9 @@ class MenuActionItem extends BaseActionItem { } } -class SubmenuActionItem extends MenuActionItem { - private mysubmenu: Menu; - private submenuContainer: HTMLElement; +class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { + private mysubmenu: Menu | null; + private submenuContainer: HTMLElement | undefined; private submenuIndicator: HTMLElement; private submenuDisposables: IDisposable[] = []; private mouseOver: boolean; @@ -463,7 +596,7 @@ class SubmenuActionItem extends MenuActionItem { }, 250); this.hideScheduler = new RunOnceScheduler(() => { - if ((!isAncestor(document.activeElement, this.element) && this.parentData.submenu === this.mysubmenu)) { + if (this.element && (!isAncestor(document.activeElement, this.element) && this.parentData.submenu === this.mysubmenu)) { this.parentData.parent.focus(false); this.cleanupExistingSubmenu(true); } @@ -473,6 +606,10 @@ class SubmenuActionItem extends MenuActionItem { render(container: HTMLElement): void { super.render(container); + if (!this.element) { + return; + } + addClass(this.item, 'monaco-submenu-item'); this.item.setAttribute('aria-haspopup', 'true'); @@ -480,7 +617,7 @@ class SubmenuActionItem extends MenuActionItem { this.submenuIndicator.setAttribute('aria-hidden', 'true'); this._register(addDisposableListener(this.element, EventType.KEY_UP, e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { EventHelper.stop(e, true); @@ -489,7 +626,7 @@ class SubmenuActionItem extends MenuActionItem { })); this._register(addDisposableListener(this.element, EventType.KEY_DOWN, e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Enter)) { EventHelper.stop(e, true); } @@ -508,10 +645,20 @@ class SubmenuActionItem extends MenuActionItem { })); this._register(addDisposableListener(this.element, EventType.FOCUS_OUT, e => { - if (!isAncestor(document.activeElement, this.element)) { + if (this.element && !isAncestor(document.activeElement, this.element)) { this.hideScheduler.schedule(); } })); + + this._register(this.parentData.parent.onScroll(() => { + this.parentData.parent.focus(false); + this.cleanupExistingSubmenu(false); + })); + } + + open(selectFirst?: boolean): void { + this.cleanupExistingSubmenu(false); + this.createSubmenu(selectFirst); } onClick(e: EventLike): void { @@ -525,54 +672,77 @@ class SubmenuActionItem extends MenuActionItem { private cleanupExistingSubmenu(force: boolean): void { if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) { this.parentData.submenu.dispose(); - this.parentData.submenu = null; + this.parentData.submenu = undefined; if (this.submenuContainer) { this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; + this.submenuContainer = undefined; } } } private createSubmenu(selectFirstItem = true): void { + if (!this.element) { + return; + } + if (!this.parentData.submenu) { this.submenuContainer = append(this.element, $('div.monaco-submenu')); addClasses(this.submenuContainer, 'menubar-menu-items-holder', 'context-view'); - this.submenuContainer.style.left = `${getClientArea(this.element).width}px`; - - this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); - if (event.equals(KeyCode.LeftArrow)) { - EventHelper.stop(e, true); - - this.parentData.parent.focus(); - this.parentData.submenu.dispose(); - this.parentData.submenu = null; - - this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; - } - })); - - this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); - if (event.equals(KeyCode.LeftArrow)) { - EventHelper.stop(e, true); - } - })); this.parentData.submenu = new Menu(this.submenuContainer, this.submenuActions, this.submenuOptions); if (this.menuStyle) { this.parentData.submenu.style(this.menuStyle); } + const boundingRect = this.element.getBoundingClientRect(); + const childBoundingRect = this.submenuContainer.getBoundingClientRect(); + const computedStyles = getComputedStyle(this.parentData.parent.domNode); + const paddingTop = parseFloat(computedStyles.paddingTop || '0') || 0; + + if (window.innerWidth <= boundingRect.right + childBoundingRect.width) { + this.submenuContainer.style.left = '10px'; + this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset + boundingRect.height}px`; + } else { + this.submenuContainer.style.left = `${this.element.offsetWidth}px`; + this.submenuContainer.style.top = `${this.element.offsetTop - this.parentData.parent.scrollOffset - paddingTop}px`; + } + + this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_UP, e => { + let event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.LeftArrow)) { + EventHelper.stop(e, true); + + this.parentData.parent.focus(); + + if (this.parentData.submenu) { + this.parentData.submenu.dispose(); + this.parentData.submenu = undefined; + } + + this.submenuDisposables = dispose(this.submenuDisposables); + this.submenuContainer = undefined; + } + })); + + this.submenuDisposables.push(addDisposableListener(this.submenuContainer, EventType.KEY_DOWN, e => { + let event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.LeftArrow)) { + EventHelper.stop(e, true); + } + })); + + this.submenuDisposables.push(this.parentData.submenu.onDidCancel(() => { this.parentData.parent.focus(); - this.parentData.submenu.dispose(); - this.parentData.submenu = null; + + if (this.parentData.submenu) { + this.parentData.submenu.dispose(); + this.parentData.submenu = undefined; + } this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; + this.submenuContainer = undefined; })); this.parentData.submenu.focus(selectFirstItem); @@ -585,7 +755,12 @@ class SubmenuActionItem extends MenuActionItem { protected applyStyle(): void { super.applyStyle(); - const isSelected = hasClass(this.element, 'focused'); + + if (!this.menuStyle) { + return; + } + + const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : null; @@ -607,12 +782,12 @@ class SubmenuActionItem extends MenuActionItem { if (this.submenuContainer) { this.submenuDisposables = dispose(this.submenuDisposables); - this.submenuContainer = null; + this.submenuContainer = undefined; } } } -class MenuSeparatorActionItem extends ActionItem { +class MenuSeparatorActionViewItem extends ActionViewItem { style(style: IMenuStyles): void { this.label.style.borderBottomColor = style.separatorColor ? `${style.separatorColor}` : null; } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts new file mode 100644 index 00000000000..631b81f7075 --- /dev/null +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -0,0 +1,997 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as browser from 'vs/base/browser/browser'; +import * as DOM from 'vs/base/browser/dom'; +import * as strings from 'vs/base/common/strings'; +import * as nls from 'vs/nls'; +import { domEvent } from 'vs/base/browser/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; +import { cleanMnemonic, IMenuOptions, Menu, MENU_ESCAPED_MNEMONIC_REGEX, MENU_MNEMONIC_REGEX, SubmenuAction, IMenuStyles } from 'vs/base/browser/ui/menu/menu'; +import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Event, Emitter } from 'vs/base/common/event'; +import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { withNullAsUndefined } from 'vs/base/common/types'; +import { asArray } from 'vs/base/common/arrays'; + +const $ = DOM.$; + +export interface IMenuBarOptions { + enableMnemonics?: boolean; + visibility?: string; + getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined; + alwaysOnMnemonics?: boolean; +} + +export interface MenuBarMenu { + actions: IAction[]; + label: string; +} + +enum MenubarState { + HIDDEN, + VISIBLE, + FOCUSED, + OPEN +} + +export class MenuBar extends Disposable { + + static readonly OVERFLOW_INDEX: number = -1; + + private menuCache: { + buttonElement: HTMLElement; + titleElement: HTMLElement; + label: string; + actions?: IAction[]; + }[]; + + private overflowMenu: { + buttonElement: HTMLElement; + titleElement: HTMLElement; + label: string; + actions?: IAction[]; + }; + + private focusedMenu: { + index: number; + holder?: HTMLElement; + widget?: Menu; + } | undefined; + + private focusToReturn: HTMLElement | undefined; + private menuUpdater: RunOnceScheduler; + + // Input-related + private _mnemonicsInUse: boolean; + private openedViaKeyboard: boolean; + private awaitingAltRelease: boolean; + private ignoreNextMouseUp: boolean; + private mnemonics: Map; + + private updatePending: boolean; + private _focusState: MenubarState; + private actionRunner: IActionRunner; + + private readonly _onVisibilityChange: Emitter; + private readonly _onFocusStateChange: Emitter; + + private numMenusShown: number; + private menuStyle: IMenuStyles; + private overflowLayoutScheduled: IDisposable | null; + + constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) { + super(); + + this.container.setAttribute('role', 'menubar'); + + this.menuCache = []; + this.mnemonics = new Map(); + + this._focusState = MenubarState.VISIBLE; + + this._onVisibilityChange = this._register(new Emitter()); + this._onFocusStateChange = this._register(new Emitter()); + + this.createOverflowMenu(); + + this.menuUpdater = this._register(new RunOnceScheduler(() => this.update(), 200)); + + this.actionRunner = this._register(new ActionRunner()); + this._register(this.actionRunner.onDidBeforeRun(() => { + this.setUnfocusedState(); + })); + + this._register(ModifierKeyEmitter.getInstance().event(this.onModifierKeyToggled, this)); + + 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 ? e.key.toLocaleLowerCase() : ''; + + if (event.equals(KeyCode.LeftArrow)) { + this.focusPrevious(); + } else if (event.equals(KeyCode.RightArrow)) { + this.focusNext(); + } else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) { + this.setUnfocusedState(); + } else if (!this.isOpen && !event.ctrlKey && this.options.enableMnemonics && this.mnemonicsInUse && this.mnemonics.has(key)) { + const menuIndex = this.mnemonics.get(key)!; + this.onMenuTriggered(menuIndex, false); + } else { + eventHandled = false; + } + + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + })); + + this._register(DOM.addDisposableListener(window, DOM.EventType.MOUSE_DOWN, () => { + // This mouse event 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; + + // We are losing focus and there is no related target, e.g. webview case + if (!event.relatedTarget) { + this.setUnfocusedState(); + } + // We are losing focus and there is a target, reset focusToReturn value as not to redirect + else if (event.relatedTarget && !this.container.contains(event.relatedTarget as HTMLElement)) { + this.focusToReturn = undefined; + this.setUnfocusedState(); + } + })); + + this._register(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + if (!this.options.enableMnemonics || !e.altKey || e.ctrlKey || e.defaultPrevented) { + return; + } + + const key = e.key.toLocaleLowerCase(); + if (!this.mnemonics.has(key)) { + return; + } + + this.mnemonicsInUse = true; + this.updateMnemonicVisibility(true); + + const menuIndex = this.mnemonics.get(key)!; + this.onMenuTriggered(menuIndex, false); + })); + + this.setUnfocusedState(); + } + + push(arg: MenuBarMenu | MenuBarMenu[]): void { + const menus: MenuBarMenu[] = asArray(arg); + + menus.forEach((menuBarMenu) => { + const menuIndex = this.menuCache.length; + const cleanMenuLabel = cleanMnemonic(menuBarMenu.label); + + const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': cleanMenuLabel, 'aria-haspopup': true }); + const titleElement = $('div.menubar-menu-title', { 'role': 'none', 'aria-hidden': true }); + + buttonElement.appendChild(titleElement); + this.container.insertBefore(buttonElement, this.overflowMenu.buttonElement); + + let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(menuBarMenu.label); + + // Register mnemonics + if (mnemonicMatches) { + let mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[2]; + + this.registerMnemonic(this.menuCache.length, mnemonic); + } + + this.updateLabels(titleElement, buttonElement, menuBarMenu.label); + + this._register(DOM.addDisposableListener(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; + } + + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + })); + + Gesture.addTarget(buttonElement); + this._register(DOM.addDisposableListener(buttonElement, EventType.Tap, (e: GestureEvent) => { + // Ignore this touch if the menu is touched + if (this.isOpen && this.focusedMenu && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) { + return; + } + + this.ignoreNextMouseUp = false; + this.onMenuTriggered(menuIndex, true); + + e.preventDefault(); + e.stopPropagation(); + })); + + this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_DOWN, (e) => { + if (!this.isOpen) { + // Open the menu with mouse down and ignore the following mouse up event + this.ignoreNextMouseUp = true; + this.onMenuTriggered(menuIndex, true); + } else { + this.ignoreNextMouseUp = false; + } + + e.preventDefault(); + e.stopPropagation(); + })); + + this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_UP, (e) => { + if (!this.ignoreNextMouseUp) { + if (this.isFocused) { + this.onMenuTriggered(menuIndex, true); + } + } else { + this.ignoreNextMouseUp = false; + } + })); + + this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_ENTER, () => { + if (this.isOpen && !this.isCurrentMenu(menuIndex)) { + this.menuCache[menuIndex].buttonElement.focus(); + this.cleanupCustomMenu(); + this.showCustomMenu(menuIndex, false); + } else if (this.isFocused && !this.isOpen) { + this.focusedMenu = { index: menuIndex }; + buttonElement.focus(); + } + })); + + this.menuCache.push({ + label: menuBarMenu.label, + actions: menuBarMenu.actions, + buttonElement: buttonElement, + titleElement: titleElement + }); + }); + } + + createOverflowMenu(): void { + const label = nls.localize('mMore', "..."); + const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'aria-haspopup': true }); + const titleElement = $('div.menubar-menu-title.toolbar-toggle-more', { 'role': 'none', 'aria-hidden': true }); + + buttonElement.appendChild(titleElement); + this.container.appendChild(buttonElement); + buttonElement.style.visibility = 'hidden'; + + this._register(DOM.addDisposableListener(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: MenuBar.OVERFLOW_INDEX }; + this.openedViaKeyboard = true; + this.focusState = MenubarState.OPEN; + } else { + eventHandled = false; + } + + if (eventHandled) { + event.preventDefault(); + event.stopPropagation(); + } + })); + + Gesture.addTarget(buttonElement); + this._register(DOM.addDisposableListener(buttonElement, EventType.Tap, (e: GestureEvent) => { + // Ignore this touch if the menu is touched + if (this.isOpen && this.focusedMenu && this.focusedMenu.holder && DOM.isAncestor(e.initialTarget as HTMLElement, this.focusedMenu.holder)) { + return; + } + + this.ignoreNextMouseUp = false; + this.onMenuTriggered(MenuBar.OVERFLOW_INDEX, true); + + e.preventDefault(); + e.stopPropagation(); + })); + + this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_DOWN, (e) => { + if (!this.isOpen) { + // Open the menu with mouse down and ignore the following mouse up event + this.ignoreNextMouseUp = true; + this.onMenuTriggered(MenuBar.OVERFLOW_INDEX, true); + } else { + this.ignoreNextMouseUp = false; + } + + e.preventDefault(); + e.stopPropagation(); + })); + + this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_UP, (e) => { + if (!this.ignoreNextMouseUp) { + if (this.isFocused) { + this.onMenuTriggered(MenuBar.OVERFLOW_INDEX, true); + } + } else { + this.ignoreNextMouseUp = false; + } + })); + + this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_ENTER, () => { + if (this.isOpen && !this.isCurrentMenu(MenuBar.OVERFLOW_INDEX)) { + this.overflowMenu.buttonElement.focus(); + this.cleanupCustomMenu(); + this.showCustomMenu(MenuBar.OVERFLOW_INDEX, false); + } else if (this.isFocused && !this.isOpen) { + this.focusedMenu = { index: MenuBar.OVERFLOW_INDEX }; + buttonElement.focus(); + } + })); + + this.overflowMenu = { + buttonElement: buttonElement, + titleElement: titleElement, + label: 'More' + }; + } + + updateMenu(menu: MenuBarMenu): void { + const menuToUpdate = this.menuCache.filter(menuBarMenu => menuBarMenu.label === menu.label); + if (menuToUpdate && menuToUpdate.length) { + menuToUpdate[0].actions = menu.actions; + } + } + + dispose(): void { + super.dispose(); + + this.menuCache.forEach(menuBarMenu => { + DOM.removeNode(menuBarMenu.titleElement); + DOM.removeNode(menuBarMenu.buttonElement); + }); + + DOM.removeNode(this.overflowMenu.titleElement); + DOM.removeNode(this.overflowMenu.buttonElement); + + if (this.overflowLayoutScheduled) { + this.overflowLayoutScheduled = dispose(this.overflowLayoutScheduled); + } + } + + blur(): void { + this.setUnfocusedState(); + } + + getWidth(): number { + if (this.menuCache) { + const left = this.menuCache[0].buttonElement.getBoundingClientRect().left; + const right = this.hasOverflow ? this.overflowMenu.buttonElement.getBoundingClientRect().right : this.menuCache[this.menuCache.length - 1].buttonElement.getBoundingClientRect().right; + return right - left; + } + + return 0; + } + + getHeight(): number { + return this.container.clientHeight; + } + + private updateOverflowAction(): void { + if (!this.menuCache || !this.menuCache.length) { + return; + } + + const sizeAvailable = this.container.offsetWidth; + let currentSize = 0; + let full = false; + const prevNumMenusShown = this.numMenusShown; + this.numMenusShown = 0; + for (let menuBarMenu of this.menuCache) { + if (!full) { + const size = menuBarMenu.buttonElement.offsetWidth; + if (currentSize + size > sizeAvailable) { + full = true; + } else { + currentSize += size; + this.numMenusShown++; + if (this.numMenusShown > prevNumMenusShown) { + menuBarMenu.buttonElement.style.visibility = 'visible'; + } + } + } + + if (full) { + menuBarMenu.buttonElement.style.visibility = 'hidden'; + } + } + + // Overflow + if (full) { + // Can't fit the more button, need to remove more menus + while (currentSize + this.overflowMenu.buttonElement.offsetWidth > sizeAvailable && this.numMenusShown > 0) { + this.numMenusShown--; + const size = this.menuCache[this.numMenusShown].buttonElement.offsetWidth; + this.menuCache[this.numMenusShown].buttonElement.style.visibility = 'hidden'; + currentSize -= size; + } + + this.overflowMenu.actions = []; + for (let idx = this.numMenusShown; idx < this.menuCache.length; idx++) { + this.overflowMenu.actions.push(new SubmenuAction(this.menuCache[idx].label, this.menuCache[idx].actions || [])); + } + + DOM.removeNode(this.overflowMenu.buttonElement); + this.container.insertBefore(this.overflowMenu.buttonElement, this.menuCache[this.numMenusShown].buttonElement); + this.overflowMenu.buttonElement.style.visibility = 'visible'; + } else { + DOM.removeNode(this.overflowMenu.buttonElement); + this.container.appendChild(this.overflowMenu.buttonElement); + this.overflowMenu.buttonElement.style.visibility = 'hidden'; + } + } + + private updateLabels(titleElement: HTMLElement, buttonElement: HTMLElement, label: string): void { + const cleanMenuLabel = cleanMnemonic(label); + + // Update the button label to reflect mnemonics + titleElement.innerHTML = this.options.enableMnemonics ? + strings.escape(label).replace(MENU_ESCAPED_MNEMONIC_REGEX, '').replace(/&&/g, '&') : + cleanMenuLabel.replace(/&&/g, '&'); + + let mnemonicMatches = MENU_MNEMONIC_REGEX.exec(label); + + // Register mnemonics + if (mnemonicMatches) { + let mnemonic = !!mnemonicMatches[1] ? mnemonicMatches[1] : mnemonicMatches[2]; + + if (this.options.enableMnemonics) { + buttonElement.setAttribute('aria-keyshortcuts', 'Alt+' + mnemonic.toLocaleLowerCase()); + } else { + buttonElement.removeAttribute('aria-keyshortcuts'); + } + } + } + + style(style: IMenuStyles): void { + this.menuStyle = style; + } + + update(options?: IMenuBarOptions): void { + if (options) { + this.options = options; + } + + // Don't update while using the menu + if (this.isFocused) { + this.updatePending = true; + return; + } + + this.menuCache.forEach(menuBarMenu => { + this.updateLabels(menuBarMenu.titleElement, menuBarMenu.buttonElement, menuBarMenu.label); + }); + + if (!this.overflowLayoutScheduled) { + this.overflowLayoutScheduled = DOM.scheduleAtNextAnimationFrame(() => { + this.updateOverflowAction(); + this.overflowLayoutScheduled = null; + }); + } + + this.setUnfocusedState(); + } + + private registerMnemonic(menuIndex: number, mnemonic: string): void { + this.mnemonics.set(mnemonic.toLocaleLowerCase(), menuIndex); + } + + private hideMenubar(): void { + if (this.container.style.display !== 'none') { + this.container.style.display = 'none'; + this._onVisibilityChange.fire(false); + } + } + + private showMenubar(): void { + if (this.container.style.display !== 'flex') { + this.container.style.display = 'flex'; + this._onVisibilityChange.fire(true); + + this.updateOverflowAction(); + } + } + + private get focusState(): MenubarState { + return this._focusState; + } + + private set focusState(value: MenubarState) { + if (this._focusState >= MenubarState.FOCUSED && value < MenubarState.FOCUSED) { + // Losing focus, update the menu if needed + + if (this.updatePending) { + this.menuUpdater.schedule(); + this.updatePending = false; + } + } + + if (value === this._focusState) { + return; + } + + const isVisible = this.isVisible; + const isOpen = this.isOpen; + const isFocused = this.isFocused; + + this._focusState = value; + + switch (value) { + case MenubarState.HIDDEN: + if (isVisible) { + this.hideMenubar(); + } + + if (isOpen) { + this.cleanupCustomMenu(); + } + + if (isFocused) { + this.focusedMenu = undefined; + + if (this.focusToReturn) { + this.focusToReturn.focus(); + this.focusToReturn = undefined; + } + } + + + break; + case MenubarState.VISIBLE: + if (!isVisible) { + this.showMenubar(); + } + + if (isOpen) { + this.cleanupCustomMenu(); + } + + if (isFocused) { + if (this.focusedMenu) { + if (this.focusedMenu.index === MenuBar.OVERFLOW_INDEX) { + this.overflowMenu.buttonElement.blur(); + } else { + this.menuCache[this.focusedMenu.index].buttonElement.blur(); + } + } + + this.focusedMenu = undefined; + + if (this.focusToReturn) { + this.focusToReturn.focus(); + this.focusToReturn = undefined; + } + } + + break; + case MenubarState.FOCUSED: + if (!isVisible) { + this.showMenubar(); + } + + if (isOpen) { + this.cleanupCustomMenu(); + } + + if (this.focusedMenu) { + if (this.focusedMenu.index === MenuBar.OVERFLOW_INDEX) { + this.overflowMenu.buttonElement.focus(); + } else { + this.menuCache[this.focusedMenu.index].buttonElement.focus(); + } + } + break; + case MenubarState.OPEN: + if (!isVisible) { + this.showMenubar(); + } + + if (this.focusedMenu) { + this.showCustomMenu(this.focusedMenu.index, this.openedViaKeyboard); + } + break; + } + + this._focusState = value; + this._onFocusStateChange.fire(this.focusState >= MenubarState.FOCUSED); + } + + private get isVisible(): boolean { + return this.focusState >= MenubarState.VISIBLE; + } + + private get isFocused(): boolean { + return this.focusState >= MenubarState.FOCUSED; + } + + private get isOpen(): boolean { + return this.focusState >= MenubarState.OPEN; + } + + private get hasOverflow(): boolean { + return this.numMenusShown < this.menuCache.length; + } + + private setUnfocusedState(): void { + if (this.options.visibility === 'toggle' || this.options.visibility === 'hidden') { + this.focusState = MenubarState.HIDDEN; + } else if (this.options.visibility === 'default' && browser.isFullscreen()) { + this.focusState = MenubarState.HIDDEN; + } else { + this.focusState = MenubarState.VISIBLE; + } + + this.ignoreNextMouseUp = false; + this.mnemonicsInUse = false; + this.updateMnemonicVisibility(false); + } + + private focusPrevious(): void { + + if (!this.focusedMenu) { + return; + } + + + let newFocusedIndex = (this.focusedMenu.index - 1 + this.numMenusShown) % this.numMenusShown; + if (this.focusedMenu.index === MenuBar.OVERFLOW_INDEX) { + newFocusedIndex = this.numMenusShown - 1; + } else if (this.focusedMenu.index === 0 && this.hasOverflow) { + newFocusedIndex = MenuBar.OVERFLOW_INDEX; + } + + if (newFocusedIndex === this.focusedMenu.index) { + return; + } + + if (this.isOpen) { + this.cleanupCustomMenu(); + this.showCustomMenu(newFocusedIndex); + } else if (this.isFocused) { + this.focusedMenu.index = newFocusedIndex; + if (newFocusedIndex === MenuBar.OVERFLOW_INDEX) { + this.overflowMenu.buttonElement.focus(); + } else { + this.menuCache[newFocusedIndex].buttonElement.focus(); + } + } + } + + private focusNext(): void { + if (!this.focusedMenu) { + return; + } + + let newFocusedIndex = (this.focusedMenu.index + 1) % this.numMenusShown; + if (this.focusedMenu.index === MenuBar.OVERFLOW_INDEX) { + newFocusedIndex = 0; + } else if (this.focusedMenu.index === this.numMenusShown - 1) { + newFocusedIndex = MenuBar.OVERFLOW_INDEX; + } + + if (newFocusedIndex === this.focusedMenu.index) { + return; + } + + if (this.isOpen) { + this.cleanupCustomMenu(); + this.showCustomMenu(newFocusedIndex); + } else if (this.isFocused) { + this.focusedMenu.index = newFocusedIndex; + if (newFocusedIndex === MenuBar.OVERFLOW_INDEX) { + this.overflowMenu.buttonElement.focus(); + } else { + this.menuCache[newFocusedIndex].buttonElement.focus(); + } + } + } + + private updateMnemonicVisibility(visible: boolean): void { + if (this.menuCache) { + this.menuCache.forEach(menuBarMenu => { + if (menuBarMenu.titleElement.children.length) { + let child = menuBarMenu.titleElement.children.item(0) as HTMLElement; + if (child) { + child.style.textDecoration = (this.options.alwaysOnMnemonics || visible) ? 'underline' : null; + } + } + }); + } + } + + private get mnemonicsInUse(): boolean { + return this._mnemonicsInUse; + } + + private set mnemonicsInUse(value: boolean) { + this._mnemonicsInUse = value; + } + + public get onVisibilityChange(): Event { + return this._onVisibilityChange.event; + } + + public get onFocusStateChange(): Event { + return this._onFocusStateChange.event; + } + + private onMenuTriggered(menuIndex: number, clicked: boolean) { + if (this.isOpen) { + if (this.isCurrentMenu(menuIndex)) { + this.setUnfocusedState(); + } else { + this.cleanupCustomMenu(); + this.showCustomMenu(menuIndex, this.openedViaKeyboard); + } + } else { + this.focusedMenu = { index: menuIndex }; + this.openedViaKeyboard = !clicked; + this.focusState = MenubarState.OPEN; + } + } + + private onModifierKeyToggled(modifierKeyStatus: IModifierKeyStatus): void { + const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey; + + if (this.options.visibility === '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.awaitingAltRelease) { + if (!this.isFocused) { + this.mnemonicsInUse = true; + this.focusedMenu = { index: this.numMenusShown > 0 ? 0 : MenuBar.OVERFLOW_INDEX }; + this.focusState = MenubarState.FOCUSED; + } else if (!this.isOpen) { + this.setUnfocusedState(); + } + } + } + + // Alt key released + if (!modifierKeyStatus.altKey && modifierKeyStatus.lastKeyReleased === 'alt') { + this.awaitingAltRelease = false; + } + + if (this.options.enableMnemonics && this.menuCache && !this.isOpen) { + this.updateMnemonicVisibility((!this.awaitingAltRelease && modifierKeyStatus.altKey) || this.mnemonicsInUse); + } + } + + private isCurrentMenu(menuIndex: number): boolean { + if (!this.focusedMenu) { + return false; + } + + return this.focusedMenu.index === menuIndex; + } + + private cleanupCustomMenu(): void { + if (this.focusedMenu) { + // Remove focus from the menus first + if (this.focusedMenu.index === MenuBar.OVERFLOW_INDEX) { + this.overflowMenu.buttonElement.focus(); + } else { + this.menuCache[this.focusedMenu.index].buttonElement.focus(); + } + + if (this.focusedMenu.holder) { + if (this.focusedMenu.holder.parentElement) { + DOM.removeClass(this.focusedMenu.holder.parentElement, 'open'); + } + + this.focusedMenu.holder.remove(); + } + + if (this.focusedMenu.widget) { + this.focusedMenu.widget.dispose(); + } + + this.focusedMenu = { index: this.focusedMenu.index }; + } + } + + private showCustomMenu(menuIndex: number, selectFirst = true): void { + const actualMenuIndex = menuIndex >= this.numMenusShown ? MenuBar.OVERFLOW_INDEX : menuIndex; + const customMenu = actualMenuIndex === MenuBar.OVERFLOW_INDEX ? this.overflowMenu : this.menuCache[actualMenuIndex]; + + if (!customMenu.actions) { + return; + } + + const menuHolder = $('div.menubar-menu-items-holder'); + + DOM.addClass(customMenu.buttonElement, 'open'); + menuHolder.style.top = `${this.container.clientHeight}px`; + menuHolder.style.left = `${customMenu.buttonElement.getBoundingClientRect().left}px`; + + customMenu.buttonElement.appendChild(menuHolder); + + let menuOptions: IMenuOptions = { + getKeyBinding: this.options.getKeybinding, + actionRunner: this.actionRunner, + enableMnemonics: this.options.alwaysOnMnemonics || (this.mnemonicsInUse && this.options.enableMnemonics), + ariaLabel: withNullAsUndefined(customMenu.buttonElement.getAttribute('aria-label')) + }; + + let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions)); + menuWidget.style(this.menuStyle); + + this._register(menuWidget.onDidCancel(() => { + this.focusState = MenubarState.FOCUSED; + })); + + if (actualMenuIndex !== menuIndex) { + menuWidget.trigger(menuIndex - this.numMenusShown); + } else { + menuWidget.focus(selectFirst); + } + + this.focusedMenu = { + index: actualMenuIndex, + holder: menuHolder, + widget: menuWidget + }; + } +} + +type ModifierKey = 'alt' | 'ctrl' | 'shift'; + +interface IModifierKeyStatus { + altKey: boolean; + shiftKey: boolean; + ctrlKey: boolean; + lastKeyPressed?: ModifierKey; + lastKeyReleased?: ModifierKey; +} + + +class ModifierKeyEmitter extends Emitter { + + private _subscriptions: IDisposable[] = []; + private _keyStatus: IModifierKeyStatus; + private static instance: ModifierKeyEmitter; + + private constructor() { + super(); + + this._keyStatus = { + altKey: false, + shiftKey: false, + ctrlKey: false + }; + + this._subscriptions.push(domEvent(document.body, 'keydown', true)(e => { + const event = new StandardKeyboardEvent(e); + + if (e.altKey && !this._keyStatus.altKey) { + this._keyStatus.lastKeyPressed = 'alt'; + } else if (e.ctrlKey && !this._keyStatus.ctrlKey) { + this._keyStatus.lastKeyPressed = 'ctrl'; + } else if (e.shiftKey && !this._keyStatus.shiftKey) { + this._keyStatus.lastKeyPressed = 'shift'; + } else if (event.keyCode !== KeyCode.Alt) { + this._keyStatus.lastKeyPressed = undefined; + } else { + return; + } + + this._keyStatus.altKey = e.altKey; + this._keyStatus.ctrlKey = e.ctrlKey; + this._keyStatus.shiftKey = e.shiftKey; + + if (this._keyStatus.lastKeyPressed) { + this.fire(this._keyStatus); + } + })); + + this._subscriptions.push(domEvent(document.body, 'keyup', true)(e => { + if (!e.altKey && this._keyStatus.altKey) { + this._keyStatus.lastKeyReleased = 'alt'; + } else if (!e.ctrlKey && this._keyStatus.ctrlKey) { + this._keyStatus.lastKeyReleased = 'ctrl'; + } else if (!e.shiftKey && this._keyStatus.shiftKey) { + this._keyStatus.lastKeyReleased = 'shift'; + } else { + this._keyStatus.lastKeyReleased = undefined; + } + + if (this._keyStatus.lastKeyPressed !== this._keyStatus.lastKeyReleased) { + this._keyStatus.lastKeyPressed = undefined; + } + + this._keyStatus.altKey = e.altKey; + this._keyStatus.ctrlKey = e.ctrlKey; + this._keyStatus.shiftKey = e.shiftKey; + + if (this._keyStatus.lastKeyReleased) { + this.fire(this._keyStatus); + } + })); + + this._subscriptions.push(domEvent(document.body, 'mousedown', true)(e => { + this._keyStatus.lastKeyPressed = undefined; + })); + + this._subscriptions.push(domEvent(document.body, 'mouseup', true)(e => { + this._keyStatus.lastKeyPressed = undefined; + })); + + this._subscriptions.push(domEvent(document.body, 'mousemove', true)(e => { + if (e.buttons) { + this._keyStatus.lastKeyPressed = undefined; + } + })); + + this._subscriptions.push(domEvent(window, 'blur')(e => { + this._keyStatus.lastKeyPressed = undefined; + this._keyStatus.lastKeyReleased = undefined; + this._keyStatus.altKey = false; + this._keyStatus.shiftKey = false; + this._keyStatus.shiftKey = false; + + this.fire(this._keyStatus); + })); + } + + static getInstance() { + if (!ModifierKeyEmitter.instance) { + ModifierKeyEmitter.instance = new ModifierKeyEmitter(); + } + + return ModifierKeyEmitter.instance; + } + + dispose() { + super.dispose(); + this._subscriptions = dispose(this._subscriptions); + } +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/README.md b/src/vs/base/browser/ui/octiconLabel/octicons/README.md deleted file mode 100644 index 10070733505..00000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/README.md +++ /dev/null @@ -1 +0,0 @@ -If you intend to install Octicons locally, install `octicons-local.ttf`. It should appear as “github-octicons” in your font list. It is specially designed not to conflict with GitHub's web fonts. diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json b/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json deleted file mode 100644 index 3d13f99e346..00000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "registrations": [ - { - "component": { - "type": "other", - "other": { - "name": "octicons-code", - "version": "3.1.0", - "downloadUrl": "https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz" - } - }, - "licenseDetail": [ - "The MIT License (MIT)", - "", - "(c) 2012-2015 GitHub", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." - ], - "license": "MIT", - "version": "3.1.0" - }, - { - "component": { - "type": "other", - "other": { - "name": "octicons-font", - "version": "3.1.0", - "downloadUrl": "https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz" - } - }, - "licenseDetail": [ - "(c) 2012-2015 GitHub", - "", - "SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007", - "", - "PREAMBLE", - "The goals of the Open Font License (OFL) are to stimulate worldwide", - "development of collaborative font projects, to support the font creation", - "efforts of academic and linguistic communities, and to provide a free and", - "open framework in which fonts may be shared and improved in partnership", - "with others.", - "", - "The OFL allows the licensed fonts to be used, studied, modified and", - "redistributed freely as long as they are not sold by themselves. The", - "fonts, including any derivative works, can be bundled, embedded,", - "redistributed and/or sold with any software provided that any reserved", - "names are not used by derivative works. The fonts and derivatives,", - "however, cannot be released under any other type of license. The", - "requirement for fonts to remain under this license does not apply", - "to any document created using the fonts or their derivatives.", - "", - "DEFINITIONS", - "\"Font Software\" refers to the set of files released by the Copyright", - "Holder(s) under this license and clearly marked as such. This may", - "include source files, build scripts and documentation.", - "", - "\"Reserved Font Name\" refers to any names specified as such after the", - "copyright statement(s).", - "", - "\"Original Version\" refers to the collection of Font Software components as", - "distributed by the Copyright Holder(s).", - "", - "\"Modified Version\" refers to any derivative made by adding to, deleting,", - "or substituting -- in part or in whole -- any of the components of the", - "Original Version, by changing formats or by porting the Font Software to a", - "new environment.", - "", - "\"Author\" refers to any designer, engineer, programmer, technical", - "writer or other person who contributed to the Font Software.", - "", - "PERMISSION & CONDITIONS", - "Permission is hereby granted, free of charge, to any person obtaining", - "a copy of the Font Software, to use, study, copy, merge, embed, modify,", - "redistribute, and sell modified and unmodified copies of the Font", - "Software, subject to the following conditions:", - "", - "1) Neither the Font Software nor any of its individual components,", - "in Original or Modified Versions, may be sold by itself.", - "", - "2) Original or Modified Versions of the Font Software may be bundled,", - "redistributed and/or sold with any software, provided that each copy", - "contains the above copyright notice and this license. These can be", - "included either as stand-alone text files, human-readable headers or", - "in the appropriate machine-readable metadata fields within text or", - "binary files as long as those fields can be easily viewed by the user.", - "", - "3) No Modified Version of the Font Software may use the Reserved Font", - "Name(s) unless explicit written permission is granted by the corresponding", - "Copyright Holder. This restriction only applies to the primary font name as", - "presented to the users.", - "", - "4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font", - "Software shall not be used to promote, endorse or advertise any", - "Modified Version, except to acknowledge the contribution(s) of the", - "Copyright Holder(s) and the Author(s) or with their explicit written", - "permission.", - "", - "5) The Font Software, modified or unmodified, in part or in whole,", - "must be distributed entirely under this license, and must not be", - "distributed under any other license. The requirement for fonts to", - "remain under this license does not apply to any document created", - "using the Font Software.", - "", - "TERMINATION", - "This license becomes null and void if any of the above conditions are", - "not met.", - "", - "DISCLAIMER", - "THE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF", - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT", - "OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE", - "COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,", - "INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL", - "DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", - "FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM", - "OTHER DEALINGS IN THE FONT SOFTWARE." - ], - "license": "SIL OFL 1.1", - "version": "3.1.0" - } - ], - "version": 1 -} diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf deleted file mode 100644 index 2050d5e8abf..00000000000 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-local.ttf and /dev/null differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css index 5a2a927f9a8..d583733048e 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css @@ -1,233 +1,244 @@ -/*! ***************************************************************************** -(c) 2012-2015 GitHub - -When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) - -Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) -Applies to all font files - -Code License: MIT (http://choosealicense.com/licenses/mit/) -Applies to all other files -***************************************************************************** */ - @font-face { - font-family: 'octicons'; - src: url('octicons.eot?#iefix') format('embedded-opentype'), - url('octicons.woff') format('woff'), - url('octicons.ttf') format('truetype'), - url('octicons.svg#octicons') format('svg'); - font-weight: normal; - font-style: normal; + font-family: "octicons"; + src: url("./octicons.ttf?91284a5a76ea88faeb754359b7f7cd03") format("truetype"), +url("./octicons.svg?91284a5a76ea88faeb754359b7f7cd03#octicons") format("svg"); } -/* - -.octicon is optimized for 16px. -.mega-octicon is optimized for 32px but can be used larger. - -*/ .octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .mega-octicon { font-size: 32px; } -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ + + +.octicon-alert:before { content: "\f02d" } +.octicon-arrow-down:before { content: "\f03f" } +.octicon-arrow-left:before { content: "\f040" } +.octicon-arrow-right:before { content: "\f03e" } +.octicon-arrow-small-down:before { content: "\f0a0" } +.octicon-arrow-small-left:before { content: "\f0a1" } +.octicon-arrow-small-right:before { content: "\f071" } +.octicon-arrow-small-up:before { content: "\f09f" } +.octicon-arrow-up:before { content: "\f03d" } +.octicon-beaker:before { content: "\f0dd" } +.octicon-bell:before { content: "\f0de" } +.octicon-bold:before { content: "\f282" } +.octicon-book:before { content: "\f007" } +.octicon-bookmark:before { content: "\f07b" } +.octicon-briefcase:before { content: "\f0d3" } +.octicon-broadcast:before { content: "\f048" } +.octicon-browser:before { content: "\f0c5" } +.octicon-bug:before { content: "\f091" } +.octicon-calendar:before { content: "\f068" } +.octicon-check:before { content: "\f03a" } +.octicon-checklist:before { content: "\f076" } +.octicon-chevron-down:before { content: "\f0a3" } +.octicon-chevron-left:before { content: "\f0a4" } +.octicon-chevron-right:before { content: "\f078" } +.octicon-chevron-up:before { content: "\f0a2" } +.octicon-circle-slash:before { content: "\f084" } +.octicon-circuit-board:before { content: "\f0d6" } +.octicon-clippy:before { content: "\f035" } +.octicon-clock:before { content: "\f046" } +.octicon-clone:before { content: "\f0dc" } +.octicon-cloud-download:before { content: "\f00b" } +.octicon-cloud-upload:before { content: "\f00c" } +.octicon-code:before { content: "\f05f" } +.octicon-color-mode:before { content: "\f065" } +.octicon-comment-add:before { content: "\f02b" } +.octicon-comment-discussion:before { content: "\f04f" } +.octicon-comment:before { content: "\f02b" } +.octicon-credit-card:before { content: "\f045" } +.octicon-dash:before { content: "\f0ca" } +.octicon-dashboard:before { content: "\f07d" } +.octicon-database:before { content: "\f096" } +.octicon-desktop-download:before { content: "\f0dc" } +.octicon-device-camera-video:before { content: "\f057" } +.octicon-device-camera:before { content: "\f056" } +.octicon-device-desktop:before { content: "\f27c" } +.octicon-device-mobile:before { content: "\f038" } +.octicon-diff-added:before { content: "\f06b" } +.octicon-diff-ignored:before { content: "\f099" } +.octicon-diff-modified:before { content: "\f06d" } +.octicon-diff-removed:before { content: "\f06c" } +.octicon-diff-renamed:before { content: "\f06e" } +.octicon-diff:before { content: "\f04d" } +.octicon-ellipsis:before { content: "\f09a" } +.octicon-eye-unwatch:before { content: "\f04e" } +.octicon-eye-watch:before { content: "\f04e" } +.octicon-eye:before { content: "\f04e" } +.octicon-file-add:before { content: "\f05d" } +.octicon-file-binary:before { content: "\f094" } +.octicon-file-code:before { content: "\f010" } +.octicon-file-directory-create:before { content: "\f05d" } +.octicon-file-directory:before { content: "\f016" } +.octicon-file-media:before { content: "\f012" } +.octicon-file-pdf:before { content: "\f014" } +.octicon-file-submodule:before { content: "\f017" } +.octicon-file-symlink-directory:before { content: "\f0b1" } +.octicon-file-symlink-file:before { content: "\f0b0" } +.octicon-file-text:before { content: "\f283" } +.octicon-file-zip:before { content: "\f013" } +.octicon-file:before { content: "\f283" } +.octicon-flame:before { content: "\f0d2" } +.octicon-fold:before { content: "\f0cc" } +.octicon-gear:before { content: "\f02f" } +.octicon-gift:before { content: "\f042" } +.octicon-gist-fork:before { content: "\f002" } +.octicon-gist-new:before { content: "\f05d" } +.octicon-gist-private:before { content: "\f06a" } +.octicon-gist-secret:before { content: "\f08c" } +.octicon-gist:before { content: "\f00e" } +.octicon-git-branch-create:before { content: "\f020" } +.octicon-git-branch-delete:before { content: "\f020" } +.octicon-git-branch:before { content: "\f020" } +.octicon-git-commit:before { content: "\f01f" } +.octicon-git-compare:before { content: "\f0ac" } +.octicon-git-fork-private:before { content: "\f06a" } +.octicon-git-merge:before { content: "\f023" } +.octicon-git-pull-request-abandoned:before { content: "\f009" } +.octicon-git-pull-request:before { content: "\f009" } +.octicon-globe:before { content: "\f0b6" } +.octicon-grabber:before { content: "\f284" } +.octicon-graph:before { content: "\f043" } +.octicon-heart:before { content: "\2665" } +.octicon-history:before { content: "\f07e" } +.octicon-home:before { content: "\f08d" } +.octicon-horizontal-rule:before { content: "\f070" } +.octicon-hubot:before { content: "\f09d" } +.octicon-inbox:before { content: "\f0cf" } +.octicon-info:before { content: "\f059" } +.octicon-issue-closed:before { content: "\f028" } +.octicon-issue-opened:before { content: "\f026" } +.octicon-issue-reopened:before { content: "\f027" } +.octicon-italic:before { content: "\f285" } +.octicon-jersey:before { content: "\f019" } +.octicon-kebab-horizontal:before { content: "\f286" } +.octicon-kebab-vertical:before { content: "\f287" } +.octicon-key:before { content: "\f049" } +.octicon-keyboard:before { content: "\f00d" } +.octicon-law:before { content: "\f0d8" } +.octicon-light-bulb:before { content: "\f000" } +.octicon-link-external:before { content: "\f07f" } +.octicon-link:before { content: "\f05c" } +.octicon-list-ordered:before { content: "\f062" } +.octicon-list-unordered:before { content: "\f061" } +.octicon-location:before { content: "\f060" } +.octicon-lock:before { content: "\f06a" } +.octicon-log-in:before { content: "\f036" } +.octicon-log-out:before { content: "\f032" } +.octicon-logo-gist:before { content: "\f288" } +.octicon-logo-github:before { content: "\f092" } +.octicon-mail-read:before { content: "\f03c" } +.octicon-mail-reply:before { content: "\f28c" } +.octicon-mail:before { content: "\f03b" } +.octicon-mark-github:before { content: "\f00a" } +.octicon-markdown:before { content: "\f0c9" } +.octicon-megaphone:before { content: "\f077" } +.octicon-mention:before { content: "\f0be" } +.octicon-microscope:before { content: "\f0dd" } +.octicon-milestone:before { content: "\f075" } +.octicon-mirror-private:before { content: "\f06a" } +.octicon-mirror-public:before { content: "\f024" } +.octicon-mirror:before { content: "\f024" } +.octicon-mortar-board:before { content: "\f0d7" } +.octicon-mute:before { content: "\f080" } +.octicon-no-newline:before { content: "\f09c" } +.octicon-note:before { content: "\f289" } +.octicon-octoface:before { content: "\f008" } +.octicon-organization:before { content: "\f037" } +.octicon-organization-filled:before { content: "\26a2" } +.octicon-organization-outline:before { content: "\f037" } +.octicon-package:before { content: "\f0c4" } +.octicon-paintcan:before { content: "\f0d1" } +.octicon-pencil:before { content: "\f058" } +.octicon-person-add:before { content: "\f018" } +.octicon-person-follow:before { content: "\f018" } +.octicon-person:before { content: "\f018" } +.octicon-person-filled:before { content: "\26a3" } +.octicon-person-outline:before { content: "\f018" } +.octicon-pin:before { content: "\f041" } +.octicon-plug:before { content: "\f0d4" } +.octicon-plus-small:before { content: "\f28a" } +.octicon-plus:before { content: "\f05d" } +.octicon-primitive-dot:before { content: "\f052" } +.octicon-primitive-square:before { content: "\f053" } +.octicon-project:before { content: "\f28b" } +.octicon-pulse:before { content: "\f085" } +.octicon-question:before { content: "\f02c" } +.octicon-quote:before { content: "\f063" } +.octicon-radio-tower:before { content: "\f030" } +.octicon-remove-close:before { content: "\f081" } +.octicon-reply:before { content: "\f28c" } +.octicon-repo-clone:before { content: "\f04c" } +.octicon-repo-create:before { content: "\f05d" } +.octicon-repo-delete:before { content: "\f001" } +.octicon-repo-force-push:before { content: "\f04a" } +.octicon-repo-forked:before { content: "\f002" } +.octicon-repo-pull:before { content: "\f006" } +.octicon-repo-push:before { content: "\f005" } +.octicon-repo-sync:before { content: "\f087" } +.octicon-repo:before { content: "\f001" } +.octicon-report:before { content: "\f28d" } +.octicon-rocket:before { content: "\f033" } +.octicon-rss:before { content: "\f034" } +.octicon-ruby:before { content: "\f047" } +.octicon-screen-full:before { content: "\f066" } +.octicon-screen-normal:before { content: "\f067" } +.octicon-search-save:before { content: "\f02e" } +.octicon-search:before { content: "\f02e" } +.octicon-server:before { content: "\f097" } +.octicon-settings:before { content: "\f07c" } +.octicon-shield:before { content: "\f0e1" } +.octicon-sign-in:before { content: "\f036" } +.octicon-sign-out:before { content: "\f032" } +.octicon-smiley:before { content: "\f27d" } +.octicon-squirrel:before { content: "\f0b2" } +.octicon-star-add:before { content: "\f02a" } +.octicon-star-delete:before { content: "\f02a" } +.octicon-star:before { content: "\f02a" } +.octicon-stop:before { content: "\f08f" } +.octicon-sync:before { content: "\f087" } +.octicon-tag-add:before { content: "\f015" } +.octicon-tag-remove:before { content: "\f015" } +.octicon-tag:before { content: "\f015" } +.octicon-tasklist:before { content: "\f27e" } +.octicon-telescope:before { content: "\f088" } +.octicon-terminal:before { content: "\f0c8" } +.octicon-text-size:before { content: "\f27f" } +.octicon-three-bars:before { content: "\f05e" } +.octicon-thumbsdown:before { content: "\f0db" } +.octicon-thumbsup:before { content: "\f0da" } +.octicon-tools:before { content: "\f031" } +.octicon-trashcan:before { content: "\f0d0" } +.octicon-triangle-down:before { content: "\f05b" } +.octicon-triangle-left:before { content: "\f044" } +.octicon-triangle-right:before { content: "\f05a" } +.octicon-triangle-up:before { content: "\f0aa" } +.octicon-unfold:before { content: "\f039" } +.octicon-unmute:before { content: "\f0ba" } +.octicon-unverified:before { content: "\f280" } +.octicon-verified:before { content: "\f281" } +.octicon-versions:before { content: "\f064" } +.octicon-watch:before { content: "\f0e0" } +.octicon-x:before { content: "\f081" } +.octicon-zap:before { content: "\26a1" } +.octicon-archive:before { content: "\f101" } +.octicon-arrow-both:before { content: "\f102" } +.octicon-eye-closed:before { content: "\f103" } +.octicon-fold-down:before { content: "\f104" } +.octicon-fold-up:before { content: "\f105" } +.octicon-github-action:before { content: "\f106" } +.octicon-play:before { content: "\f107" } +.octicon-remote:before { content: "\f108" } +.octicon-request-changes:before { content: "\f109" } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot deleted file mode 100644 index 2bf20bca003..00000000000 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.eot and /dev/null differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.less b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.less deleted file mode 100644 index d1d751ea45e..00000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.less +++ /dev/null @@ -1,220 +0,0 @@ -@octicons-font-path: "."; -@octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d"; - -@font-face { - font-family: 'octicons'; - src: ~"url('@{octicons-font-path}/octicons.eot?#iefix&v=@{octicons-version}') format('embedded-opentype')", - ~"url('@{octicons-font-path}/octicons.woff?v=@{octicons-version}') format('woff')", - ~"url('@{octicons-font-path}/octicons.ttf?v=@{octicons-version}') format('truetype')", - ~"url('@{octicons-font-path}/octicons.svg?v=@{octicons-version}#octicons') format('svg')"; - font-weight: normal; - font-style: normal; -} - -// .octicon is optimized for 16px. -// .mega-octicon is optimized for 32px but can be used larger. -.octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mega-octicon { font-size: 32px; } - -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss deleted file mode 100644 index 0902cedce8f..00000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.scss +++ /dev/null @@ -1,220 +0,0 @@ -$octicons-font-path: "." !default; -$octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d"; - -@font-face { - font-family: 'octicons'; - src: url('#{$octicons-font-path}/octicons.eot?#iefix&v=#{$octicons-version}') format('embedded-opentype'), - url('#{$octicons-font-path}/octicons.woff?v=#{$octicons-version}') format('woff'), - url('#{$octicons-font-path}/octicons.ttf?v=#{$octicons-version}') format('truetype'), - url('#{$octicons-font-path}/octicons.svg?v=#{$octicons-version}#octicons') format('svg'); - font-weight: normal; - font-style: normal; -} - -// .octicon is optimized for 16px. -// .mega-octicon is optimized for 32px but can be used larger. -.octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mega-octicon { font-size: 32px; } - -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg index d932988bb57..af839609786 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg @@ -1,183 +1,570 @@ - - + + - -(c) 2012-2015 GitHub - -When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) - -Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) -Applies to all font files - -Code License: MIT (http://choosealicense.com/licenses/mit/) -Applies to all other files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf index 32e6720a733..8c29d338308 100644 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf and b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.ttf differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff deleted file mode 100644 index cbf9f62ea65..00000000000 Binary files a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.woff and /dev/null differ diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss b/src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss deleted file mode 100644 index cef21ae62ee..00000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/sprockets-octicons.scss +++ /dev/null @@ -1,217 +0,0 @@ -@font-face { - font-family: 'octicons'; - src: font-url('octicons.eot?#iefix') format('embedded-opentype'), - font-url('octicons.woff') format('woff'), - font-url('octicons.ttf') format('truetype'), - font-url('octicons.svg#octicons') format('svg'); - font-weight: normal; - font-style: normal; -} - -// .octicon is optimized for 16px. -// .mega-octicon is optimized for 32px but can be used larger. -.octicon, .mega-octicon { - font: normal normal normal 16px/1 octicons; - display: inline-block; - text-decoration: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mega-octicon { font-size: 32px; } - -.octicon-alert:before { content: '\f02d'} /*  */ -.octicon-arrow-down:before { content: '\f03f'} /*  */ -.octicon-arrow-left:before { content: '\f040'} /*  */ -.octicon-arrow-right:before { content: '\f03e'} /*  */ -.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ -.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ -.octicon-arrow-small-right:before { content: '\f071'} /*  */ -.octicon-arrow-small-up:before { content: '\f09f'} /*  */ -.octicon-arrow-up:before { content: '\f03d'} /*  */ -.octicon-microscope:before, -.octicon-beaker:before { content: '\f0dd'} /*  */ -.octicon-bell:before { content: '\f0de'} /*  */ -.octicon-book:before { content: '\f007'} /*  */ -.octicon-bookmark:before { content: '\f07b'} /*  */ -.octicon-briefcase:before { content: '\f0d3'} /*  */ -.octicon-broadcast:before { content: '\f048'} /*  */ -.octicon-browser:before { content: '\f0c5'} /*  */ -.octicon-bug:before { content: '\f091'} /*  */ -.octicon-calendar:before { content: '\f068'} /*  */ -.octicon-check:before { content: '\f03a'} /*  */ -.octicon-checklist:before { content: '\f076'} /*  */ -.octicon-chevron-down:before { content: '\f0a3'} /*  */ -.octicon-chevron-left:before { content: '\f0a4'} /*  */ -.octicon-chevron-right:before { content: '\f078'} /*  */ -.octicon-chevron-up:before { content: '\f0a2'} /*  */ -.octicon-circle-slash:before { content: '\f084'} /*  */ -.octicon-circuit-board:before { content: '\f0d6'} /*  */ -.octicon-clippy:before { content: '\f035'} /*  */ -.octicon-clock:before { content: '\f046'} /*  */ -.octicon-cloud-download:before { content: '\f00b'} /*  */ -.octicon-cloud-upload:before { content: '\f00c'} /*  */ -.octicon-code:before { content: '\f05f'} /*  */ -.octicon-color-mode:before { content: '\f065'} /*  */ -.octicon-comment-add:before, -.octicon-comment:before { content: '\f02b'} /*  */ -.octicon-comment-discussion:before { content: '\f04f'} /*  */ -.octicon-credit-card:before { content: '\f045'} /*  */ -.octicon-dash:before { content: '\f0ca'} /*  */ -.octicon-dashboard:before { content: '\f07d'} /*  */ -.octicon-database:before { content: '\f096'} /*  */ -.octicon-clone:before, -.octicon-desktop-download:before { content: '\f0dc'} /*  */ -.octicon-device-camera:before { content: '\f056'} /*  */ -.octicon-device-camera-video:before { content: '\f057'} /*  */ -.octicon-device-desktop:before { content: '\f27c'} /*  */ -.octicon-device-mobile:before { content: '\f038'} /*  */ -.octicon-diff:before { content: '\f04d'} /*  */ -.octicon-diff-added:before { content: '\f06b'} /*  */ -.octicon-diff-ignored:before { content: '\f099'} /*  */ -.octicon-diff-modified:before { content: '\f06d'} /*  */ -.octicon-diff-removed:before { content: '\f06c'} /*  */ -.octicon-diff-renamed:before { content: '\f06e'} /*  */ -.octicon-ellipsis:before { content: '\f09a'} /*  */ -.octicon-eye-unwatch:before, -.octicon-eye-watch:before, -.octicon-eye:before { content: '\f04e'} /*  */ -.octicon-file-binary:before { content: '\f094'} /*  */ -.octicon-file-code:before { content: '\f010'} /*  */ -.octicon-file-directory:before { content: '\f016'} /*  */ -.octicon-file-media:before { content: '\f012'} /*  */ -.octicon-file-pdf:before { content: '\f014'} /*  */ -.octicon-file-submodule:before { content: '\f017'} /*  */ -.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ -.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ -.octicon-file-text:before { content: '\f011'} /*  */ -.octicon-file-zip:before { content: '\f013'} /*  */ -.octicon-flame:before { content: '\f0d2'} /*  */ -.octicon-fold:before { content: '\f0cc'} /*  */ -.octicon-gear:before { content: '\f02f'} /*  */ -.octicon-gift:before { content: '\f042'} /*  */ -.octicon-gist:before { content: '\f00e'} /*  */ -.octicon-gist-secret:before { content: '\f08c'} /*  */ -.octicon-git-branch-create:before, -.octicon-git-branch-delete:before, -.octicon-git-branch:before { content: '\f020'} /*  */ -.octicon-git-commit:before { content: '\f01f'} /*  */ -.octicon-git-compare:before { content: '\f0ac'} /*  */ -.octicon-git-merge:before { content: '\f023'} /*  */ -.octicon-git-pull-request-abandoned:before, -.octicon-git-pull-request:before { content: '\f009'} /*  */ -.octicon-globe:before { content: '\f0b6'} /*  */ -.octicon-graph:before { content: '\f043'} /*  */ -.octicon-heart:before { content: '\2665'} /* ♥ */ -.octicon-history:before { content: '\f07e'} /*  */ -.octicon-home:before { content: '\f08d'} /*  */ -.octicon-horizontal-rule:before { content: '\f070'} /*  */ -.octicon-hubot:before { content: '\f09d'} /*  */ -.octicon-inbox:before { content: '\f0cf'} /*  */ -.octicon-info:before { content: '\f059'} /*  */ -.octicon-issue-closed:before { content: '\f028'} /*  */ -.octicon-issue-opened:before { content: '\f026'} /*  */ -.octicon-issue-reopened:before { content: '\f027'} /*  */ -.octicon-jersey:before { content: '\f019'} /*  */ -.octicon-key:before { content: '\f049'} /*  */ -.octicon-keyboard:before { content: '\f00d'} /*  */ -.octicon-law:before { content: '\f0d8'} /*  */ -.octicon-light-bulb:before { content: '\f000'} /*  */ -.octicon-link:before { content: '\f05c'} /*  */ -.octicon-link-external:before { content: '\f07f'} /*  */ -.octicon-list-ordered:before { content: '\f062'} /*  */ -.octicon-list-unordered:before { content: '\f061'} /*  */ -.octicon-location:before { content: '\f060'} /*  */ -.octicon-gist-private:before, -.octicon-mirror-private:before, -.octicon-git-fork-private:before, -.octicon-lock:before { content: '\f06a'} /*  */ -.octicon-logo-github:before { content: '\f092'} /*  */ -.octicon-mail:before { content: '\f03b'} /*  */ -.octicon-mail-read:before { content: '\f03c'} /*  */ -.octicon-mail-reply:before { content: '\f051'} /*  */ -.octicon-mark-github:before { content: '\f00a'} /*  */ -.octicon-markdown:before { content: '\f0c9'} /*  */ -.octicon-megaphone:before { content: '\f077'} /*  */ -.octicon-mention:before { content: '\f0be'} /*  */ -.octicon-milestone:before { content: '\f075'} /*  */ -.octicon-mirror-public:before, -.octicon-mirror:before { content: '\f024'} /*  */ -.octicon-mortar-board:before { content: '\f0d7'} /*  */ -.octicon-mute:before { content: '\f080'} /*  */ -.octicon-no-newline:before { content: '\f09c'} /*  */ -.octicon-octoface:before { content: '\f008'} /*  */ -.octicon-organization:before { content: '\f037'} /*  */ -.octicon-package:before { content: '\f0c4'} /*  */ -.octicon-paintcan:before { content: '\f0d1'} /*  */ -.octicon-pencil:before { content: '\f058'} /*  */ -.octicon-person-add:before, -.octicon-person-follow:before, -.octicon-person:before { content: '\f018'} /*  */ -.octicon-pin:before { content: '\f041'} /*  */ -.octicon-plug:before { content: '\f0d4'} /*  */ -.octicon-repo-create:before, -.octicon-gist-new:before, -.octicon-file-directory-create:before, -.octicon-file-add:before, -.octicon-plus:before { content: '\f05d'} /*  */ -.octicon-primitive-dot:before { content: '\f052'} /*  */ -.octicon-primitive-square:before { content: '\f053'} /*  */ -.octicon-pulse:before { content: '\f085'} /*  */ -.octicon-question:before { content: '\f02c'} /*  */ -.octicon-quote:before { content: '\f063'} /*  */ -.octicon-radio-tower:before { content: '\f030'} /*  */ -.octicon-repo-delete:before, -.octicon-repo:before { content: '\f001'} /*  */ -.octicon-repo-clone:before { content: '\f04c'} /*  */ -.octicon-repo-force-push:before { content: '\f04a'} /*  */ -.octicon-gist-fork:before, -.octicon-repo-forked:before { content: '\f002'} /*  */ -.octicon-repo-pull:before { content: '\f006'} /*  */ -.octicon-repo-push:before { content: '\f005'} /*  */ -.octicon-rocket:before { content: '\f033'} /*  */ -.octicon-rss:before { content: '\f034'} /*  */ -.octicon-ruby:before { content: '\f047'} /*  */ -.octicon-screen-full:before { content: '\f066'} /*  */ -.octicon-screen-normal:before { content: '\f067'} /*  */ -.octicon-search-save:before, -.octicon-search:before { content: '\f02e'} /*  */ -.octicon-server:before { content: '\f097'} /*  */ -.octicon-settings:before { content: '\f07c'} /*  */ -.octicon-shield:before { content: '\f0e1'} /*  */ -.octicon-log-in:before, -.octicon-sign-in:before { content: '\f036'} /*  */ -.octicon-log-out:before, -.octicon-sign-out:before { content: '\f032'} /*  */ -.octicon-squirrel:before { content: '\f0b2'} /*  */ -.octicon-star-add:before, -.octicon-star-delete:before, -.octicon-star:before { content: '\f02a'} /*  */ -.octicon-stop:before { content: '\f08f'} /*  */ -.octicon-repo-sync:before, -.octicon-sync:before { content: '\f087'} /*  */ -.octicon-tag-remove:before, -.octicon-tag-add:before, -.octicon-tag:before { content: '\f015'} /*  */ -.octicon-telescope:before { content: '\f088'} /*  */ -.octicon-terminal:before { content: '\f0c8'} /*  */ -.octicon-three-bars:before { content: '\f05e'} /*  */ -.octicon-thumbsdown:before { content: '\f0db'} /*  */ -.octicon-thumbsup:before { content: '\f0da'} /*  */ -.octicon-tools:before { content: '\f031'} /*  */ -.octicon-trashcan:before { content: '\f0d0'} /*  */ -.octicon-triangle-down:before { content: '\f05b'} /*  */ -.octicon-triangle-left:before { content: '\f044'} /*  */ -.octicon-triangle-right:before { content: '\f05a'} /*  */ -.octicon-triangle-up:before { content: '\f0aa'} /*  */ -.octicon-unfold:before { content: '\f039'} /*  */ -.octicon-unmute:before { content: '\f0ba'} /*  */ -.octicon-versions:before { content: '\f064'} /*  */ -.octicon-watch:before { content: '\f0e0'} /*  */ -.octicon-remove-close:before, -.octicon-x:before { content: '\f081'} /*  */ -.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/src/vs/base/browser/ui/progressbar/progressbar.ts b/src/vs/base/browser/ui/progressbar/progressbar.ts index a8e247af55a..ce3a86a4d30 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.ts +++ b/src/vs/base/browser/ui/progressbar/progressbar.ts @@ -10,6 +10,7 @@ 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'; +import { isNumber } from 'vs/base/common/types'; const css_done = 'done'; const css_active = 'active'; @@ -146,7 +147,7 @@ export class ProgressBar extends Disposable { * Finds out if this progress bar is configured with total work */ hasTotal(): boolean { - return !isNaN(this.totalWork as number); + return isNumber(this.totalWork); } /** @@ -172,10 +173,11 @@ export class ProgressBar extends Disposable { } private doSetWorked(value: number): ProgressBar { - assert.ok(!isNaN(this.totalWork as number), 'Total work not set'); + assert.ok(isNumber(this.totalWork), 'Total work not set'); + const totalWork = this.totalWork!; this.workedVal = value; - this.workedVal = Math.min(this.totalWork as number, this.workedVal); + this.workedVal = Math.min(totalWork, this.workedVal); if (hasClass(this.element, css_infinite)) { removeClass(this.element, css_infinite); @@ -193,7 +195,7 @@ export class ProgressBar extends Disposable { addClass(this.element, css_discrete); } - this.bit.style.width = 100 * (this.workedVal / (this.totalWork as number)) + '%'; + this.bit.style.width = 100 * (this.workedVal / (totalWork)) + '%'; return this; } diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index 94ea3182c95..27be21169e7 100644 --- a/src/vs/base/browser/ui/sash/sash.css +++ b/src/vs/base/browser/ui/sash/sash.css @@ -5,7 +5,7 @@ .monaco-sash { position: absolute; - z-index: 90; + z-index: 35; touch-action: none; } @@ -84,6 +84,7 @@ .monaco-sash.disabled { cursor: default !important; + pointer-events: none !important; } /** Touch **/ @@ -98,10 +99,14 @@ /** Debug **/ -.monaco-sash.debug:not(.disabled) { +.monaco-sash.debug { background: cyan; } +.monaco-sash.debug.disabled { + background: rgba(0, 255, 255, 0.2); +} + .monaco-sash.debug:not(.disabled).orthogonal-start::before, .monaco-sash.debug:not(.disabled).orthogonal-end::after { background: red; diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index ca7b602142a..8c7168bad53 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -264,7 +264,7 @@ export class Sash extends Disposable { const onMouseMove = (e: MouseEvent) => { EventHelper.stop(e, false); - const mouseMoveEvent = new StandardMouseEvent(e as MouseEvent); + const mouseMoveEvent = new StandardMouseEvent(e); const event: ISashEvent = { startX, currentX: mouseMoveEvent.posx, startY, currentY: mouseMoveEvent.posy, altKey }; this._onDidChange.fire(event); diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index 2be752aa68d..1c36edb215a 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -34,8 +34,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { className: 'left-arrow', top: scrollbarDelta, left: arrowDelta, - bottom: void 0, - right: void 0, + bottom: undefined, + right: undefined, bgWidth: options.arrowSize, bgHeight: options.horizontalScrollbarSize, onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 1, 0)), @@ -44,8 +44,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'right-arrow', top: scrollbarDelta, - left: void 0, - bottom: void 0, + left: undefined, + bottom: undefined, right: arrowDelta, bgWidth: options.arrowSize, bgHeight: options.horizontalScrollbarSize, diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index a6256deeba9..fbf8d5dcd13 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -281,6 +281,7 @@ export abstract class AbstractScrollableElement extends Widget { let massagedOptions = resolveOptions(newOptions); this._options.handleMouseWheel = massagedOptions.handleMouseWheel; this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity; + this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity; this._setListeningToMouseWheel(this._options.handleMouseWheel); if (!this._options.lazyRender) { @@ -340,6 +341,12 @@ export abstract class AbstractScrollableElement extends Widget { deltaY = 0; } + if (e.browserEvent && e.browserEvent.altKey) { + // fastScrolling + deltaX = deltaX * this._options.fastScrollSensitivity; + deltaY = deltaY * this._options.fastScrollSensitivity; + } + const futureScrollPosition = this._scrollable.getFutureScrollPosition(); let desiredScrollPosition: INewScrollPosition = {}; @@ -516,7 +523,7 @@ export class DomScrollableElement extends ScrollableElement { } public scanDomNode(): void { - // widh, scrollLeft, scrollWidth, height, scrollTop, scrollHeight + // width, scrollLeft, scrollWidth, height, scrollTop, scrollHeight this.setScrollDimensions({ width: this._element.clientWidth, scrollWidth: this._element.scrollWidth, @@ -540,6 +547,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme alwaysConsumeMouseWheel: (typeof opts.alwaysConsumeMouseWheel !== 'undefined' ? opts.alwaysConsumeMouseWheel : false), scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false), mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1), + fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5), mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true), arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11), diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts index f8ed7e6fb11..7073bee8cb5 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts @@ -50,6 +50,11 @@ export interface ScrollableElementCreationOptions { * Defaults to 1. */ mouseWheelScrollSensitivity?: number; + /** + * FastScrolling mulitplier speed when pressing `Alt` + * Defaults to 5. + */ + fastScrollSensitivity?: number; /** * Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right). * Defaults to 11. @@ -107,6 +112,7 @@ export interface ScrollableElementCreationOptions { export interface ScrollableElementChangeOptions { handleMouseWheel?: boolean; mouseWheelScrollSensitivity?: number; + fastScrollSensitivity: number; } export interface ScrollableElementResolvedOptions { @@ -118,6 +124,7 @@ export interface ScrollableElementResolvedOptions { scrollYToX: boolean; alwaysConsumeMouseWheel: boolean; mouseWheelScrollSensitivity: number; + fastScrollSensitivity: number; mouseWheelSmoothScroll: boolean; arrowSize: number; listenOnDomNode: HTMLElement | null; diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index 9851f932bf2..8c1bb82823f 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -35,8 +35,8 @@ export class VerticalScrollbar extends AbstractScrollbar { className: 'up-arrow', top: arrowDelta, left: scrollbarDelta, - bottom: void 0, - right: void 0, + bottom: undefined, + right: undefined, bgWidth: options.verticalScrollbarSize, bgHeight: options.arrowSize, onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 0, 1)), @@ -44,10 +44,10 @@ export class VerticalScrollbar extends AbstractScrollbar { this._createArrow({ className: 'down-arrow', - top: void 0, + top: undefined, left: scrollbarDelta, bottom: arrowDelta, - right: void 0, + right: undefined, bgWidth: options.verticalScrollbarSize, bgHeight: options.arrowSize, onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 0, -1)), diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 40a4d7c9a35..d74c30c8df4 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -14,7 +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 @@ -22,10 +22,9 @@ export interface ISelectBoxDelegate { // Public SelectBox Interface readonly onDidSelect: Event; - setOptions(options: string[], selected?: number, disabled?: number): void; + setOptions(options: ISelectOptionItem[], selected?: number): void; select(index: number): void; - setAriaLabel(label: string); - setDetailsProvider(provider: (index: number) => { details: string, isMarkdown: boolean }); + setAriaLabel(label: string): void; focus(): void; blur(): void; dispose(): void; @@ -37,16 +36,25 @@ export interface ISelectBoxDelegate { } export interface ISelectBoxOptions { + useCustomDrawn?: boolean; ariaLabel?: string; minBottomMargin?: number; - hasDetails?: boolean; - markdownActionHandler?: IContentActionHandler; +} + +// Utilize optionItem interface to capture all option parameters +export interface ISelectOptionItem { + text: string; + decoratorRight?: string; + description?: string; + descriptionIsMarkdown?: boolean; + isDisabled?: boolean; } export interface ISelectBoxStyles extends IListStyles { selectBackground?: Color; selectListBackground?: Color; selectForeground?: Color; + decoratorRightForeground?: Color; selectBorder?: Color; selectListBorder?: Color; focusBorder?: Color; @@ -67,13 +75,13 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { private styles: ISelectBoxStyles; private selectBoxDelegate: ISelectBoxDelegate; - constructor(options: string[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles = deepClone(defaultStyles), selectBoxOptions?: ISelectBoxOptions) { + constructor(options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles = deepClone(defaultStyles), selectBoxOptions?: ISelectBoxOptions) { super(); mixin(this.styles, defaultStyles, false); - // Instantiate select implementation based on platform - if (isMacintosh && !(selectBoxOptions && selectBoxOptions.hasDetails)) { + // Default to native SelectBox for OSX unless overridden + if (isMacintosh && !(selectBoxOptions && selectBoxOptions.useCustomDrawn)) { this.selectBoxDelegate = new SelectBoxNative(options, selected, styles, selectBoxOptions); } else { this.selectBoxDelegate = new SelectBoxList(options, selected, contextViewProvider, styles, selectBoxOptions); @@ -88,8 +96,8 @@ export class SelectBox extends Widget implements ISelectBoxDelegate { return this.selectBoxDelegate.onDidSelect; } - public setOptions(options: string[], selected?: number, disabled?: number): void { - this.selectBoxDelegate.setOptions(options, selected, disabled); + public setOptions(options: ISelectOptionItem[], selected?: number): void { + this.selectBoxDelegate.setOptions(options, selected); } public select(index: number): void { @@ -100,10 +108,6 @@ 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 c2f95bd9f13..7bc3f55b13e 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css @@ -71,13 +71,27 @@ padding-bottom: var(--dropdown-padding-bottom); } +.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row { + cursor: pointer; +} + .monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-text { text-overflow: ellipsis; overflow: hidden; padding-left: 3.5px; white-space: nowrap; + float: left; } +.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row > .option-decorator-right { + text-overflow: ellipsis; + overflow: hidden; + padding-right: 10px; + white-space: nowrap; + float: right; +} + + /* Accepted CSS hiding technique for accessibility reader text */ /* https://webaim.org/techniques/css/invisiblecontent/ */ diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 36b40a6995c..d7ed35c9239 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -6,7 +6,7 @@ import 'vs/css!./selectBoxCustom'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Event, Emitter, chain } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as dom from 'vs/base/browser/dom'; @@ -16,7 +16,7 @@ import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, 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 { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { isMacintosh } from 'vs/base/common/platform'; import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; @@ -24,16 +24,11 @@ const $ = dom.$; const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'selectOption.entry.template'; -export interface ISelectOptionItem { - optionText: string; - optionDescriptionText?: string; - optionDisabled: boolean; -} - interface ISelectListTemplateData { root: HTMLElement; - optionText: HTMLElement; - optionDescriptionText: HTMLElement; + text: HTMLElement; + itemDescription: HTMLElement; + decoratorRight: HTMLElement; disposables: IDisposable[]; } @@ -43,44 +38,43 @@ class SelectListRenderer implements IListRendererObject.create(null); + renderTemplate(container: HTMLElement): ISelectListTemplateData { + const data: ISelectListTemplateData = Object.create(null); data.disposables = []; data.root = container; - data.optionText = dom.append(container, $('.option-text')); - data.optionDescriptionText = dom.append(container, $('.option-text-description')); - dom.addClass(data.optionDescriptionText, 'visually-hidden'); + data.text = dom.append(container, $('.option-text')); + data.decoratorRight = dom.append(container, $('.option-decorator-right')); + data.itemDescription = dom.append(container, $('.option-text-description')); + dom.addClass(data.itemDescription, 'visually-hidden'); return data; } renderElement(element: ISelectOptionItem, index: number, templateData: ISelectListTemplateData): void { - const data = templateData; - const optionText = (element).optionText; - const optionDisabled = (element).optionDisabled; + const data: ISelectListTemplateData = templateData; + const text = element.text; + const decoratorRight = element.decoratorRight; + const isDisabled = element.isDisabled; - data.optionText.textContent = optionText; + data.text.textContent = text; + data.decoratorRight.innerText = (!!decoratorRight ? decoratorRight : ''); - if (typeof element.optionDescriptionText === 'string') { - const optionDescriptionId = (optionText.replace(/ /g, '_').toLowerCase() + '_description_' + data.root.id); - data.optionText.setAttribute('aria-describedby', optionDescriptionId); - data.optionDescriptionText.id = optionDescriptionId; - data.optionDescriptionText.innerText = element.optionDescriptionText; + if (typeof element.description === 'string') { + const itemDescriptionId = (text.replace(/ /g, '_').toLowerCase() + '_description_' + data.root.id); + data.text.setAttribute('aria-describedby', itemDescriptionId); + data.itemDescription.id = itemDescriptionId; + data.itemDescription.innerText = element.description; } // pseudo-select disabled option - if (optionDisabled) { - dom.addClass((data.root), 'option-disabled'); + if (isDisabled) { + dom.addClass(data.root, 'option-disabled'); } else { // Make sure we do class removal from prior template rendering - dom.removeClass((data.root), 'option-disabled'); + dom.removeClass(data.root, 'option-disabled'); } } - disposeElement(): void { - // noop - } - disposeTemplate(templateData: ISelectListTemplateData): void { templateData.disposables = dispose(templateData.disposables); } @@ -95,9 +89,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate; private toDispose: IDisposable[]; private styles: ISelectBoxStyles; @@ -110,13 +103,13 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { details: string, isMarkdown: boolean }; + private _hasDetails: boolean = false; private selectionDetailsPane: HTMLElement; private _skipLayout: boolean = false; private _sticky: boolean = false; // for dev purposes only - constructor(options: string[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { + constructor(options: ISelectOptionItem[], selected: number, contextViewProvider: IContextViewProvider, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { this.toDispose = []; this._isVisible = false; @@ -130,12 +123,6 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { - this.selectElement.add(this.createOption(option, i, disabled === i++)); + this.options.forEach((option, index) => { + this.selectElement.add(this.createOption(option.text, index, option.isDisabled)); + if (typeof option.description === 'string') { + this._hasDetails = true; + } }); - - if (disabled !== undefined) { - this.disabledOptionIndex = disabled; - } } if (selected !== undefined) { @@ -279,18 +265,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { details: string, isMarkdown: boolean }): void { - this.detailsProvider = provider; - } - public focus(): void { if (this.selectElement) { this.selectElement.focus(); @@ -352,7 +323,11 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate .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)) { + if (this.styles.decoratorRightForeground) { + content.push(`.monaco-select-box-dropdown-container > .select-box-dropdown-list-container .monaco-list .monaco-list-row .option-decorator-right { color: ${this.styles.decoratorRightForeground} !important; }`); + } + + if (this.styles.selectBackground && this.styles.selectBorder && !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 { border-top: 1px solid ${this.styles.selectBorder} } `); content.push(`.monaco-select-box-dropdown-container > .select-box-details-pane.border-bottom { border-bottom: 1px solid ${this.styles.selectBorder} } `); @@ -395,10 +370,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { this.selectionDetailsPane.innerText = ''; - description = this.detailsProvider ? this.detailsProvider(index) : { details: '', isMarkdown: false }; - if (description.details) { - if (description.isMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description.details)); + if (option.description) { + if (option.descriptionIsMarkdown) { + this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(option.description)); } else { - this.selectionDetailsPane.innerText = description.details; + this.selectionDetailsPane.innerText = option.description; } this.selectionDetailsPane.style.display = 'block'; } else { @@ -555,18 +518,19 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.options[longest].length) { + this.options.forEach((option, index) => { + const len = option.text.length + (!!option.decoratorRight ? option.decoratorRight.length : 0); + if (len > longestLength) { longest = index; + longestLength = len; } - } + }); - container.innerHTML = this.options[longest]; + + container.innerHTML = this.options[longest].text + (!!this.options[longest].decoratorRight ? (this.options[longest].decoratorRight + ' ') : ''); elementWidth = dom.getTotalWidth(container); } return elementWidth; } - private cloneElementFont(source: HTMLElement, target: HTMLElement) { - const fontSize = window.getComputedStyle(source, null).getPropertyValue('font-size'); - const fontFamily = window.getComputedStyle(source, null).getPropertyValue('font-family'); - target.style.fontFamily = fontFamily; - target.style.fontSize = fontSize; - } - private createSelectList(parent: HTMLElement): void { // If we have already constructive list on open, skip @@ -756,14 +719,13 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.selectList.length > 0) .map(e => new StandardKeyboardEvent(e)); @@ -779,17 +741,17 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.selectList.length > 0) .on(e => this.onMouseUp(e), this, this.toDispose); this.toDispose.push( - this.selectList.onDidBlur(e => this.onListBlur()), - this.selectList.onMouseOver(e => this.selectList.setFocus([e.index])), + this.selectList.onDidBlur(_ => this.onListBlur()), + this.selectList.onMouseOver(e => typeof e.index !== 'undefined' && this.selectList.setFocus([e.index])), this.selectList.onFocusChange(e => this.onListFocus(e)) ); - this.selectList.getHTMLElement().setAttribute('aria-label', this.selectBoxOptions.ariaLabel); + this.selectList.getHTMLElement().setAttribute('aria-label', this.selectBoxOptions.ariaLabel || ''); this.selectList.getHTMLElement().setAttribute('aria-expanded', 'true'); this.styleList(); @@ -802,12 +764,21 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegatee.target; + if (!target) { return; } - const listRowElement = e.toElement.parentElement; + // Check our mouse event is on an option (not scrollbar) + if (!!target.classList.contains('slider')) { + return; + } + + const listRowElement = target.closest('.monaco-list-row'); + + if (!listRowElement) { + return; + } const index = Number(listRowElement.getAttribute('data-index')); const disabled = listRowElement.classList.contains('option-disabled'); @@ -826,7 +797,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate { for (let i = 0; i < element.childNodes.length; i++) { - const child = element.childNodes.item(i); + const child = element.childNodes.item(i); - const tagName = (child).tagName && (child).tagName.toLowerCase(); + const tagName = child.tagName && child.tagName.toLowerCase(); if (tagName === 'img') { element.removeChild(child); } else { @@ -860,9 +831,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate) { // Skip during initial layout - if (!this._isVisible) { + if (!this._isVisible || !this._hasDetails) { return; } 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)); + const description = this.options[selectedIndex].description; + const descriptionIsMarkdown = this.options[selectedIndex].descriptionIsMarkdown; + + if (description) { + if (descriptionIsMarkdown) { + this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description)); } else { - this.selectionDetailsPane.innerText = description.details; + this.selectionDetailsPane.innerText = description; } this.selectionDetailsPane.style.display = 'block'; } else { @@ -918,7 +889,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.selected + 2) { + const nextOptionDisabled = this.options[this.selected + 1].isDisabled; + + if (nextOptionDisabled && this.options.length > this.selected + 2) { this.selected += 2; - } else if ((this.selected + 1) === this.disabledOptionIndex) { + } else if (nextOptionDisabled) { return; } else { this.selected++; } + // Set focus/selection - only fire event when closing drop-down or on blur this.select(this.selected); this.selectList.setFocus([this.selected]); @@ -946,7 +921,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 0) { // Skip disabled options - if ((this.selected - 1) === this.disabledOptionIndex && this.selected > 1) { + const previousOptionDisabled = this.options[this.selected - 1].isDisabled; + if (previousOptionDisabled && this.selected > 1) { this.selected -= 2; } else { this.selected--; @@ -968,7 +944,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 0) { + if (this.options[this.selected].isDisabled && this.selected > 0) { this.selected--; this.selectList.setFocus([this.selected]); } @@ -1003,7 +979,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 1) { + if (this.options[this.selected].isDisabled && this.selected > 1) { this.selected++; } this.selectList.setFocus([this.selected]); @@ -1018,7 +994,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate 1) { + if (this.options[this.selected].isDisabled && this.selected > 1) { this.selected--; } this.selectList.setFocus([this.selected]); @@ -1033,7 +1009,7 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate; private toDispose: IDisposable[]; private styles: ISelectBoxStyles; - constructor(options: string[], selected: number, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { - + constructor(options: ISelectOptionItem[], selected: number, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) { this.toDispose = []; this.selectBoxOptions = selectBoxOptions || Object.create(null); - this.selectElement = document.createElement('select'); + this.options = []; - // Workaround for Electron 2.x - // Native select should not require explicit role attribute, however, Electron 2.x - // incorrectly exposes select as menuItem which interferes with labeling and results - // in the unlabeled not been read. Electron 3 appears to fix. - this.selectElement.setAttribute('role', 'combobox'); + this.selectElement = document.createElement('select'); this.selectElement.className = 'monaco-select-box'; @@ -83,15 +78,14 @@ export class SelectBoxNative implements ISelectBoxDelegate { return this._onDidSelect.event; } - public setOptions(options: string[], selected?: number, disabled?: number): void { + public setOptions(options: ISelectOptionItem[], selected?: number): void { if (!this.options || !arrays.equals(this.options, options)) { this.options = options; this.selectElement.options.length = 0; - let i = 0; - this.options.forEach((option) => { - this.selectElement.add(this.createOption(option, i, disabled === i++)); + this.options.forEach((option, index) => { + this.selectElement.add(this.createOption(option.text, index, option.isDisabled)); }); } @@ -102,7 +96,9 @@ export class SelectBoxNative implements ISelectBoxDelegate { } public select(index: number): void { - if (index >= 0 && index < this.options.length) { + if (this.options.length === 0) { + this.selected = 0; + } else if (index >= 0 && index < this.options.length) { this.selected = index; } else if (index > this.options.length - 1) { // Adjust index to end of list @@ -113,7 +109,11 @@ export class SelectBoxNative implements ISelectBoxDelegate { } this.selectElement.selectedIndex = this.selected; - this.selectElement.title = this.options[this.selected]; + if ((this.selected < this.options.length) && typeof this.options[this.selected].text === 'string') { + this.selectElement.title = this.options[this.selected].text; + } else { + this.selectElement.title = ''; + } } public setAriaLabel(label: string): void { @@ -121,10 +121,6 @@ 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(); @@ -165,10 +161,10 @@ export class SelectBoxNative implements ISelectBoxDelegate { } private createOption(value: string, index: number, disabled?: boolean): HTMLOptionElement { - let option = document.createElement('option'); + const option = document.createElement('option'); option.value = value; option.text = value; - option.disabled = disabled; + option.disabled = !!disabled; return option; } diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 2aa77865f06..735c201e8db 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -5,7 +5,7 @@ import 'vs/css!./panelview'; import { IDisposable, dispose, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter, chain, filterEvent } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -42,9 +42,10 @@ export abstract class Panel implements IView { private static readonly HEADER_SIZE = 22; readonly element: HTMLElement; + private header: HTMLElement; + private body: HTMLElement; protected _expanded: boolean; - protected disposables: IDisposable[] = []; private expandedSize: number | undefined = undefined; private _headerVisible = true; @@ -52,12 +53,13 @@ export abstract class Panel implements IView { private _maximumBodySize: number; private ariaHeaderLabel: string; private styles: IPanelStyles = {}; - - private header: HTMLElement; + private animationTimer: number | undefined = undefined; private _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; + protected disposables: IDisposable[] = []; + get draggableElement(): HTMLElement { return this.header; } @@ -77,7 +79,7 @@ export abstract class Panel implements IView { set minimumBodySize(size: number) { this._minimumBodySize = size; - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } get maximumBodySize(): number { @@ -86,7 +88,7 @@ export abstract class Panel implements IView { set maximumBodySize(size: number) { this._maximumBodySize = size; - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } private get headerSize(): number { @@ -109,6 +111,8 @@ export abstract class Panel implements IView { return headerSize + maximumBodySize; } + width: number; + constructor(options: IPanelOptions = {}) { this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; this.ariaHeaderLabel = options.ariaHeaderLabel || ''; @@ -122,14 +126,27 @@ export abstract class Panel implements IView { return this._expanded; } - setExpanded(expanded: boolean): void { + setExpanded(expanded: boolean): boolean { if (this._expanded === !!expanded) { - return; + return false; } this._expanded = !!expanded; this.updateHeader(); + + if (expanded) { + if (typeof this.animationTimer === 'number') { + clearTimeout(this.animationTimer); + } + append(this.element, this.body); + } else { + this.animationTimer = window.setTimeout(() => { + this.body.remove(); + }, 200); + } + this._onDidChange.fire(expanded ? this.expandedSize : undefined); + return true; } get headerVisible(): boolean { @@ -143,7 +160,7 @@ export abstract class Panel implements IView { this._headerVisible = !!visible; this.updateHeader(); - this._onDidChange.fire(); + this._onDidChange.fire(undefined); } render(): void { @@ -155,12 +172,13 @@ export abstract class Panel implements IView { this.renderHeader(this.header); const focusTracker = trackFocus(this.header); - focusTracker.onDidFocus(() => addClass(this.header, 'focused')); - focusTracker.onDidBlur(() => removeClass(this.header, 'focused')); + this.disposables.push(focusTracker); + focusTracker.onDidFocus(() => addClass(this.header, 'focused'), null, this.disposables); + focusTracker.onDidBlur(() => removeClass(this.header, 'focused'), null, this.disposables); this.updateHeader(); - const onHeaderKeyDown = chain(domEvent(this.header, 'keydown')) + const onHeaderKeyDown = Event.chain(domEvent(this.header, 'keydown')) .map(e => new StandardKeyboardEvent(e)); onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) @@ -175,23 +193,16 @@ export abstract class Panel implements IView { domEvent(this.header, 'click') (() => this.setExpanded(!this.isExpanded()), null, this.disposables); - // TODO@Joao move this down to panelview - // onHeaderKeyDown.filter(e => e.keyCode === KeyCode.UpArrow) - // .event(focusPrevious, this, this.disposables); - - // onHeaderKeyDown.filter(e => e.keyCode === KeyCode.DownArrow) - // .event(focusNext, this, this.disposables); - - const body = append(this.element, $('.panel-body')); - this.renderBody(body); + this.body = append(this.element, $('.panel-body')); + this.renderBody(this.body); } - layout(size: number): void { + layout(height: number): void { const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; if (this.isExpanded()) { - this.layoutBody(size - headerSize); - this.expandedSize = size; + this.layoutBody(height - headerSize, this.width); + this.expandedSize = height; } } @@ -222,7 +233,7 @@ export abstract class Panel implements IView { protected abstract renderHeader(container: HTMLElement): void; protected abstract renderBody(container: HTMLElement): void; - protected abstract layoutBody(size: number): void; + protected abstract layoutBody(height: number, width: number): void; dispose(): void { this.disposables = dispose(this.disposables); @@ -264,7 +275,7 @@ class PanelDraggable extends Disposable { e.dataTransfer.effectAllowed = 'move'; - const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggableElement.textContent || '')); + const dragImage = append(document.body, $('.monaco-drag-image', {}, this.panel.draggableElement.textContent || '')); e.dataTransfer.setDragImage(dragImage, -10, -10); setTimeout(() => document.body.removeChild(dragImage), 0); @@ -367,8 +378,9 @@ export class PanelView extends Disposable { private dndContext: IDndContext = { draggable: null }; private el: HTMLElement; private panelItems: IPanelItem[] = []; + private width: number; private splitview: SplitView; - private animationTimer: number | null = null; + private animationTimer: number | undefined = undefined; private _onDidDrop = this._register(new Emitter<{ from: Panel, to: Panel }>()); readonly onDidDrop: Event<{ from: Panel, to: Panel }> = this._onDidDrop.event; @@ -391,11 +403,12 @@ export class PanelView extends Disposable { let shouldAnimate = false; disposables.push(scheduleAtNextAnimationFrame(() => shouldAnimate = true)); - filterEvent(panel.onDidChange, () => shouldAnimate) + Event.filter(panel.onDidChange, () => shouldAnimate) (this.setupAnimation, this, disposables); const panelItem = { panel, disposable: combinedDisposable(disposables) }; this.panelItems.splice(index, 0, panelItem); + panel.width = this.width; this.splitview.addView(panel, size, index); if (this.dnd) { @@ -451,8 +464,14 @@ export class PanelView extends Disposable { return this.splitview.getViewSize(index); } - layout(size: number): void { - this.splitview.layout(size); + layout(height: number, width: number): void { + this.width = width; + + for (const panelItem of this.panelItems) { + panelItem.panel.width = width; + } + + this.splitview.layout(height); } private setupAnimation(): void { @@ -463,7 +482,7 @@ export class PanelView extends Disposable { addClass(this.el, 'animated'); this.animationTimer = window.setTimeout(() => { - this.animationTimer = null; + this.animationTimer = undefined; removeClass(this.el, 'animated'); }, 200); } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 0de80a6c763..48b0cef6866 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -5,7 +5,7 @@ import 'vs/css!./splitview'; import { IDisposable, combinedDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { Event, mapEvent, Emitter } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; import * as dom from 'vs/base/browser/dom'; import { clamp } from 'vs/base/common/numbers'; @@ -47,7 +47,6 @@ export interface IView { readonly maximumSize: number; readonly onDidChange: Event; readonly priority?: LayoutPriority; - readonly snapSize?: number; layout(size: number, orientation: Orientation): void; } @@ -227,7 +226,7 @@ export class SplitView extends Disposable { // Add sash if (this.viewItems.length > 1) { const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL; - const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: sash => this.getSashPosition(sash) } : { getVerticalSashLeft: sash => this.getSashPosition(sash) }; + const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: (sash: Sash) => this.getSashPosition(sash) } : { getVerticalSashLeft: (sash: Sash) => this.getSashPosition(sash) }; const sash = new Sash(this.sashContainer, layoutProvider, { orientation, orthogonalStartSash: this.orthogonalStartSash, @@ -235,14 +234,14 @@ export class SplitView extends Disposable { }); const sashEventMapper = this.orientation === Orientation.VERTICAL - ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey } as ISashEvent) - : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey } as ISashEvent); + ? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY, alt: e.altKey }) + : (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX, alt: e.altKey }); - const onStart = mapEvent(sash.onDidStart, sashEventMapper); + const onStart = Event.map(sash.onDidStart, sashEventMapper); const onStartDisposable = onStart(this.onSashStart, this); - const onChange = mapEvent(sash.onDidChange, sashEventMapper); + const onChange = Event.map(sash.onDidChange, sashEventMapper); const onChangeDisposable = onChange(this.onSashChange, this); - const onEnd = mapEvent(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); + const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); const onEndDisposable = onEnd(this.onSashEnd, this); const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); @@ -352,7 +351,7 @@ export class SplitView extends Disposable { } else { for (let i = 0; i < this.viewItems.length; i++) { const item = this.viewItems[i]; - item.size = SplitView.clamp(item, Math.round(this.proportions[i] * size)); + item.size = clamp(Math.round(this.proportions[i] * size), item.view.minimumSize, item.view.maximumSize); } } @@ -444,7 +443,7 @@ export class SplitView extends Disposable { } size = typeof size === 'number' ? size : item.size; - size = SplitView.clamp(item, size); + size = clamp(size, item.view.minimumSize, item.view.maximumSize); if (this.inverseAltBehavior && index > 0) { // In this case, we want the view to grow or shrink both sides equally @@ -551,29 +550,27 @@ export class SplitView extends Disposable { const downItems = downIndexes.map(i => this.viewItems[i]); const downSizes = downIndexes.map(i => sizes[i]); - const minDeltaUp = upIndexes.reduce((r, i) => r + ((typeof this.viewItems[i].view.snapSize === 'number' ? 0 : this.viewItems[i].view.minimumSize) - sizes[i]), 0); + const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.minimumSize - sizes[i]), 0); const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0); - const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - (typeof this.viewItems[i].view.snapSize === 'number' ? 0 : this.viewItems[i].view.minimumSize)), 0); + const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0); const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.maximumSize), 0); const minDelta = Math.max(minDeltaUp, minDeltaDown, overloadMinDelta); const maxDelta = Math.min(maxDeltaDown, maxDeltaUp, overloadMaxDelta); - const tentativeDelta = clamp(delta, minDelta, maxDelta); - let actualDelta = 0; + delta = clamp(delta, minDelta, maxDelta); - for (let i = 0, deltaUp = tentativeDelta; i < upItems.length; i++) { + for (let i = 0, deltaUp = delta; i < upItems.length; i++) { const item = upItems[i]; - const size = SplitView.clamp(item, upSizes[i] + deltaUp/* , upIndexes[i] === index */); + const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize); const viewDelta = size - upSizes[i]; - actualDelta += viewDelta; deltaUp -= viewDelta; item.size = size; } - for (let i = 0, deltaDown = actualDelta; i < downItems.length; i++) { + for (let i = 0, deltaDown = delta; i < downItems.length; i++) { const item = downItems[i]; - const size = SplitView.clamp(item, downSizes[i] - deltaDown); + const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize); const viewDelta = size - downSizes[i]; deltaDown += viewDelta; @@ -583,24 +580,13 @@ export class SplitView extends Disposable { return delta; } - private static clamp(item: IViewItem, size: number): number { - const result = clamp(size, item.view.minimumSize, item.view.maximumSize); - - if (typeof item.view.snapSize !== 'number' || size >= item.view.minimumSize) { - return result; - } - - const snapSize = Math.min(item.view.snapSize, item.view.minimumSize); - return size < snapSize ? 0 : item.view.minimumSize; - } - private distributeEmptySpace(): void { let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); let emptyDelta = this.size - contentSize; for (let i = this.viewItems.length - 1; emptyDelta !== 0 && i >= 0; i--) { const item = this.viewItems[i]; - const size = SplitView.clamp(item, item.size + emptyDelta); + const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize); const viewDelta = size - item.size; emptyDelta -= viewDelta; diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index ff50ccd875d..557df5fada7 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -6,19 +6,20 @@ import 'vs/css!./toolbar'; import * as nls from 'vs/nls'; import { Action, IActionRunner, IAction } from 'vs/base/common/actions'; -import { ActionBar, ActionsOrientation, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IContextMenuProvider, DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown'; +import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IContextMenuProvider, DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { withNullAsUndefined } from 'vs/base/common/types'; export const CONTEXT = 'context.toolbar'; export interface IToolBarOptions { orientation?: ActionsOrientation; - actionItemProvider?: IActionItemProvider; + actionViewItemProvider?: IActionViewItemProvider; ariaLabel?: string; - getKeyBinding?: (action: IAction) => ResolvedKeybinding; + getKeyBinding?: (action: IAction) => ResolvedKeybinding | undefined; actionRunner?: IActionRunner; toggleMenuTitle?: string; anchorAlignmentProvider?: () => AnchorAlignment; @@ -31,7 +32,7 @@ export class ToolBar extends Disposable { private options: IToolBarOptions; private actionBar: ActionBar; private toggleMenuAction: ToggleMenuAction; - private toggleMenuActionItem: DropdownMenuActionItem; + private toggleMenuActionViewItem?: DropdownMenuActionViewItem; private hasSecondaryActions: boolean; private lookupKeybindings: boolean; @@ -41,7 +42,7 @@ export class ToolBar extends Disposable { this.options = options; this.lookupKeybindings = typeof this.options.getKeyBinding === 'function'; - this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionItem && this.toggleMenuActionItem.show(), options.toggleMenuTitle)); + this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem && this.toggleMenuActionViewItem.show(), options.toggleMenuTitle)); let element = document.createElement('div'); element.className = 'monaco-toolbar'; @@ -51,33 +52,33 @@ export class ToolBar extends Disposable { orientation: options.orientation, ariaLabel: options.ariaLabel, actionRunner: options.actionRunner, - actionItemProvider: (action: Action) => { + actionViewItemProvider: (action: Action) => { // Return special action item for the toggle menu action if (action.id === ToggleMenuAction.ID) { // Dispose old - if (this.toggleMenuActionItem) { - this.toggleMenuActionItem.dispose(); + if (this.toggleMenuActionViewItem) { + this.toggleMenuActionViewItem.dispose(); } // Create new - this.toggleMenuActionItem = new DropdownMenuActionItem( + this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( action, (action).menuActions, contextMenuProvider, - this.options.actionItemProvider, + this.options.actionViewItemProvider, this.actionRunner, this.options.getKeyBinding, 'toolbar-toggle-more', this.options.anchorAlignmentProvider ); - this.toggleMenuActionItem.setActionContext(this.actionBar.context); + this.toggleMenuActionViewItem!.setActionContext(this.actionBar.context); - return this.toggleMenuActionItem; + return this.toggleMenuActionViewItem; } - return options.actionItemProvider ? options.actionItemProvider(action) : null; + return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; } })); } @@ -92,8 +93,8 @@ export class ToolBar extends Disposable { set context(context: any) { this.actionBar.context = context; - if (this.toggleMenuActionItem) { - this.toggleMenuActionItem.setActionContext(context); + if (this.toggleMenuActionViewItem) { + this.toggleMenuActionViewItem.setActionContext(context); } } @@ -118,8 +119,8 @@ export class ToolBar extends Disposable { let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : []; // Inject additional action to open secondary actions if present - this.hasSecondaryActions = secondaryActions && secondaryActions.length > 0; - if (this.hasSecondaryActions) { + this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0); + if (this.hasSecondaryActions && secondaryActions) { this.toggleMenuAction.menuActions = secondaryActions.slice(0); primaryActionsToSet.push(this.toggleMenuAction); } @@ -132,10 +133,10 @@ export class ToolBar extends Disposable { }; } - private getKeybindingLabel(action: IAction): string { - const key = this.lookupKeybindings ? this.options.getKeyBinding(action) : void 0; + private getKeybindingLabel(action: IAction): string | undefined { + const key = this.lookupKeybindings && this.options.getKeyBinding ? this.options.getKeyBinding(action) : undefined; - return key ? key.getLabel() : void 0; + return withNullAsUndefined(key && key.getLabel()); } addPrimaryAction(primaryAction: IAction): () => void { @@ -155,9 +156,9 @@ export class ToolBar extends Disposable { } dispose(): void { - if (this.toggleMenuActionItem) { - this.toggleMenuActionItem.dispose(); - this.toggleMenuActionItem = void 0; + if (this.toggleMenuActionViewItem) { + this.toggleMenuActionViewItem.dispose(); + this.toggleMenuActionViewItem = undefined; } super.dispose(); @@ -173,7 +174,7 @@ class ToggleMenuAction extends Action { constructor(toggleDropdownMenu: () => void, title?: string) { title = title || nls.localize('moreActions', "More Actions..."); - super(ToggleMenuAction.ID, title, null, true); + super(ToggleMenuAction.ID, title, undefined, true); this.toggleDropdownMenu = toggleDropdownMenu; } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index f1ff33750cb..14757bf2117 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -4,58 +4,162 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/tree'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IListOptions, List, IIdentityProvider, IMultipleSelectionController, IListStyles, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass } from 'vs/base/browser/dom'; -import { Event, Relay, chain } from 'vs/base/common/event'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass } from 'vs/base/browser/dom'; +import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; +import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; +import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd'; +import { range, equals, distinctES6 } from 'vs/base/common/arrays'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { domEvent } from 'vs/base/browser/event'; +import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; +import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { localize } from 'vs/nls'; +import { disposableTimeout } from 'vs/base/common/async'; +import { isMacintosh } from 'vs/base/common/platform'; +import { values } from 'vs/base/common/map'; +import { clamp } from 'vs/base/common/numbers'; +import { ScrollEvent } from 'vs/base/common/scrollable'; + +function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + return new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + return data; +} + +class TreeNodeListDragAndDrop implements IListDragAndDrop> { + + private autoExpandNode: ITreeNode | undefined; + private autoExpandDisposable: IDisposable = Disposable.None; + + constructor(private modelProvider: () => ITreeModel, private dnd: ITreeDragAndDrop) { } + + getDragURI(node: ITreeNode): string | null { + return this.dnd.getDragURI(node.element); + } + + getDragLabel(nodes: ITreeNode[]): string | undefined { + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(nodes.map(node => node.element)); + } -export function createComposedTreeListOptions(options?: IListOptions): IListOptions | undefined { - if (!options) { return undefined; } - let identityProvider: IIdentityProvider | undefined = undefined; - - if (options.identityProvider) { - const ip = options.identityProvider; - identityProvider = el => ip(el.element); + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + if (this.dnd.onDragStart) { + this.dnd.onDragStart(asTreeDragAndDropData(data), originalEvent); + } } - let multipleSelectionController: IMultipleSelectionController | undefined = undefined; + onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { + const result = this.dnd.onDragOver(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); + const didChangeAutoExpandNode = this.autoExpandNode !== targetNode; - if (options.multipleSelectionController) { - const msc = options.multipleSelectionController; - multipleSelectionController = { + if (didChangeAutoExpandNode) { + this.autoExpandDisposable.dispose(); + this.autoExpandNode = targetNode; + } + + if (typeof targetNode === 'undefined') { + return result; + } + + if (didChangeAutoExpandNode && typeof result !== 'boolean' && result.autoExpand) { + this.autoExpandDisposable = disposableTimeout(() => { + const model = this.modelProvider(); + const ref = model.getNodeLocation(targetNode); + + if (model.isCollapsed(ref)) { + model.setCollapsed(ref, false); + } + + this.autoExpandNode = undefined; + }, 500); + } + + if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined') { + if (!raw) { + const accept = typeof result === 'boolean' ? result : result.accept; + const effect = typeof result === 'boolean' ? undefined : result.effect; + return { accept, effect, feedback: [targetIndex!] }; + } + + return result; + } + + if (result.bubble === TreeDragOverBubble.Up) { + const parentNode = targetNode.parent; + const model = this.modelProvider(); + const parentIndex = parentNode && model.getListIndex(model.getNodeLocation(parentNode)); + + return this.onDragOver(data, parentNode, parentIndex, originalEvent, false); + } + + const model = this.modelProvider(); + const ref = model.getNodeLocation(targetNode); + const start = model.getListIndex(ref); + const length = model.getListRenderCount(ref); + + return { ...result, feedback: range(start, start + length) }; + } + + drop(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + this.autoExpandDisposable.dispose(); + this.autoExpandNode = undefined; + + this.dnd.drop(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); + } +} + +function asListOptions(modelProvider: () => ITreeModel, options?: IAbstractTreeOptions): IListOptions> | undefined { + return options && { + ...options, + identityProvider: options.identityProvider && { + getId(el) { + return options.identityProvider!.getId(el.element); + } + }, + dnd: options.dnd && new TreeNodeListDragAndDrop(modelProvider, options.dnd), + multipleSelectionController: options.multipleSelectionController && { isSelectionSingleChangeEvent(e) { - return msc.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); + return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); }, isSelectionRangeChangeEvent(e) { - return msc.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); + return options.multipleSelectionController!.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); } - }; - } - - let accessibilityProvider: IAccessibilityProvider | undefined = undefined; - - if (options.accessibilityProvider) { - const ap = options.accessibilityProvider; - accessibilityProvider = { + }, + accessibilityProvider: options.accessibilityProvider && { getAriaLabel(e) { - return ap.getAriaLabel(e.element); + return options.accessibilityProvider!.getAriaLabel(e.element); + }, + getAriaLevel(node) { + return node.depth; } - }; - } - - return { - ...options, - identityProvider, - multipleSelectionController, - accessibilityProvider + }, + keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && { + ...options.keyboardNavigationLabelProvider, + getKeyboardNavigationLabel(node) { + return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(node.element); + } + }, + enableKeyboardNavigation: options.simpleKeyboardNavigation, + ariaSetProvider: { + getSetSize(node) { + return node.parent!.visibleChildrenCount; + }, + getPosInSet(node) { + return node.visibleChildIndex + 1; + } + } }; } @@ -74,56 +178,90 @@ export class ComposedTreeDelegate implements IListV hasDynamicHeight(element: N): boolean { return !!this.delegate.hasDynamicHeight && this.delegate.hasDynamicHeight(element.element); } + + setDynamicHeight(element: N, height: number): void { + if (this.delegate.setDynamicHeight) { + this.delegate.setDynamicHeight(element.element, height); + } + } } interface ITreeListTemplateData { - twistie: HTMLElement; - templateData: T; + readonly container: HTMLElement; + readonly twistie: HTMLElement; + readonly templateData: T; +} + +interface ITreeRendererOptions { + readonly indent?: number; } class TreeRenderer implements IListRenderer, ITreeListTemplateData> { + private static DefaultIndent = 8; + readonly templateId: string; private renderedElements = new Map>(); private renderedNodes = new Map, ITreeListTemplateData>(); + private indent: number = TreeRenderer.DefaultIndent; private disposables: IDisposable[] = []; constructor( private renderer: ITreeRenderer, - onDidChangeCollapseState: Event> + onDidChangeCollapseState: Event>, + options: ITreeRendererOptions = {} ) { this.templateId = renderer.templateId; + this.updateOptions(options); - onDidChangeCollapseState(this.onDidChangeNodeTwistieState, this, this.disposables); + Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables); if (renderer.onDidChangeTwistieState) { renderer.onDidChangeTwistieState(this.onDidChangeTwistieState, this, this.disposables); } } + updateOptions(options: ITreeRendererOptions = {}): void { + if (typeof options.indent !== 'undefined') { + this.indent = clamp(options.indent, 0, 40); + } + + this.renderedNodes.forEach((templateData, node) => { + templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`; + }); + } + renderTemplate(container: HTMLElement): ITreeListTemplateData { const el = append(container, $('.monaco-tl-row')); const twistie = append(el, $('.monaco-tl-twistie')); const contents = append(el, $('.monaco-tl-contents')); const templateData = this.renderer.renderTemplate(contents); - return { twistie, templateData }; + return { container, twistie, templateData }; } - renderElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData): void { - this.renderedNodes.set(node, templateData); - this.renderedElements.set(node.element, node); + renderElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, dynamicHeightProbing?: boolean): void { + if (!dynamicHeightProbing) { + this.renderedNodes.set(node, templateData); + this.renderedElements.set(node.element, node); + } - templateData.twistie.style.width = `${10 + node.depth * 10}px`; - this.renderTwistie(node, templateData.twistie); + const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; + templateData.twistie.style.marginLeft = `${indent}px`; + this.update(node, templateData); - this.renderer.renderElement(node, index, templateData.templateData); + this.renderer.renderElement(node, index, templateData.templateData, dynamicHeightProbing); } - disposeElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData): void { - this.renderer.disposeElement(node, index, templateData.templateData); - this.renderedNodes.delete(node); - this.renderedElements.set(node.element); + disposeElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, dynamicHeightProbing?: boolean): void { + if (this.renderer.disposeElement) { + this.renderer.disposeElement(node, index, templateData.templateData, dynamicHeightProbing); + } + + if (!dynamicHeightProbing) { + this.renderedNodes.delete(node); + this.renderedElements.delete(node.element); + } } disposeTemplate(templateData: ITreeListTemplateData): void { @@ -147,20 +285,22 @@ class TreeRenderer implements IListRenderer, twistieElement: HTMLElement) { - if (this.renderer.renderTwistie && this.renderer.renderTwistie(node.element, twistieElement)) { - return; + private update(node: ITreeNode, templateData: ITreeListTemplateData) { + if (this.renderer.renderTwistie) { + this.renderer.renderTwistie(node.element, templateData.twistie); } - TreeRenderer.renderDefaultTwistie(node, twistieElement); - } + toggleClass(templateData.twistie, 'collapsible', node.collapsible); + toggleClass(templateData.twistie, 'collapsed', node.collapsible && node.collapsed); - private static renderDefaultTwistie(node: ITreeNode, twistie: HTMLElement): void { - toggleClass(twistie, 'collapsible', node.collapsible); - toggleClass(twistie, 'collapsed', node.collapsed); + if (node.collapsible) { + templateData.container.setAttribute('aria-expanded', String(!node.collapsed)); + } else { + templateData.container.removeAttribute('aria-expanded'); + } } dispose(): void { @@ -170,59 +310,766 @@ class TreeRenderer implements IListRenderer implements ITreeFilter, IDisposable { + + private _totalCount = 0; + get totalCount(): number { return this._totalCount; } + private _matchCount = 0; + get matchCount(): number { return this._matchCount; } + + private _pattern: string; + private _lowercasePattern: string; + private disposables: IDisposable[] = []; + + set pattern(pattern: string) { + this._pattern = pattern; + this._lowercasePattern = pattern.toLowerCase(); + } + + constructor( + private tree: AbstractTree, + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider, + private _filter?: ITreeFilter + ) { + tree.onWillRefilter(this.reset, this, this.disposables); + } + + filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { + if (this._filter) { + const result = this._filter.filter(element, parentVisibility); + + if (this.tree.options.simpleKeyboardNavigation) { + return result; + } + + let visibility: TreeVisibility; + + if (typeof result === 'boolean') { + visibility = result ? TreeVisibility.Visible : TreeVisibility.Hidden; + } else if (isFilterResult(result)) { + visibility = getVisibleState(result.visibility); + } else { + visibility = result; + } + + if (visibility === TreeVisibility.Hidden) { + return false; + } + } + + this._totalCount++; + + if (this.tree.options.simpleKeyboardNavigation || !this._pattern) { + this._matchCount++; + return { data: FuzzyScore.Default, visibility: true }; + } + + const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(element); + const labelStr = label && label.toString(); + + if (typeof labelStr === 'undefined') { + return { data: FuzzyScore.Default, visibility: true }; + } + + const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, labelStr, labelStr.toLowerCase(), 0, true); + + if (!score) { + if (this.tree.options.filterOnType) { + return TreeVisibility.Recurse; + } else { + return { data: FuzzyScore.Default, visibility: true }; + } + + // DEMO: smarter filter ? + // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + } + + this._matchCount++; + return { data: score, visibility: true }; + } + + private reset(): void { + this._totalCount = 0; + this._matchCount = 0; + } + + dispose(): void { + this.disposables = dispose(this.disposables); + } +} + +class TypeFilterController implements IDisposable { + + private _enabled = false; + get enabled(): boolean { return this._enabled; } + + private _pattern = ''; + get pattern(): string { return this._pattern; } + + private _filterOnType: boolean; + get filterOnType(): boolean { return this._filterOnType; } + + private _empty: boolean; + get empty(): boolean { return this._empty; } + + private _onDidChangeEmptyState = new Emitter(); + readonly onDidChangeEmptyState: Event = Event.latch(this._onDidChangeEmptyState.event); + + private positionClassName = 'ne'; + private domNode: HTMLElement; + private messageDomNode: HTMLElement; + private labelDomNode: HTMLElement; + private filterOnTypeDomNode: HTMLInputElement; + private clearDomNode: HTMLElement; + private keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; + + private automaticKeyboardNavigation = true; + private triggered = false; + + private _onDidChangePattern = new Emitter(); + readonly onDidChangePattern = this._onDidChangePattern.event; + + private enabledDisposables: IDisposable[] = []; + private disposables: IDisposable[] = []; + + constructor( + private tree: AbstractTree, + model: ITreeModel, + private view: List>, + private filter: TypeFilter, + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider + ) { + this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`); + this.domNode.draggable = true; + domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); + + this.messageDomNode = append(view.getHTMLElement(), $(`.monaco-list-type-filter-message`)); + + this.labelDomNode = append(this.domNode, $('span.label')); + const controls = append(this.domNode, $('.controls')); + + this._filterOnType = !!tree.options.filterOnType; + this.filterOnTypeDomNode = append(controls, $('input.filter')); + this.filterOnTypeDomNode.type = 'checkbox'; + this.filterOnTypeDomNode.checked = this._filterOnType; + this.filterOnTypeDomNode.tabIndex = -1; + this.updateFilterOnTypeTitle(); + domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); + + this.clearDomNode = append(controls, $('button.clear')); + this.clearDomNode.tabIndex = -1; + this.clearDomNode.title = localize('clear', "Clear"); + + this.keyboardNavigationEventFilter = tree.options.keyboardNavigationEventFilter; + + model.onDidSplice(this.onDidSpliceModel, this, this.disposables); + this.updateOptions(tree.options); + } + + updateOptions(options: IAbstractTreeOptions): void { + if (options.simpleKeyboardNavigation) { + this.disable(); + } else { + this.enable(); + } + + if (typeof options.filterOnType !== 'undefined') { + this._filterOnType = !!options.filterOnType; + this.filterOnTypeDomNode.checked = this._filterOnType; + } + + if (typeof options.automaticKeyboardNavigation !== 'undefined') { + this.automaticKeyboardNavigation = options.automaticKeyboardNavigation; + } + + this.tree.refilter(); + this.render(); + + if (!this.automaticKeyboardNavigation) { + this.onEventOrInput(''); + } + } + + toggle(): void { + this.triggered = !this.triggered; + + if (!this.triggered) { + this.onEventOrInput(''); + } + } + + private enable(): void { + if (this._enabled) { + return; + } + + const isPrintableCharEvent = this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); + const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) + .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) + .map(e => new StandardKeyboardEvent(e)) + .filter(this.keyboardNavigationEventFilter || (() => true)) + .filter(() => this.automaticKeyboardNavigation || this.triggered) + .filter(e => isPrintableCharEvent(e) || ((this.pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? (e.altKey && !e.metaKey) : e.ctrlKey) && !e.shiftKey))) + .forEach(e => { e.stopPropagation(); e.preventDefault(); }) + .event; + + const onClear = domEvent(this.clearDomNode, 'click'); + + Event.chain(Event.any(onKeyDown, onClear)) + .event(this.onEventOrInput, this, this.enabledDisposables); + + this.filter.pattern = ''; + this.tree.refilter(); + this.render(); + this._enabled = true; + this.triggered = false; + } + + private disable(): void { + if (!this._enabled) { + return; + } + + this.domNode.remove(); + this.enabledDisposables = dispose(this.enabledDisposables); + this.tree.refilter(); + this.render(); + this._enabled = false; + this.triggered = false; + } + + private onEventOrInput(e: MouseEvent | StandardKeyboardEvent | string): void { + if (typeof e === 'string') { + this.onInput(e); + } else if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey))) { + this.onInput(''); + } else if (e.keyCode === KeyCode.Backspace) { + this.onInput(this.pattern.length === 0 ? '' : this.pattern.substr(0, this.pattern.length - 1)); + } else { + this.onInput(this.pattern + e.browserEvent.key); + } + } + + private onInput(pattern: string): void { + const container = this.view.getHTMLElement(); + + if (pattern && !this.domNode.parentElement) { + container.append(this.domNode); + } else if (!pattern && this.domNode.parentElement) { + this.domNode.remove(); + this.tree.domFocus(); + } + + this._pattern = pattern; + this._onDidChangePattern.fire(pattern); + + this.filter.pattern = pattern; + this.tree.refilter(); + + if (pattern) { + this.tree.focusNext(0, true, undefined, node => !FuzzyScore.isDefault(node.filterData as any as FuzzyScore)); + } + + const focus = this.tree.getFocus(); + + if (focus.length > 0) { + const element = focus[0]; + + if (this.tree.getRelativeTop(element) === null) { + this.tree.reveal(element, 0.5); + } + } + + this.render(); + + if (!pattern) { + this.triggered = false; + } + } + + private onDragStart(): void { + const container = this.view.getHTMLElement(); + const { left } = getDomNodePagePosition(container); + const containerWidth = container.clientWidth; + const midContainerWidth = containerWidth / 2; + const width = this.domNode.clientWidth; + const disposables: IDisposable[] = []; + let positionClassName = this.positionClassName; + + const updatePosition = () => { + switch (positionClassName) { + case 'nw': + this.domNode.style.top = `4px`; + this.domNode.style.left = `4px`; + break; + case 'ne': + this.domNode.style.top = `4px`; + this.domNode.style.left = `${containerWidth - width - 6}px`; + break; + } + }; + + const onDragOver = (event: DragEvent) => { + event.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) + + const x = event.screenX - left; + if (event.dataTransfer) { + event.dataTransfer.dropEffect = 'none'; + } + + if (x < midContainerWidth) { + positionClassName = 'nw'; + } else { + positionClassName = 'ne'; + } + + updatePosition(); + }; + + const onDragEnd = () => { + this.positionClassName = positionClassName; + this.domNode.className = `monaco-list-type-filter ${this.positionClassName}`; + this.domNode.style.top = null; + this.domNode.style.left = null; + + dispose(disposables); + }; + + updatePosition(); + removeClass(this.domNode, positionClassName); + + addClass(this.domNode, 'dragging'); + disposables.push(toDisposable(() => removeClass(this.domNode, 'dragging'))); + + domEvent(document, 'dragover')(onDragOver, null, disposables); + domEvent(this.domNode, 'dragend')(onDragEnd, null, disposables); + + StaticDND.CurrentDragAndDropData = new DragAndDropData('vscode-ui'); + disposables.push(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); + } + + private onDidSpliceModel(): void { + if (!this._enabled || this.pattern.length === 0) { + return; + } + + this.tree.refilter(); + this.render(); + } + + private onDidChangeFilterOnType(): void { + this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked }); + this.tree.refilter(); + this.tree.domFocus(); + this.render(); + this.updateFilterOnTypeTitle(); + } + + private updateFilterOnTypeTitle(): void { + if (this.filterOnType) { + this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); + } else { + this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); + } + } + + private render(): void { + const noMatches = this.filter.totalCount > 0 && this.filter.matchCount === 0; + + if (this.pattern && this.tree.options.filterOnType && noMatches) { + this.messageDomNode.textContent = localize('empty', "No elements found"); + this._empty = true; + } else { + this.messageDomNode.innerHTML = ''; + this._empty = false; + } + + toggleClass(this.domNode, 'no-matches', noMatches); + this.domNode.title = localize('found', "Matched {0} out of {1} elements", this.filter.matchCount, this.filter.totalCount); + this.labelDomNode.textContent = this.pattern.length > 16 ? '…' + this.pattern.substr(this.pattern.length - 16) : this.pattern; + + this._onDidChangeEmptyState.fire(this._empty); + } + + shouldAllowFocus(node: ITreeNode): boolean { + if (!this.enabled || !this.pattern || this.filterOnType) { + return true; + } + + if (this.filter.totalCount > 0 && this.filter.matchCount <= 1) { + return true; + } + + return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); + } + + dispose() { + this.disable(); + this._onDidChangePattern.dispose(); + this.disposables = dispose(this.disposables); + } +} + function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } -export interface ITreeOptions extends IListOptions, ITreeModelOptions { } -export interface ITreeEvent extends IListEvent> { } -export interface ITreeContextMenuEvent extends IListContextMenuEvent> { } +function asTreeEvent(event: IListEvent>): ITreeEvent { + return { + elements: event.elements.map(node => node.element), + browserEvent: event.browserEvent + }; +} + +function asTreeMouseEvent(event: IListMouseEvent>): ITreeMouseEvent { + return { + browserEvent: event.browserEvent, + element: event.element ? event.element.element : null + }; +} + +function asTreeContextMenuEvent(event: IListContextMenuEvent>): ITreeContextMenuEvent { + return { + element: event.element ? event.element.element : null, + browserEvent: event.browserEvent, + anchor: event.anchor + }; +} + +export interface IKeyboardNavigationEventFilter { + (e: StandardKeyboardEvent): boolean; +} + +export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { + readonly automaticKeyboardNavigation?: boolean; + readonly simpleKeyboardNavigation?: boolean; + readonly filterOnType?: boolean; + readonly openOnSingleClick?: boolean; +} + +export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { + readonly collapseByDefault?: boolean; // defaults to false + readonly filter?: ITreeFilter; + readonly dnd?: ITreeDragAndDrop; + readonly autoExpandSingleChildren?: boolean; + readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; + readonly expandOnlyOnTwistieClick?: boolean | ((e: T) => boolean); +} + +function dfs(node: ITreeNode, fn: (node: ITreeNode) => void): void { + fn(node); + node.children.forEach(child => dfs(child, fn)); +} + +/** + * The trait concept needs to exist at the tree level, because collapsed + * tree nodes will not be known by the list. + */ +class Trait { + + private nodes: ITreeNode[] = []; + private elements: T[] | undefined; + + private _onDidChange = new Emitter>(); + readonly onDidChange = this._onDidChange.event; + + private _nodeSet: Set> | undefined; + private get nodeSet(): Set> { + if (!this._nodeSet) { + this._nodeSet = this.createNodeSet(); + } + + return this._nodeSet; + } + + constructor(private identityProvider?: IIdentityProvider) { } + + set(nodes: ITreeNode[], browserEvent?: UIEvent): void { + if (equals(this.nodes, nodes)) { + return; + } + + this._set(nodes, false, browserEvent); + } + + private _set(nodes: ITreeNode[], silent: boolean, browserEvent?: UIEvent): void { + this.nodes = [...nodes]; + this.elements = undefined; + this._nodeSet = undefined; + + if (!silent) { + const that = this; + this._onDidChange.fire({ get elements() { return that.get(); }, browserEvent }); + } + } + + get(): T[] { + if (!this.elements) { + this.elements = this.nodes.map(node => node.element); + } + + return [...this.elements]; + } + + has(node: ITreeNode): boolean { + return this.nodeSet.has(node); + } + + onDidModelSplice({ insertedNodes, deletedNodes }: ITreeModelSpliceEvent): void { + if (!this.identityProvider) { + const set = this.createNodeSet(); + const visit = (node: ITreeNode) => set.delete(node); + deletedNodes.forEach(node => dfs(node, visit)); + this.set(values(set)); + return; + } + + const deletedNodesIdSet = new Set(); + const deletedNodesVisitor = (node: ITreeNode) => deletedNodesIdSet.add(this.identityProvider!.getId(node.element).toString()); + deletedNodes.forEach(node => dfs(node, deletedNodesVisitor)); + + const insertedNodesMap = new Map>(); + const insertedNodesVisitor = (node: ITreeNode) => insertedNodesMap.set(this.identityProvider!.getId(node.element).toString(), node); + insertedNodes.forEach(node => dfs(node, insertedNodesVisitor)); + + const nodes: ITreeNode[] = []; + let silent = true; + + for (const node of this.nodes) { + const id = this.identityProvider.getId(node.element).toString(); + const wasDeleted = deletedNodesIdSet.has(id); + + if (!wasDeleted) { + nodes.push(node); + } else { + const insertedNode = insertedNodesMap.get(id); + + if (insertedNode) { + nodes.push(insertedNode); + } else { + silent = false; + } + } + } + + this._set(nodes, silent); + } + + private createNodeSet(): Set> { + const set = new Set>(); + + for (const node of this.nodes) { + set.add(node); + } + + return set; + } +} + +class TreeNodeListMouseController extends MouseController> { + + constructor(list: TreeNodeList, private tree: AbstractTree) { + super(list); + } + + protected onPointer(e: IListMouseEvent>): void { + if (isInputElement(e.browserEvent.target as HTMLElement)) { + return; + } + + const node = e.element; + + if (!node) { + return super.onPointer(e); + } + + if (this.isSelectionRangeChangeEvent(e) || this.isSelectionSingleChangeEvent(e)) { + return super.onPointer(e); + } + + const onTwistie = hasClass(e.browserEvent.target as HTMLElement, 'monaco-tl-twistie'); + + if (!this.tree.openOnSingleClick && e.browserEvent.detail !== 2 && !onTwistie) { + return super.onPointer(e); + } + + let expandOnlyOnTwistieClick = false; + + if (typeof this.tree.expandOnlyOnTwistieClick === 'function') { + expandOnlyOnTwistieClick = this.tree.expandOnlyOnTwistieClick(node.element); + } else { + expandOnlyOnTwistieClick = !!this.tree.expandOnlyOnTwistieClick; + } + + if (expandOnlyOnTwistieClick && !onTwistie) { + return super.onPointer(e); + } + + const model = ((this.tree as any).model as ITreeModel); // internal + const location = model.getNodeLocation(node); + const recursive = e.browserEvent.altKey; + model.setCollapsed(location, undefined, recursive); + + if (expandOnlyOnTwistieClick && onTwistie) { + return; + } + + super.onPointer(e); + } +} + +interface ITreeNodeListOptions extends IListOptions> { + readonly tree: AbstractTree; +} + +/** + * We use this List subclass to restore selection and focus as nodes + * get rendered in the list, possibly due to a node expand() call. + */ +class TreeNodeList extends List> { + + constructor( + container: HTMLElement, + virtualDelegate: IListVirtualDelegate>, + renderers: IListRenderer[], + private focusTrait: Trait, + private selectionTrait: Trait, + options: ITreeNodeListOptions + ) { + super(container, virtualDelegate, renderers, options); + } + + protected createMouseController(options: ITreeNodeListOptions): MouseController> { + return new TreeNodeListMouseController(this, options.tree); + } + + splice(start: number, deleteCount: number, elements: ITreeNode[] = []): void { + super.splice(start, deleteCount, elements); + + if (elements.length === 0) { + return; + } + + const additionalFocus: number[] = []; + const additionalSelection: number[] = []; + + elements.forEach((node, index) => { + if (this.focusTrait.has(node)) { + additionalFocus.push(start + index); + } + + if (this.selectionTrait.has(node)) { + additionalSelection.push(start + index); + } + }); + + if (additionalFocus.length > 0) { + super.setFocus(distinctES6([...super.getFocus(), ...additionalFocus])); + } + + if (additionalSelection.length > 0) { + super.setSelection(distinctES6([...super.getSelection(), ...additionalSelection])); + } + } + + setFocus(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void { + super.setFocus(indexes, browserEvent); + + if (!fromAPI) { + this.focusTrait.set(indexes.map(i => this.element(i)), browserEvent); + } + } + + setSelection(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void { + super.setSelection(indexes, browserEvent); + + if (!fromAPI) { + this.selectionTrait.set(indexes.map(i => this.element(i)), browserEvent); + } + } +} export abstract class AbstractTree implements IDisposable { - private view: List>; + protected view: TreeNodeList; + private renderers: TreeRenderer[]; protected model: ITreeModel; + private focus: Trait; + private selection: Trait; + private eventBufferer = new EventBufferer(); + private typeFilterController?: TypeFilterController; + private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected disposables: IDisposable[] = []; - readonly onDidChangeCollapseState: Event>; - readonly onDidChangeRenderNodeCount: Event>; - readonly onDidChangeFocus: Event>; - readonly onDidChangeSelection: Event>; + get onDidScroll(): Event { return this.view.onDidScroll; } - readonly onContextMenu: Event>; + get onDidChangeFocus(): Event> { return this.eventBufferer.wrapEvent(this.focus.onDidChange); } + get onDidChangeSelection(): Event> { return this.eventBufferer.wrapEvent(this.selection.onDidChange); } + get onDidOpen(): Event> { return Event.map(this.view.onDidOpen, asTreeEvent); } + + get onMouseClick(): Event> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); } + get onMouseDblClick(): Event> { return Event.map(this.view.onMouseDblClick, asTreeMouseEvent); } + get onContextMenu(): Event> { return Event.map(this.view.onContextMenu, asTreeContextMenuEvent); } + + get onKeyDown(): Event { return this.view.onKeyDown; } + get onKeyUp(): Event { return this.view.onKeyUp; } + get onKeyPress(): Event { return this.view.onKeyPress; } get onDidFocus(): Event { return this.view.onDidFocus; } get onDidBlur(): Event { return this.view.onDidBlur; } + + get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } + get onDidChangeRenderNodeCount(): Event> { return this.model.onDidChangeRenderNodeCount; } + + private _onWillRefilter = new Emitter(); + readonly onWillRefilter: Event = this._onWillRefilter.event; + + get filterOnType(): boolean { return !!this._options.filterOnType; } + get onDidChangeTypeFilterPattern(): Event { return this.typeFilterController ? this.typeFilterController.onDidChangePattern : Event.None; } + + // Options TODO@joao expose options only, not Optional<> + get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; } + get expandOnlyOnTwistieClick(): boolean | ((e: T) => boolean) { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } + + private _onDidUpdateOptions = new Emitter>(); + readonly onDidUpdateOptions: Event> = this._onDidUpdateOptions.event; + get onDidDispose(): Event { return this.view.onDidDispose; } constructor( container: HTMLElement, delegate: IListVirtualDelegate, - renderers: ITreeRenderer[], - options: ITreeOptions = {} + renderers: ITreeRenderer[], + private _options: IAbstractTreeOptions = {} ) { const treeDelegate = new ComposedTreeDelegate>(delegate); - const onDidChangeCollapseStateRelay = new Relay>(); - const treeRenderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event)); - this.disposables.push(...treeRenderers); + const onDidChangeCollapseStateRelay = new Relay>(); + this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, _options)); + this.disposables.push(...this.renderers); - this.view = new List(container, treeDelegate, treeRenderers, createComposedTreeListOptions(options)); - this.onDidChangeFocus = this.view.onFocusChange; - this.onDidChangeSelection = this.view.onSelectionChange; - this.onContextMenu = this.view.onContextMenu; + let filter: TypeFilter | undefined; - this.model = this.createModel(this.view, options); - onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; - this.onDidChangeCollapseState = this.model.onDidChangeCollapseState; - this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount; - - if (options.mouseSupport !== false) { - this.view.onMouseClick(this.onMouseClick, this, this.disposables); + if (_options.keyboardNavigationLabelProvider) { + filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as any as ITreeFilter); + _options = { ..._options, filter: filter as ITreeFilter }; // TODO need typescript help here + this.disposables.push(filter); } - if (options.keyboardSupport !== false) { - const onKeyDown = chain(this.view.onKeyDown) + this.focus = new Trait(_options.identityProvider); + this.selection = new Trait(_options.identityProvider); + this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, { ...asListOptions(() => this.model, _options), tree: this }); + + this.model = this.createModel(this.view, _options); + onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; + + this.model.onDidSplice(e => { + this.focus.onDidModelSplice(e); + this.selection.onDidModelSplice(e); + }, null, this.disposables); + + if (_options.keyboardSupport !== false) { + const onKeyDown = Event.chain(this.view.onKeyDown) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -230,6 +1077,45 @@ export abstract class AbstractTree implements IDisposable onKeyDown.filter(e => e.keyCode === KeyCode.RightArrow).on(this.onRightArrow, this, this.disposables); onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } + + if (_options.keyboardNavigationLabelProvider) { + this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); + this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node); + this.disposables.push(this.typeFilterController!); + } + } + + updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void { + this._options = { ...this._options, ...optionsUpdate }; + + for (const renderer of this.renderers) { + renderer.updateOptions(optionsUpdate); + } + + this.view.updateOptions({ + enableKeyboardNavigation: this._options.simpleKeyboardNavigation, + automaticKeyboardNavigation: this._options.automaticKeyboardNavigation + }); + + if (this.typeFilterController) { + this.typeFilterController.updateOptions(this._options); + } + + this._onDidUpdateOptions.fire(this._options); + } + + get options(): IAbstractTreeOptions { + return this._options; + } + + updateWidth(element: TRef): void { + const index = this.model.getListIndex(element); + + if (index === -1) { + return; + } + + this.view.updateWidth(index); } // Widget @@ -238,16 +1124,62 @@ export abstract class AbstractTree implements IDisposable return this.view.getHTMLElement(); } + get contentHeight(): number { + if (this.typeFilterController && this.typeFilterController.filterOnType && this.typeFilterController.empty) { + return 100; + } + + return this.view.contentHeight; + } + + get onDidChangeContentHeight(): Event { + let result = this.view.onDidChangeContentHeight; + + if (this.typeFilterController) { + result = Event.any(result, Event.map(this.typeFilterController.onDidChangeEmptyState, () => this.contentHeight)); + } + + return result; + } + + get scrollTop(): number { + return this.view.scrollTop; + } + + set scrollTop(scrollTop: number) { + this.view.scrollTop = scrollTop; + } + + get scrollHeight(): number { + return this.view.scrollHeight; + } + + get renderHeight(): number { + return this.view.renderHeight; + } + + get firstVisibleElement(): T { + const index = this.view.firstVisibleIndex; + const node = this.view.element(index); + return node.element; + } + + get lastVisibleElement(): T { + const index = this.view.lastVisibleIndex; + const node = this.view.element(index); + return node.element; + } + domFocus(): void { this.view.domFocus(); } - layout(height?: number): void { - this.view.layout(height); + isDOMFocused(): boolean { + return this.getHTMLElement() === document.activeElement; } - layoutWidth(width: number): void { - this.view.layoutWidth(width); + layout(height?: number, width?: number): void { + this.view.layout(height, width); } style(styles: IListStyles): void { @@ -256,16 +1188,12 @@ export abstract class AbstractTree implements IDisposable // Tree navigation - getParentElement(location: TRef | null = null): T | null { + getParentElement(location: TRef): T { return this.model.getParentElement(location); } - getFirstElementChild(location: TRef | null = null): T | null { - return this.model.getFirstChildElement(location); - } - - getLastElementAncestor(location: TRef | null = null): T | null { - return this.model.getLastAncestorElement(location); + getFirstElementChild(location: TRef): T | undefined { + return this.model.getFirstElementChild(location); } // Tree @@ -274,85 +1202,109 @@ export abstract class AbstractTree implements IDisposable return this.model.getNode(location); } - collapse(location: TRef): boolean { - return this.model.setCollapsed(location, true); + collapse(location: TRef, recursive: boolean = false): boolean { + return this.model.setCollapsed(location, true, recursive); } - expand(location: TRef): boolean { - return this.model.setCollapsed(location, false); + expand(location: TRef, recursive: boolean = false): boolean { + return this.model.setCollapsed(location, false, recursive); } - toggleCollapsed(location: TRef): void { - this.model.toggleCollapsed(location); + toggleCollapsed(location: TRef, recursive: boolean = false): boolean { + return this.model.setCollapsed(location, undefined, recursive); + } + + expandAll(): void { + this.model.setCollapsed(this.model.rootRef, false, true); } collapseAll(): void { - this.model.collapseAll(); + this.model.setCollapsed(this.model.rootRef, true, true); + } + + isCollapsible(location: TRef): boolean { + return this.model.isCollapsible(location); } isCollapsed(location: TRef): boolean { return this.model.isCollapsed(location); } - isExpanded(location: TRef): boolean { - return !this.isCollapsed(location); + toggleKeyboardNavigation(): void { + this.view.toggleKeyboardNavigation(); + + if (this.typeFilterController) { + this.typeFilterController.toggle(); + } } refilter(): void { + this._onWillRefilter.fire(undefined); this.model.refilter(); } setSelection(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setSelection(indexes, browserEvent); + const nodes = elements.map(e => this.model.getNode(e)); + this.selection.set(nodes, browserEvent); + + const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i > -1); + this.view.setSelection(indexes, browserEvent, true); } getSelection(): T[] { - const nodes = this.view.getSelectedElements(); - return nodes.map(n => n.element); + return this.selection.get(); } setFocus(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setFocus(indexes, browserEvent); + const nodes = elements.map(e => this.model.getNode(e)); + this.focus.set(nodes, browserEvent); + + const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i > -1); + this.view.setFocus(indexes, browserEvent, true); } - focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusNext(n, loop, browserEvent); + focusNext(n = 1, loop = false, browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusNext(n, loop, browserEvent, filter); } - focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusPrevious(n, loop, browserEvent); + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusPrevious(n, loop, browserEvent, filter); } - focusNextPage(browserEvent?: UIEvent): void { - this.view.focusNextPage(browserEvent); + focusNextPage(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusNextPage(browserEvent, filter); } - focusPreviousPage(browserEvent?: UIEvent): void { - this.view.focusPreviousPage(browserEvent); + focusPreviousPage(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusPreviousPage(browserEvent, filter); } - focusLast(browserEvent?: UIEvent): void { - this.view.focusLast(browserEvent); + focusLast(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusLast(browserEvent, filter); } - focusFirst(browserEvent?: UIEvent): void { - this.view.focusFirst(browserEvent); + focusFirst(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusFirst(browserEvent, filter); } getFocus(): T[] { - const nodes = this.view.getFocusedElements(); - return nodes.map(n => n.element); + return this.focus.get(); } - open(elements: TRef[]): void { + open(elements: TRef[], browserEvent?: UIEvent): void { const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.open(indexes); + this.view.open(indexes, browserEvent); } reveal(location: TRef, relativeTop?: number): void { + this.model.expandTo(location); + const index = this.model.getListIndex(location); + + if (index === -1) { + return; + } + this.view.reveal(index, relativeTop); } @@ -362,19 +1314,15 @@ export abstract class AbstractTree implements IDisposable */ getRelativeTop(location: TRef): number | null { const index = this.model.getListIndex(location); + + if (index === -1) { + return null; + } + return this.view.getRelativeTop(index); } - private onMouseClick(e: IListMouseEvent>): void { - const node = e.element; - - if (!node) { - return; - } - - const location = this.model.getNodeLocation(node); - this.model.toggleCollapsed(location); - } + // List private onLeftArrow(e: StandardKeyboardEvent): void { e.preventDefault(); @@ -419,7 +1367,7 @@ export abstract class AbstractTree implements IDisposable const didChange = this.model.setCollapsed(location, false); if (!didChange) { - if (node.children.length === 0) { + if (!node.children.some(child => child.visible)) { return; } @@ -443,13 +1391,81 @@ export abstract class AbstractTree implements IDisposable const node = nodes[0]; const location = this.model.getNodeLocation(node); - this.model.toggleCollapsed(location); + const recursive = e.browserEvent.altKey; + + this.model.setCollapsed(location, undefined, recursive); } - protected abstract createModel(view: ISpliceable>, options: ITreeOptions): ITreeModel; + protected abstract createModel(view: ISpliceable>, options: IAbstractTreeOptions): ITreeModel; + + navigate(start?: TRef): ITreeNavigator { + return new TreeNavigator(this.view, this.model, start); + } dispose(): void { this.disposables = dispose(this.disposables); this.view.dispose(); } -} \ No newline at end of file +} + +interface ITreeNavigatorView, TFilterData> { + readonly length: number; + element(index: number): ITreeNode; +} + +class TreeNavigator, TFilterData, TRef> implements ITreeNavigator { + + private index: number; + + constructor(private view: ITreeNavigatorView, private model: ITreeModel, start?: TRef) { + if (start) { + this.index = this.model.getListIndex(start); + } else { + this.index = -1; + } + } + + current(): T | null { + if (this.index < 0 || this.index >= this.view.length) { + return null; + } + + return this.view.element(this.index).element; + } + + previous(): T | null { + this.index--; + return this.current(); + } + + next(): T | null { + this.index++; + return this.current(); + } + + parent(): T | null { + if (this.index < 0 || this.index >= this.view.length) { + return null; + } + + const node = this.view.element(this.index); + + if (!node.parent) { + this.index = -1; + return this.current(); + } + + this.index = this.model.getListIndex(this.model.getNodeLocation(node.parent)); + return this.current(); + } + + first(): T | null { + this.index = 0; + return this.current(); + } + + last(): T | null { + this.index = this.view.length - 1; + return this.current(); + } +} diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts new file mode 100644 index 00000000000..050d3f49a89 --- /dev/null +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -0,0 +1,869 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; +import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; +import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; +import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; +import { Iterator } from 'vs/base/common/iterator'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; +import { toggleClass } from 'vs/base/browser/dom'; +import { values } from 'vs/base/common/map'; +import { ScrollEvent } from 'vs/base/common/scrollable'; + +interface IAsyncDataTreeNode { + element: TInput | T; + readonly parent: IAsyncDataTreeNode | null; + readonly children: IAsyncDataTreeNode[]; + readonly id?: string | null; + loading: boolean; + hasChildren: boolean; + stale: boolean; + slow: boolean; +} + +interface IAsyncDataTreeNodeRequiredProps extends Partial> { + readonly element: TInput | T; + readonly parent: IAsyncDataTreeNode | null; + readonly hasChildren: boolean; +} + +function createAsyncDataTreeNode(props: IAsyncDataTreeNodeRequiredProps): IAsyncDataTreeNode { + return { + ...props, + children: [], + loading: false, + stale: true, + slow: false + }; +} + +function isAncestor(ancestor: IAsyncDataTreeNode, descendant: IAsyncDataTreeNode): boolean { + if (!descendant.parent) { + return false; + } else if (descendant.parent === ancestor) { + return true; + } else { + return isAncestor(ancestor, descendant.parent); + } +} + +function intersects(node: IAsyncDataTreeNode, other: IAsyncDataTreeNode): boolean { + return node === other || isAncestor(node, other) || isAncestor(other, node); +} + +interface IDataTreeListTemplateData { + templateData: T; +} + +class AsyncDataTreeNodeWrapper implements ITreeNode { + + get element(): T { return this.node.element!.element as T; } + get parent(): ITreeNode | undefined { return this.node.parent && new AsyncDataTreeNodeWrapper(this.node.parent); } + get children(): ITreeNode[] { return this.node.children.map(node => new AsyncDataTreeNodeWrapper(node)); } + get depth(): number { return this.node.depth; } + get visibleChildrenCount(): number { return this.node.visibleChildrenCount; } + get visibleChildIndex(): number { return this.node.visibleChildIndex; } + get collapsible(): boolean { return this.node.collapsible; } + get collapsed(): boolean { return this.node.collapsed; } + get visible(): boolean { return this.node.visible; } + get filterData(): TFilterData | undefined { return this.node.filterData; } + + constructor(private node: ITreeNode | null, TFilterData>) { } +} + +class DataTreeRenderer implements ITreeRenderer, TFilterData, IDataTreeListTemplateData> { + + readonly templateId: string; + private renderedNodes = new Map, IDataTreeListTemplateData>(); + private disposables: IDisposable[] = []; + + constructor( + private renderer: ITreeRenderer, + readonly onDidChangeTwistieState: Event> + ) { + this.templateId = renderer.templateId; + } + + renderTemplate(container: HTMLElement): IDataTreeListTemplateData { + const templateData = this.renderer.renderTemplate(container); + return { templateData }; + } + + renderElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, dynamicHeightProbing?: boolean): void { + this.renderer.renderElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, dynamicHeightProbing); + } + + renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { + toggleClass(twistieElement, 'loading', element.slow); + return false; + } + + disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, dynamicHeightProbing?: boolean): void { + if (this.renderer.disposeElement) { + this.renderer.disposeElement(new AsyncDataTreeNodeWrapper(node), index, templateData.templateData, dynamicHeightProbing); + } + } + + disposeTemplate(templateData: IDataTreeListTemplateData): void { + this.renderer.disposeTemplate(templateData.templateData); + } + + dispose(): void { + this.renderedNodes.clear(); + this.disposables = dispose(this.disposables); + } +} + +function asTreeEvent(e: ITreeEvent>): ITreeEvent { + return { + browserEvent: e.browserEvent, + elements: e.elements.map(e => e.element as T) + }; +} + +function asTreeMouseEvent(e: ITreeMouseEvent>): ITreeMouseEvent { + return { + browserEvent: e.browserEvent, + element: e.element && e.element.element as T + }; +} + +function asTreeContextMenuEvent(e: ITreeContextMenuEvent>): ITreeContextMenuEvent { + return { + browserEvent: e.browserEvent, + element: e.element && e.element.element as T, + anchor: e.anchor + }; +} + +export enum ChildrenResolutionReason { + Refresh, + Expand +} + +export interface IChildrenResolutionEvent { + readonly element: T | null; + readonly reason: ChildrenResolutionReason; +} + +function asAsyncDataTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + return new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + return data; +} + +class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop> { + + constructor(private dnd: ITreeDragAndDrop) { } + + getDragURI(node: IAsyncDataTreeNode): string | null { + return this.dnd.getDragURI(node.element as T); + } + + getDragLabel(nodes: IAsyncDataTreeNode[]): string | undefined { + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(nodes.map(node => node.element as T)); + } + + return undefined; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + if (this.dnd.onDragStart) { + this.dnd.onDragStart(asAsyncDataTreeDragAndDropData(data), originalEvent); + } + } + + onDragOver(data: IDragAndDropData, targetNode: IAsyncDataTreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { + return this.dnd.onDragOver(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent); + } + + drop(data: IDragAndDropData, targetNode: IAsyncDataTreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + this.dnd.drop(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent); + } +} + +function asObjectTreeOptions(options?: IAsyncDataTreeOptions): IObjectTreeOptions, TFilterData> | undefined { + return options && { + ...options, + collapseByDefault: true, + identityProvider: options.identityProvider && { + getId(el) { + return options.identityProvider!.getId(el.element as T); + } + }, + dnd: options.dnd && new AsyncDataTreeNodeListDragAndDrop(options.dnd), + multipleSelectionController: options.multipleSelectionController && { + isSelectionSingleChangeEvent(e) { + return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); + }, + isSelectionRangeChangeEvent(e) { + return options.multipleSelectionController!.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); + } + }, + accessibilityProvider: options.accessibilityProvider && { + getAriaLabel(e) { + return options.accessibilityProvider!.getAriaLabel(e.element as T); + } + }, + filter: options.filter && { + filter(e, parentVisibility) { + return options.filter!.filter(e.element as T, parentVisibility); + } + }, + keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && { + getKeyboardNavigationLabel(e) { + return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(e.element as T); + } + }, + sorter: undefined, + expandOnlyOnTwistieClick: typeof options.expandOnlyOnTwistieClick === 'undefined' ? undefined : ( + typeof options.expandOnlyOnTwistieClick !== 'function' ? options.expandOnlyOnTwistieClick : ( + e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T) + ) + ), + ariaSetProvider: undefined + }; +} + +function asTreeElement(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): ITreeElement> { + let collapsed: boolean | undefined; + + if (viewStateContext && viewStateContext.viewState.expanded && node.id) { + collapsed = viewStateContext.viewState.expanded.indexOf(node.id) === -1; + } + + return { + element: node, + children: node.hasChildren ? Iterator.map(Iterator.fromArray(node.children), child => asTreeElement(child, viewStateContext)) : [], + collapsible: node.hasChildren, + collapsed + }; +} + +export interface IAsyncDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { } + +export interface IAsyncDataTreeOptions extends IAsyncDataTreeOptionsUpdate, IAbstractTreeOptions { + identityProvider?: IIdentityProvider; + sorter?: ITreeSorter; + autoExpandSingleChildren?: boolean; +} + +export interface IAsyncDataTreeViewState { + readonly focus?: string[]; + readonly selection?: string[]; + readonly expanded?: string[]; + readonly scrollTop?: number; +} + +interface IAsyncDataTreeViewStateContext { + readonly viewState: IAsyncDataTreeViewState; + readonly selection: IAsyncDataTreeNode[]; + readonly focus: IAsyncDataTreeNode[]; +} + +function dfs(node: IAsyncDataTreeNode, fn: (node: IAsyncDataTreeNode) => void): void { + fn(node); + node.children.forEach(child => dfs(child, fn)); +} + +export class AsyncDataTree implements IDisposable { + + private readonly tree: ObjectTree, TFilterData>; + private readonly root: IAsyncDataTreeNode; + private readonly nodes = new Map>(); + private readonly sorter?: ITreeSorter; + + private readonly subTreeRefreshPromises = new Map, Promise>(); + private readonly refreshPromises = new Map, CancelablePromise>(); + + private readonly identityProvider?: IIdentityProvider; + private readonly autoExpandSingleChildren: boolean; + + private readonly _onDidRender = new Emitter(); + private readonly _onDidChangeNodeSlowState = new Emitter>(); + + protected readonly disposables: IDisposable[] = []; + + get onDidScroll(): Event { return this.tree.onDidScroll; } + + get onDidChangeFocus(): Event> { return Event.map(this.tree.onDidChangeFocus, asTreeEvent); } + get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } + get onDidOpen(): Event> { return Event.map(this.tree.onDidOpen, asTreeEvent); } + + get onKeyDown(): Event { return this.tree.onKeyDown; } + get onMouseClick(): Event> { return Event.map(this.tree.onMouseClick, asTreeMouseEvent); } + get onMouseDblClick(): Event> { return Event.map(this.tree.onMouseDblClick, asTreeMouseEvent); } + get onContextMenu(): Event> { return Event.map(this.tree.onContextMenu, asTreeContextMenuEvent); } + get onDidFocus(): Event { return this.tree.onDidFocus; } + get onDidBlur(): Event { return this.tree.onDidBlur; } + + get onDidUpdateOptions(): Event { return this.tree.onDidUpdateOptions; } + + get filterOnType(): boolean { return this.tree.filterOnType; } + get openOnSingleClick(): boolean { return this.tree.openOnSingleClick; } + + get onDidDispose(): Event { return this.tree.onDidDispose; } + + constructor( + container: HTMLElement, + delegate: IListVirtualDelegate, + renderers: ITreeRenderer[], + private dataSource: IAsyncDataSource, + options: IAsyncDataTreeOptions = {} + ) { + this.identityProvider = options.identityProvider; + this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; + this.sorter = options.sorter; + + const objectTreeDelegate = new ComposedTreeDelegate>(delegate); + const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event)); + const objectTreeOptions = asObjectTreeOptions(options) || {}; + + this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); + + this.root = createAsyncDataTreeNode({ + element: undefined!, + parent: null, + hasChildren: true + }); + + if (this.identityProvider) { + this.root = { + ...this.root, + id: null + }; + } + + this.nodes.set(null, this.root); + + this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables); + } + + updateOptions(options: IAsyncDataTreeOptionsUpdate = {}): void { + this.tree.updateOptions(options); + } + + // Widget + + getHTMLElement(): HTMLElement { + return this.tree.getHTMLElement(); + } + + get contentHeight(): number { + return this.tree.contentHeight; + } + + get onDidChangeContentHeight(): Event { + return this.tree.onDidChangeContentHeight; + } + + get scrollTop(): number { + return this.tree.scrollTop; + } + + set scrollTop(scrollTop: number) { + this.tree.scrollTop = scrollTop; + } + + get scrollHeight(): number { + return this.tree.scrollHeight; + } + + get renderHeight(): number { + return this.tree.renderHeight; + } + + get firstVisibleElement(): T { + return this.tree.firstVisibleElement!.element as T; + } + + get lastVisibleElement(): T { + return this.tree.lastVisibleElement!.element as T; + } + + domFocus(): void { + this.tree.domFocus(); + } + + layout(height?: number, width?: number): void { + this.tree.layout(height, width); + } + + style(styles: IListStyles): void { + this.tree.style(styles); + } + + // Model + + getInput(): TInput | undefined { + return this.root.element as TInput; + } + + async setInput(input: TInput, viewState?: IAsyncDataTreeViewState): Promise { + this.refreshPromises.forEach(promise => promise.cancel()); + this.refreshPromises.clear(); + + this.root.element = input!; + + const viewStateContext = viewState && { viewState, focus: [], selection: [] } as IAsyncDataTreeViewStateContext; + + await this.updateChildren(input, true, viewStateContext); + + if (viewStateContext) { + this.tree.setFocus(viewStateContext.focus); + this.tree.setSelection(viewStateContext.selection); + } + + if (viewState && typeof viewState.scrollTop === 'number') { + this.scrollTop = viewState.scrollTop; + } + } + + async updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + if (typeof this.root.element === 'undefined') { + throw new Error('Tree input not set'); + } + + if (this.root.loading) { + await this.subTreeRefreshPromises.get(this.root)!; + await Event.toPromise(this._onDidRender.event); + } + + await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); + } + + resort(element: TInput | T = this.root.element, recursive = true): void { + this.tree.resort(this.getDataNode(element), recursive); + } + + hasNode(element: TInput | T): boolean { + return element === this.root.element || this.nodes.has(element as T); + } + + // View + + rerender(element?: T): void { + if (element === undefined) { + this.tree.rerender(); + return; + } + + const node = this.getDataNode(element); + this.tree.rerender(node); + } + + updateWidth(element: T): void { + const node = this.getDataNode(element); + this.tree.updateWidth(node); + } + + // Tree + + getNode(element: TInput | T = this.root.element): ITreeNode { + const dataNode = this.getDataNode(element); + const node = this.tree.getNode(dataNode === this.root ? null : dataNode); + return new AsyncDataTreeNodeWrapper(node); + } + + collapse(element: T, recursive: boolean = false): boolean { + const node = this.getDataNode(element); + return this.tree.collapse(node === this.root ? null : node, recursive); + } + + async expand(element: T, recursive: boolean = false): Promise { + if (typeof this.root.element === 'undefined') { + throw new Error('Tree input not set'); + } + + if (this.root.loading) { + await this.subTreeRefreshPromises.get(this.root)!; + await Event.toPromise(this._onDidRender.event); + } + + const node = this.getDataNode(element); + + if (node !== this.root && !node.loading && !this.tree.isCollapsed(node)) { + return false; + } + + const result = this.tree.expand(node === this.root ? null : node, recursive); + + if (node.loading) { + await this.subTreeRefreshPromises.get(node)!; + await Event.toPromise(this._onDidRender.event); + } + + return result; + } + + toggleCollapsed(element: T, recursive: boolean = false): boolean { + return this.tree.toggleCollapsed(this.getDataNode(element), recursive); + } + + expandAll(): void { + this.tree.expandAll(); + } + + collapseAll(): void { + this.tree.collapseAll(); + } + + isCollapsible(element: T): boolean { + return this.tree.isCollapsible(this.getDataNode(element)); + } + + isCollapsed(element: T): boolean { + return this.tree.isCollapsed(this.getDataNode(element)); + } + + toggleKeyboardNavigation(): void { + this.tree.toggleKeyboardNavigation(); + } + + refilter(): void { + this.tree.refilter(); + } + + setSelection(elements: T[], browserEvent?: UIEvent): void { + const nodes = elements.map(e => this.getDataNode(e)); + this.tree.setSelection(nodes, browserEvent); + } + + getSelection(): T[] { + const nodes = this.tree.getSelection(); + return nodes.map(n => n!.element as T); + } + + setFocus(elements: T[], browserEvent?: UIEvent): void { + const nodes = elements.map(e => this.getDataNode(e)); + this.tree.setFocus(nodes, browserEvent); + } + + focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { + this.tree.focusNext(n, loop, browserEvent); + } + + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { + this.tree.focusPrevious(n, loop, browserEvent); + } + + focusNextPage(browserEvent?: UIEvent): void { + this.tree.focusNextPage(browserEvent); + } + + focusPreviousPage(browserEvent?: UIEvent): void { + this.tree.focusPreviousPage(browserEvent); + } + + focusLast(browserEvent?: UIEvent): void { + this.tree.focusLast(browserEvent); + } + + focusFirst(browserEvent?: UIEvent): void { + this.tree.focusFirst(browserEvent); + } + + getFocus(): T[] { + const nodes = this.tree.getFocus(); + return nodes.map(n => n!.element as T); + } + + open(elements: T[]): void { + const nodes = elements.map(e => this.getDataNode(e)); + this.tree.open(nodes); + } + + reveal(element: T, relativeTop?: number): void { + this.tree.reveal(this.getDataNode(element), relativeTop); + } + + getRelativeTop(element: T): number | null { + return this.tree.getRelativeTop(this.getDataNode(element)); + } + + // Tree navigation + + getParentElement(element: T): TInput | T { + const node = this.tree.getParentElement(this.getDataNode(element)); + return (node && node.element)!; + } + + getFirstElementChild(element: TInput | T = this.root.element): TInput | T | undefined { + const dataNode = this.getDataNode(element); + const node = this.tree.getFirstElementChild(dataNode === this.root ? null : dataNode); + return (node && node.element)!; + } + + // Implementation + + private getDataNode(element: TInput | T): IAsyncDataTreeNode { + const node: IAsyncDataTreeNode | undefined = this.nodes.get((element === this.root.element ? null : element) as T); + + if (!node) { + throw new Error(`Data tree node not found: ${element}`); + } + + return node; + } + + private async refreshAndRenderNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + await this.refreshNode(node, recursive, viewStateContext); + this.render(node, viewStateContext); + + if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) { + const treeNode = this.tree.getNode(node); + const visibleChildren = treeNode.children.filter(node => node.visible); + + if (visibleChildren.length === 1) { + await this.tree.expand(visibleChildren[0].element, false); + } + } + } + + private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + let result: Promise | undefined; + + this.subTreeRefreshPromises.forEach((refreshPromise, refreshNode) => { + if (!result && intersects(refreshNode, node)) { + result = refreshPromise.then(() => this.refreshNode(node, recursive, viewStateContext)); + } + }); + + if (result) { + return result; + } + + result = this.doRefreshSubTree(node, recursive, viewStateContext); + this.subTreeRefreshPromises.set(node, result); + + try { + await result; + } finally { + this.subTreeRefreshPromises.delete(node); + } + } + + private async doRefreshSubTree(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + node.loading = true; + + try { + const childrenToRefresh = await this.doRefreshNode(node, recursive, viewStateContext); + node.stale = false; + + await Promise.all(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext))); + } finally { + node.loading = false; + } + } + + private async doRefreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise[]> { + node.hasChildren = !!this.dataSource.hasChildren(node.element!); + + let childrenPromise: Promise; + + if (!node.hasChildren) { + childrenPromise = Promise.resolve([]); + } else { + const slowTimeout = timeout(800); + + slowTimeout.then(() => { + node.slow = true; + this._onDidChangeNodeSlowState.fire(node); + }, _ => null); + + childrenPromise = this.doGetChildren(node) + .finally(() => slowTimeout.cancel()); + } + + try { + const children = await childrenPromise; + return this.setChildren(node, children, recursive, viewStateContext); + } catch (err) { + if (node !== this.root) { + this.tree.collapse(node === this.root ? null : node); + } + + if (isPromiseCanceledError(err)) { + return []; + } + + throw err; + } finally { + if (node.slow) { + node.slow = false; + this._onDidChangeNodeSlowState.fire(node); + } + } + } + + private doGetChildren(node: IAsyncDataTreeNode): Promise { + let result = this.refreshPromises.get(node); + + if (result) { + return result; + } + + result = createCancelablePromise(async () => { + const children = await this.dataSource.getChildren(node.element!); + + if (this.sorter) { + children.sort(this.sorter.compare.bind(this.sorter)); + } + + return children; + }); + + this.refreshPromises.set(node, result); + + return result.finally(() => this.refreshPromises.delete(node)); + } + + private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void { + if (!node.collapsed && node.element.stale) { + if (deep) { + this.collapse(node.element.element as T); + } else { + this.refreshAndRenderNode(node.element, false, ChildrenResolutionReason.Expand) + .catch(onUnexpectedError); + } + } + } + + private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): IAsyncDataTreeNode[] { + // perf: if the node was and still is a leaf, avoid all this hassle + if (node.children.length === 0 && childrenElements.length === 0) { + return []; + } + + const nodesToForget = new Map>(); + const childrenTreeNodesById = new Map | null, TFilterData>>(); + + for (const child of node.children) { + nodesToForget.set(child.element as T, child); + + if (this.identityProvider) { + childrenTreeNodesById.set(child.id!, this.tree.getNode(child)); + } + } + + const childrenToRefresh: IAsyncDataTreeNode[] = []; + + const children = childrenElements.map>(element => { + if (!this.identityProvider) { + return createAsyncDataTreeNode({ + element, + parent: node, + hasChildren: !!this.dataSource.hasChildren(element) + }); + } + + const id = this.identityProvider.getId(element).toString(); + const childNode = childrenTreeNodesById.get(id); + + if (childNode) { + const asyncDataTreeNode = childNode.element!; + + nodesToForget.delete(asyncDataTreeNode.element as T); + this.nodes.delete(asyncDataTreeNode.element as T); + this.nodes.set(element, asyncDataTreeNode); + + asyncDataTreeNode.element = element; + asyncDataTreeNode.hasChildren = !!this.dataSource.hasChildren(element); + + if (recursive) { + if (childNode.collapsed) { + dfs(asyncDataTreeNode, node => node.stale = true); + } else { + childrenToRefresh.push(asyncDataTreeNode); + } + } + + return asyncDataTreeNode; + } + + const childAsyncDataTreeNode = createAsyncDataTreeNode({ + element, + parent: node, + id, + hasChildren: !!this.dataSource.hasChildren(element) + }); + + if (viewStateContext && viewStateContext.viewState.focus && viewStateContext.viewState.focus.indexOf(id) > -1) { + viewStateContext.focus.push(childAsyncDataTreeNode); + } + + if (viewStateContext && viewStateContext.viewState.selection && viewStateContext.viewState.selection.indexOf(id) > -1) { + viewStateContext.selection.push(childAsyncDataTreeNode); + } + + if (viewStateContext && viewStateContext.viewState.expanded && viewStateContext.viewState.expanded.indexOf(id) > -1) { + childrenToRefresh.push(childAsyncDataTreeNode); + } + + return childAsyncDataTreeNode; + }); + + for (const node of values(nodesToForget)) { + dfs(node, node => this.nodes.delete(node.element as T)); + } + + for (const child of children) { + this.nodes.set(child.element as T, child); + } + + node.children.splice(0, node.children.length, ...children); + + return childrenToRefresh; + } + + private render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { + const children = node.children.map(c => asTreeElement(c, viewStateContext)); + this.tree.setChildren(node === this.root ? null : node, children); + this._onDidRender.fire(); + } + + // view state + + getViewState(): IAsyncDataTreeViewState { + if (!this.identityProvider) { + throw new Error('Can\'t get tree view state without an identity provider'); + } + + const getId = (element: T) => this.identityProvider!.getId(element).toString(); + const focus = this.getFocus().map(getId); + const selection = this.getSelection().map(getId); + + const expanded: string[] = []; + const root = this.tree.getNode(); + const queue = [root]; + + while (queue.length > 0) { + const node = queue.shift()!; + + if (node !== root && node.collapsible && !node.collapsed) { + expanded.push(getId(node.element!.element as T)); + } + + queue.push(...node.children); + } + + return { focus, selection, expanded, scrollTop: this.scrollTop }; + } + + dispose(): void { + dispose(this.disposables); + } +} diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index ae1d2d6b7b7..d2342d7e85b 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -3,295 +3,196 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITreeOptions, ComposedTreeDelegate, createComposedTreeListOptions } from 'vs/base/browser/ui/tree/abstractTree'; -import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; -import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Emitter, Event } from 'vs/base/common/event'; -import { timeout } from 'vs/base/common/async'; +import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { ISpliceable } from 'vs/base/common/sequence'; +import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource } from 'vs/base/browser/ui/tree/tree'; +import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { Iterator } from 'vs/base/common/iterator'; -export interface IDataTreeElement { - readonly element: T; - readonly collapsible?: boolean; - readonly collapsed?: boolean; +export interface IDataTreeOptions extends IAbstractTreeOptions { + sorter?: ITreeSorter; } -export interface IDataSource> { - hasChildren(element: T | null): boolean; - getChildren(element: T | null): Thenable[]>; +export interface IDataTreeViewState { + readonly focus: string[]; + readonly selection: string[]; + readonly expanded: string[]; } -enum DataTreeNodeState { - Uninitialized, - Loaded, - Loading, - Slow -} +export class DataTree extends AbstractTree { -interface IDataTreeNode> { - readonly element: T | null; - readonly parent: IDataTreeNode | null; - state: DataTreeNodeState; -} + protected model: ObjectTreeModel; + private input: TInput | undefined; -interface IDataTreeListTemplateData { - templateData: T; -} - -function unpack(node: ITreeNode, TFilterData>): ITreeNode { - return new Proxy(Object.create(null), { - get: (_: any, name: string) => { - switch (name) { - case 'element': return node.element.element; - default: return node[name]; - } - } - }); -} - -class DataTreeRenderer implements ITreeRenderer, TFilterData, IDataTreeListTemplateData> { - - readonly templateId: string; - private renderedNodes = new Map, IDataTreeListTemplateData>(); - private disposables: IDisposable[] = []; - - constructor( - private renderer: ITreeRenderer, - readonly onDidChangeTwistieState: Event> - ) { - this.templateId = renderer.templateId; - } - - renderTemplate(container: HTMLElement): IDataTreeListTemplateData { - const templateData = this.renderer.renderTemplate(container); - return { templateData }; - } - - renderElement(element: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { - this.renderer.renderElement(unpack(element), index, templateData.templateData); - } - - renderTwistie(element: IDataTreeNode, twistieElement: HTMLElement): boolean { - if (element.state === DataTreeNodeState.Slow) { - twistieElement.innerText = '🤨'; - return true; - } - - return false; - } - - disposeElement(element: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData): void { - this.renderer.disposeElement(unpack(element), index, templateData.templateData); - } - - disposeTemplate(templateData: IDataTreeListTemplateData): void { - this.renderer.disposeTemplate(templateData.templateData); - } - - dispose(): void { - this.renderedNodes.clear(); - this.disposables = dispose(this.disposables); - } -} - -export class DataTree, TFilterData = void> implements IDisposable { - - private tree: ObjectTree, TFilterData>; - private root: IDataTreeNode; - private nodes = new Map>(); - - private _onDidChangeNodeState = new Emitter>(); - - private disposables: IDisposable[] = []; + private identityProvider: IIdentityProvider | undefined; + private nodesByIdentity = new Map>(); constructor( container: HTMLElement, delegate: IListVirtualDelegate, - renderers: ITreeRenderer[], - private dataSource: IDataSource, - options?: ITreeOptions + renderers: ITreeRenderer[], + private dataSource: IDataSource, + options: IDataTreeOptions = {} ) { - const objectTreeDelegate = new ComposedTreeDelegate>(delegate); - const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event)); - const objectTreeOptions = createComposedTreeListOptions>(options); + super(container, delegate, renderers, options); + this.identityProvider = options.identityProvider; + } - this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); - this.root = { - element: null, - parent: null, - state: DataTreeNodeState.Uninitialized, + // Model + + getInput(): TInput | undefined { + return this.input; + } + + setInput(input: TInput, viewState?: IDataTreeViewState): void { + if (viewState && !this.identityProvider) { + throw new Error('Can\'t restore tree view state without an identity provider'); + } + + this.input = input; + + if (!viewState) { + this._refresh(input); + return; + } + + const focus: T[] = []; + const selection: T[] = []; + + const isCollapsed = (element: T) => { + const id = this.identityProvider!.getId(element).toString(); + return viewState.expanded.indexOf(id) === -1; }; - this.nodes.set(null, this.root); + const onDidCreateNode = (node: ITreeNode) => { + const id = this.identityProvider!.getId(node.element).toString(); - this.tree.onDidChangeCollapseState(this.onDidChangeCollapseState, this, this.disposables); + if (viewState.focus.indexOf(id) > -1) { + focus.push(node.element); + } + + if (viewState.selection.indexOf(id) > -1) { + selection.push(node.element); + } + }; + + this._refresh(input, isCollapsed, onDidCreateNode); + this.setFocus(focus); + this.setSelection(selection); } - layout(height?: number): void { - this.tree.layout(height); - } - - refresh(element: T | null): Thenable { - return this.refreshNode(this.getNode(element)); - } - - private getNode(element: T | null): IDataTreeNode { - const node: IDataTreeNode = this.nodes.get(element); - - if (typeof node === 'undefined') { - throw new Error(`Data tree node not found: ${element}`); + updateChildren(element: TInput | T = this.input!): void { + if (typeof this.input === 'undefined') { + throw new Error('Tree input not set'); } - return node; - } + let isCollapsed: ((el: T) => boolean | undefined) | undefined; - private refreshNode(node: IDataTreeNode): Thenable { - const hasChildren = this.dataSource.hasChildren(node.element); + if (this.identityProvider) { + isCollapsed = element => { + const id = this.identityProvider!.getId(element).toString(); + const node = this.nodesByIdentity.get(id); - if (!hasChildren) { - this.tree.setChildren(node === this.root ? null : node); - return Promise.resolve(); - } else { - node.state = DataTreeNodeState.Loading; - this._onDidChangeNodeState.fire(node); + if (!node) { + return undefined; + } - const slowTimeout = timeout(800); - - slowTimeout.then(() => { - node.state = DataTreeNodeState.Slow; - this._onDidChangeNodeState.fire(node); - }); - - return this.dataSource.getChildren(node.element) - .then(children => { - slowTimeout.cancel(); - node.state = DataTreeNodeState.Loaded; - this._onDidChangeNodeState.fire(node); - - const createTreeElement = (el: IDataTreeElement): ITreeElement> => { - return { - element: { - element: el.element, - state: DataTreeNodeState.Uninitialized, - parent: node - }, - collapsible: el.collapsible, - collapsed: typeof el.collapsed === 'boolean' ? el.collapsed : true - }; - }; - - const nodeChildren = children.map>>(createTreeElement); - - this.tree.setChildren(node === this.root ? null : node, nodeChildren); - }, err => { - slowTimeout.cancel(); - node.state = DataTreeNodeState.Uninitialized; - this._onDidChangeNodeState.fire(node); - - if (node !== this.root) { - this.tree.collapse(node); - } - - return Promise.reject(err); - }); + return node.collapsed; + }; } + + this._refresh(element, isCollapsed); } - private onDidChangeCollapseState(treeNode: ITreeNode, any>): void { - if (!treeNode.collapsed && treeNode.element.state === DataTreeNodeState.Uninitialized) { - this.refreshNode(treeNode.element); + resort(element: T | TInput = this.input!, recursive = true): void { + this.model.resort((element === this.input ? null : element) as T, recursive); + } + + // View + + refresh(element?: T): void { + if (element === undefined) { + this.view.rerender(); + return; } + + this.model.rerender(element); } - // Tree + // Implementation - collapse(element: T): boolean { - return this.tree.collapse(this.getNode(element)); + private _refresh(element: TInput | T, isCollapsed?: (el: T) => boolean | undefined, onDidCreateNode?: (node: ITreeNode) => void): void { + let onDidDeleteNode: ((node: ITreeNode) => void) | undefined; + + if (this.identityProvider) { + const insertedElements = new Set(); + + const outerOnDidCreateNode = onDidCreateNode; + onDidCreateNode = (node: ITreeNode) => { + const id = this.identityProvider!.getId(node.element).toString(); + + insertedElements.add(id); + this.nodesByIdentity.set(id, node); + + if (outerOnDidCreateNode) { + outerOnDidCreateNode(node); + } + }; + + onDidDeleteNode = (node: ITreeNode) => { + const id = this.identityProvider!.getId(node.element).toString(); + + if (!insertedElements.has(id)) { + this.nodesByIdentity.delete(id); + } + }; + } + + this.model.setChildren((element === this.input ? null : element) as T, this.iterate(element, isCollapsed).elements, onDidCreateNode, onDidDeleteNode); } - expand(element: T): boolean { - return this.tree.expand(this.getNode(element)); + private iterate(element: TInput | T, isCollapsed?: (el: T) => boolean | undefined): { elements: Iterator>, size: number } { + const children = this.dataSource.getChildren(element); + const elements = Iterator.map>(Iterator.fromArray(children), element => { + const { elements: children, size } = this.iterate(element, isCollapsed); + const collapsed = size === 0 ? undefined : (isCollapsed && isCollapsed(element)); + + return { element, children, collapsed }; + }); + + return { elements, size: children.length }; } - toggleCollapsed(element: T): void { - this.tree.toggleCollapsed(this.getNode(element)); + protected createModel(view: ISpliceable>, options: IDataTreeOptions): ITreeModel { + return new ObjectTreeModel(view, options); } - collapseAll(): void { - this.tree.collapseAll(); - } + // view state - isCollapsed(element: T): boolean { - return this.tree.isCollapsed(this.getNode(element)); - } + getViewState(): IDataTreeViewState { + if (!this.identityProvider) { + throw new Error('Can\'t get tree view state without an identity provider'); + } - isExpanded(element: T): boolean { - return this.tree.isExpanded(this.getNode(element)); - } + const getId = (element: T) => this.identityProvider!.getId(element).toString(); + const focus = this.getFocus().map(getId); + const selection = this.getSelection().map(getId); - refilter(): void { - this.tree.refilter(); - } + const expanded: string[] = []; + const root = this.model.getNode(); + const queue = [root]; - setSelection(elements: T[], browserEvent?: UIEvent): void { - const nodes = elements.map(e => this.getNode(e)); - this.tree.setSelection(nodes, browserEvent); - } + while (queue.length > 0) { + const node = queue.shift()!; - getSelection(): T[] { - const nodes = this.tree.getSelection(); - return nodes.map(n => n.element!); - } + if (node !== root && node.collapsible && !node.collapsed) { + expanded.push(getId(node.element!)); + } - setFocus(elements: T[], browserEvent?: UIEvent): void { - const nodes = elements.map(e => this.getNode(e)); - this.tree.setFocus(nodes, browserEvent); - } + queue.push(...node.children); + } - focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { - this.tree.focusNext(n, loop, browserEvent); + return { focus, selection, expanded }; } - - focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { - this.tree.focusPrevious(n, loop, browserEvent); - } - - focusNextPage(browserEvent?: UIEvent): void { - this.tree.focusNextPage(browserEvent); - } - - focusPreviousPage(browserEvent?: UIEvent): void { - this.tree.focusPreviousPage(browserEvent); - } - - focusLast(browserEvent?: UIEvent): void { - this.tree.focusLast(browserEvent); - } - - focusFirst(browserEvent?: UIEvent): void { - this.tree.focusFirst(browserEvent); - } - - getFocus(): T[] { - const nodes = this.tree.getFocus(); - return nodes.map(n => n.element!); - } - - open(elements: T[]): void { - const nodes = elements.map(e => this.getNode(e)); - this.tree.open(nodes); - } - - reveal(element: T, relativeTop?: number): void { - this.tree.reveal(this.getNode(element), relativeTop); - } - - getRelativeTop(element: T): number | null { - return this.tree.getRelativeTop(this.getNode(element)); - } - - dispose(): void { - this.disposables = dispose(this.disposables); - } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/tree/indexTree.ts b/src/vs/base/browser/ui/tree/indexTree.ts index 02e9b4492bb..54eb0e103e6 100644 --- a/src/vs/base/browser/ui/tree/indexTree.ts +++ b/src/vs/base/browser/ui/tree/indexTree.ts @@ -5,20 +5,42 @@ import 'vs/css!./media/tree'; import { Iterator, ISequence } from 'vs/base/common/iterator'; -import { AbstractTree, ITreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; import { IndexTreeModel } from 'vs/base/browser/ui/tree/indexTreeModel'; -import { ITreeElement, ITreeModel, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import { ITreeElement, ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; + +export interface IIndexTreeOptions extends IAbstractTreeOptions { } export class IndexTree extends AbstractTree { protected model: IndexTreeModel; + constructor( + container: HTMLElement, + delegate: IListVirtualDelegate, + renderers: ITreeRenderer[], + private rootElement: T, + options: IIndexTreeOptions = {} + ) { + super(container, delegate, renderers, options); + } + splice(location: number[], deleteCount: number, toInsert: ISequence> = Iterator.empty()): Iterator> { return this.model.splice(location, deleteCount, toInsert); } - protected createModel(view: ISpliceable>, options: ITreeOptions): ITreeModel { - return new IndexTreeModel(view, options); + rerender(location?: number[]): void { + if (location === undefined) { + this.view.rerender(); + return; + } + + this.model.rerender(location); } -} \ No newline at end of file + + protected createModel(view: ISpliceable>, options: IIndexTreeOptions): ITreeModel { + return new IndexTreeModel(view, this.rootElement, options); + } +} diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 1b4ee5b0c70..a568881e7c2 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -3,15 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator, ISequence } from 'vs/base/common/iterator'; -import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; +import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; import { tail2 } from 'vs/base/common/arrays'; -import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; +import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; +import { ISequence, Iterator } from 'vs/base/common/iterator'; +import { ISpliceable } from 'vs/base/common/sequence'; interface IMutableTreeNode extends ITreeNode { readonly parent: IMutableTreeNode | undefined; readonly children: IMutableTreeNode[]; + visibleChildrenCount: number; + visibleChildIndex: number; collapsible: boolean; collapsed: boolean; renderNodeCount: number; @@ -19,10 +21,18 @@ interface IMutableTreeNode extends ITreeNode { filterData: TFilterData | undefined; } -function isFilterResult(obj: any): obj is ITreeFilterDataResult { +export function isFilterResult(obj: any): obj is ITreeFilterDataResult { return typeof obj === 'object' && 'visibility' in obj && 'data' in obj; } +export function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { + switch (visibility) { + case true: return TreeVisibility.Visible; + case false: return TreeVisibility.Hidden; + default: return visibility; + } +} + function treeNodeToElement(node: IMutableTreeNode): ITreeElement { const { element, collapsed } = node; const children = Iterator.map(Iterator.fromArray(node.children), treeNodeToElement); @@ -30,40 +40,50 @@ function treeNodeToElement(node: IMutableTreeNode): ITreeElement { return { element, children, collapsed }; } -function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { - switch (visibility) { - case true: return TreeVisibility.Visible; - case false: return TreeVisibility.Hidden; - default: return visibility; - } +export interface IIndexTreeModelOptions { + readonly collapseByDefault?: boolean; // defaults to false + readonly filter?: ITreeFilter; + readonly autoExpandSingleChildren?: boolean; } -export class IndexTreeModel implements ITreeModel { +export class IndexTreeModel, TFilterData = void> implements ITreeModel { - private root: IMutableTreeNode = { - parent: undefined, - element: undefined!, - children: [], - depth: 0, - collapsible: false, - collapsed: false, - renderNodeCount: 0, - visible: true, - filterData: undefined - }; + readonly rootRef = []; + private root: IMutableTreeNode; private eventBufferer = new EventBufferer(); - private _onDidChangeCollapseState = new Emitter>(); - readonly onDidChangeCollapseState: Event> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event); + private _onDidChangeCollapseState = new Emitter>(); + readonly onDidChangeCollapseState: Event> = this.eventBufferer.wrapEvent(this._onDidChangeCollapseState.event); private _onDidChangeRenderNodeCount = new Emitter>(); readonly onDidChangeRenderNodeCount: Event> = this.eventBufferer.wrapEvent(this._onDidChangeRenderNodeCount.event); + private collapseByDefault: boolean; private filter?: ITreeFilter; + private autoExpandSingleChildren: boolean; - constructor(private list: ISpliceable>, options: ITreeModelOptions = {}) { + private _onDidSplice = new Emitter>(); + readonly onDidSplice = this._onDidSplice.event; + + constructor(private list: ISpliceable>, rootElement: T, options: IIndexTreeModelOptions = {}) { + this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault; this.filter = options.filter; + this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; + + this.root = { + parent: undefined, + element: rootElement, + children: [], + depth: 0, + visibleChildrenCount: 0, + visibleChildIndex: -1, + collapsible: false, + collapsed: false, + renderNodeCount: 0, + visible: true, + filterData: undefined + }; } splice( @@ -77,22 +97,64 @@ export class IndexTreeModel implements ITreeModel[] = []; const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); + const lastIndex = location[location.length - 1]; + + // figure out what's the visible child start index right before the + // splice point + let visibleChildStartIndex = 0; + + for (let i = lastIndex; i >= 0 && i < parentNode.children.length; i--) { + const child = parentNode.children[i]; + + if (child.visible) { + visibleChildStartIndex = child.visibleChildIndex; + break; + } + } + const nodesToInsert: IMutableTreeNode[] = []; + let insertedVisibleChildrenCount = 0; let renderNodeCount = 0; - Iterator.forEach(nodesToInsertIterator, node => { - nodesToInsert.push(node); - renderNodeCount += node.renderNodeCount; + Iterator.forEach(nodesToInsertIterator, child => { + nodesToInsert.push(child); + renderNodeCount += child.renderNodeCount; + + if (child.visible) { + child.visibleChildIndex = visibleChildStartIndex + insertedVisibleChildrenCount++; + } }); - const lastIndex = location[location.length - 1]; const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); - if (revealed) { + // figure out what is the count of deleted visible children + let deletedVisibleChildrenCount = 0; + + for (const child of deletedNodes) { + if (child.visible) { + deletedVisibleChildrenCount++; + } + } + + // and adjust for all visible children after the splice point + if (deletedVisibleChildrenCount !== 0) { + for (let i = lastIndex + nodesToInsert.length; i < parentNode.children.length; i++) { + const child = parentNode.children[i]; + + if (child.visible) { + child.visibleChildIndex -= deletedVisibleChildrenCount; + } + } + } + + // update parent's visible children count + parentNode.visibleChildrenCount += insertedVisibleChildrenCount - deletedVisibleChildrenCount; + + if (revealed && visible) { const visibleDeleteCount = deletedNodes.reduce((r, node) => r + node.renderNodeCount, 0); this._updateAncestorsRenderNodeCount(parentNode, renderNodeCount - visibleDeleteCount); @@ -108,73 +170,133 @@ export class IndexTreeModel implements ITreeModel this._setCollapsed(node, listIndex, revealed, collapsed)); + getListRenderCount(location: number[]): number { + return this.getTreeNode(location).renderNodeCount; } - toggleCollapsed(location: number[]): void { - const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); - this.eventBufferer.bufferEvents(() => this._setCollapsed(node, listIndex, revealed)); - } - - collapseAll(): void { - const queue = [...this.root.children]; - let listIndex = 0; - - this.eventBufferer.bufferEvents(() => { - while (queue.length > 0) { - const node = queue.shift()!; - const revealed = listIndex < this.root.children.length; - this._setCollapsed(node, listIndex, revealed, true); - - queue.push(...node.children); - listIndex++; - } - }); + isCollapsible(location: number[]): boolean { + return this.getTreeNode(location).collapsible; } isCollapsed(location: number[]): boolean { return this.getTreeNode(location).collapsed; } - refilter(): void { - const previousRenderNodeCount = this.root.renderNodeCount; - const toInsert = this.updateNodeAfterFilterChange(this.root); - this.list.splice(0, previousRenderNodeCount, toInsert); - } - - private _setCollapsed(node: IMutableTreeNode, listIndex: number, revealed: boolean, collapsed?: boolean | undefined): boolean { - if (!node.collapsible) { - return false; - } + setCollapsed(location: number[], collapsed?: boolean, recursive?: boolean): boolean { + const node = this.getTreeNode(location); if (typeof collapsed === 'undefined') { collapsed = !node.collapsed; } - if (node.collapsed === collapsed) { - return false; + return this.eventBufferer.bufferEvents(() => this._setCollapsed(location, collapsed!, recursive)); + } + + private _setCollapsed(location: number[], collapsed: boolean, recursive?: boolean): boolean { + const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); + + const result = this._setListNodeCollapsed(node, listIndex, revealed, collapsed!, recursive || false); + + if (this.autoExpandSingleChildren && !collapsed! && !recursive) { + let onlyVisibleChildIndex = -1; + + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + + if (child.visible) { + if (onlyVisibleChildIndex > -1) { + onlyVisibleChildIndex = -1; + break; + } else { + onlyVisibleChildIndex = i; + } + } + } + + if (onlyVisibleChildIndex > -1) { + this._setCollapsed([...location, onlyVisibleChildIndex], false, false); + } } - node.collapsed = collapsed; + return result; + } - if (revealed) { - const previousRenderNodeCount = node.renderNodeCount; - const toInsert = this.updateNodeAfterCollapseChange(node); + private _setListNodeCollapsed(node: IMutableTreeNode, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean { + const result = this._setNodeCollapsed(node, collapsed, recursive, false); - this.list.splice(listIndex + 1, previousRenderNodeCount - 1, toInsert.slice(1)); - this._onDidChangeCollapseState.fire(node); + if (!revealed || !node.visible) { + return result; } - return true; + const previousRenderNodeCount = node.renderNodeCount; + const toInsert = this.updateNodeAfterCollapseChange(node); + const deleteCount = previousRenderNodeCount - (listIndex === -1 ? 0 : 1); + this.list.splice(listIndex + 1, deleteCount, toInsert.slice(1)); + + return result; + } + + private _setNodeCollapsed(node: IMutableTreeNode, collapsed: boolean, recursive: boolean, deep: boolean): boolean { + let result = node.collapsible && node.collapsed !== collapsed; + + if (node.collapsible) { + node.collapsed = collapsed; + + if (result) { + this._onDidChangeCollapseState.fire({ node, deep }); + } + } + + if (recursive) { + for (const child of node.children) { + result = this._setNodeCollapsed(child, collapsed, true, true) || result; + } + } + + return result; + } + + expandTo(location: number[]): void { + this.eventBufferer.bufferEvents(() => { + let node = this.getTreeNode(location); + + while (node.parent) { + node = node.parent; + location = location.slice(0, location.length - 1); + + if (node.collapsed) { + this._setCollapsed(location, false); + } + } + }); + } + + refilter(): void { + const previousRenderNodeCount = this.root.renderNodeCount; + const toInsert = this.updateNodeAfterFilterChange(this.root); + this.list.splice(0, previousRenderNodeCount, toInsert); } private createTreeNode( @@ -190,8 +312,10 @@ export class IndexTreeModel implements ITreeModel implements ITreeModel this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); - let hasVisibleDescendants = false; + let visibleChildrenCount = 0; let renderNodeCount = 1; Iterator.forEach(childNodes, child => { node.children.push(child); - hasVisibleDescendants = hasVisibleDescendants || child.visible; renderNodeCount += child.renderNodeCount; + + if (child.visible) { + child.visibleChildIndex = visibleChildrenCount++; + } }); node.collapsible = node.collapsible || node.children.length > 0; - node.visible = visibility === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility === TreeVisibility.Visible); + node.visibleChildrenCount = visibleChildrenCount; + node.visible = visibility === TreeVisibility.Recurse ? visibleChildrenCount > 0 : (visibility === TreeVisibility.Visible); if (!node.visible) { node.renderNodeCount = 0; @@ -295,9 +423,19 @@ export class IndexTreeModel implements ITreeModel implements ITreeModel(result)) { node.filterData = result.data; return getVisibleState(result.visibility); @@ -346,7 +484,7 @@ export class IndexTreeModel implements ITreeModel = this.root): IMutableTreeNode { + private getTreeNode(location: number[], node: IMutableTreeNode = this.root): IMutableTreeNode { if (!location || location.length === 0) { return node; } @@ -361,8 +499,12 @@ export class IndexTreeModel implements ITreeModel, listIndex: number, revealed: boolean } { - const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location); + private getTreeNodeWithListIndex(location: number[]): { node: IMutableTreeNode, listIndex: number, revealed: boolean, visible: boolean } { + if (location.length === 0) { + return { node: this.root, listIndex: -1, revealed: true, visible: false }; + } + + const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location); const index = location[location.length - 1]; if (index < 0 || index > parentNode.children.length) { @@ -371,10 +513,10 @@ export class IndexTreeModel implements ITreeModel = this.root, listIndex: number = 0, revealed = true): { parentNode: IMutableTreeNode; listIndex: number; revealed: boolean; } { + private getParentNodeWithListIndex(location: number[], node: IMutableTreeNode = this.root, listIndex: number = 0, revealed = true, visible = true): { parentNode: IMutableTreeNode; listIndex: number; revealed: boolean; visible: boolean; } { const [index, ...rest] = location; if (index < 0 || index > node.children.length) { @@ -387,12 +529,13 @@ export class IndexTreeModel implements ITreeModel { @@ -411,7 +554,7 @@ export class IndexTreeModel implements ITreeModel implements ITreeModel): T | null { + private _getLastElementAncestor(node: ITreeNode): T { if (node.children.length === 0) { return node.element; } return this._getLastElementAncestor(node.children[node.children.length - 1]); } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/tree/media/loading-dark.svg b/src/vs/base/browser/ui/tree/media/loading-dark.svg new file mode 100644 index 00000000000..7dc1ebd8cf0 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/loading-dark.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/tree/media/loading-hc.svg b/src/vs/base/browser/ui/tree/media/loading-hc.svg new file mode 100644 index 00000000000..c3633c0ddab --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/loading-hc.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/tree/media/loading.svg b/src/vs/base/browser/ui/tree/media/loading.svg new file mode 100644 index 00000000000..e762f06d5e6 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/loading.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 62a7ea676a4..4f86f971f97 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -19,36 +19,51 @@ text-align: right; margin-right: 6px; flex-shrink: 0; + width: 16px; } .monaco-tl-contents { flex: 1; + overflow: hidden; } .monaco-tl-twistie.collapsible { background-size: 16px; - background-position: 100% 50%; + background-position: 3px 50%; background-repeat: no-repeat; background-image: url("expanded.svg"); } -.monaco-tl-twistie.collapsible.collapsed { +.monaco-tl-twistie.collapsible.collapsed:not(.loading) { display: inline-block; background-image: url("collapsed.svg"); } -.vs-dark .monaco-tl-twistie.collapsible { +.vs-dark .monaco-tl-twistie.collapsible:not(.loading) { background-image: url("expanded-dark.svg"); } -.vs-dark .monaco-tl-twistie.collapsible.collapsed { +.vs-dark .monaco-tl-twistie.collapsible.collapsed:not(.loading) { background-image: url("collapsed-dark.svg"); } -.hc-black .monaco-tl-twistie.collapsible { +.hc-black .monaco-tl-twistie.collapsible:not(.loading) { background-image: url("expanded-hc.svg"); } -.hc-black .monaco-tl-twistie.collapsible.collapsed { +.hc-black .monaco-tl-twistie.collapsible.collapsed:not(.loading) { background-image: url("collapsed-hc.svg"); +} + +.monaco-tl-twistie.loading { + background-image: url("loading.svg"); + background-position: 0 center; +} + +.vs-dark .monaco-tl-twistie.loading { + background-image: url("loading-dark.svg"); +} + +.hc-black .monaco-tl-twistie.loading { + background-image: url("loading-hc.svg"); } \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index ecd6e05e082..1236b065722 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -4,20 +4,52 @@ *--------------------------------------------------------------------------------------------*/ import { Iterator, ISequence } from 'vs/base/common/iterator'; -import { AbstractTree, ITreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; -import { ITreeNode, ITreeModel, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -export class ObjectTree, TFilterData = void> extends AbstractTree { +export interface IObjectTreeOptions extends IAbstractTreeOptions { + sorter?: ITreeSorter; +} + +export class ObjectTree, TFilterData = void> extends AbstractTree { protected model: ObjectTreeModel; - setChildren(element: T | null, children?: ISequence>): Iterator> { - return this.model.setChildren(element, children); + constructor( + container: HTMLElement, + delegate: IListVirtualDelegate, + renderers: ITreeRenderer[], + options: IObjectTreeOptions = {} + ) { + super(container, delegate, renderers, options); } - protected createModel(view: ISpliceable>, options: ITreeOptions): ITreeModel { + setChildren( + element: T | null, + children?: ISequence>, + onDidCreateNode?: (node: ITreeNode) => void, + onDidDeleteNode?: (node: ITreeNode) => void + ): Iterator> { + return this.model.setChildren(element, children, onDidCreateNode, onDidDeleteNode); + } + + rerender(element?: T): void { + if (element === undefined) { + this.view.rerender(); + return; + } + + this.model.rerender(element); + } + + resort(element: T, recursive = true): void { + this.model.resort(element, recursive); + } + + protected createModel(view: ISpliceable>, options: IObjectTreeOptions): ITreeModel { return new ObjectTreeModel(view, options); } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 742aed056ef..9fc7e00898b 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -4,43 +4,148 @@ *--------------------------------------------------------------------------------------------*/ import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator, ISequence } from 'vs/base/common/iterator'; -import { IndexTreeModel } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterator'; +import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; -import { ITreeModel, ITreeNode, ITreeElement, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; -export class ObjectTreeModel, TFilterData = void> implements ITreeModel { +export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { + readonly sorter?: ITreeSorter; +} - private model: IndexTreeModel; - private nodes = new Map>(); +export class ObjectTreeModel, TFilterData extends NonNullable = void> implements ITreeModel { - readonly onDidChangeCollapseState: Event>; + readonly rootRef = null; + + private model: IndexTreeModel; + private nodes = new Map>(); + private sorter?: ITreeSorter<{ element: T; }>; + + readonly onDidSplice: Event>; + readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; get size(): number { return this.nodes.size; } - constructor(list: ISpliceable>, options: ITreeModelOptions = {}) { - this.model = new IndexTreeModel(list, options); - this.onDidChangeCollapseState = this.model.onDidChangeCollapseState; - this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount; + constructor(list: ISpliceable>, options: IObjectTreeModelOptions = {}) { + this.model = new IndexTreeModel(list, null, options); + this.onDidSplice = this.model.onDidSplice; + this.onDidChangeCollapseState = this.model.onDidChangeCollapseState as Event>; + this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount as Event>; + + if (options.sorter) { + this.sorter = { + compare(a, b) { + return options.sorter!.compare(a.element, b.element); + } + }; + } } - setChildren(element: T | null, children?: ISequence>): Iterator> { + setChildren( + element: T | null, + children: ISequence> | undefined, + onDidCreateNode?: (node: ITreeNode) => void, + onDidDeleteNode?: (node: ITreeNode) => void + ): Iterator> { const location = this.getElementLocation(element); - const insertedElements = new Set(); + return this._setChildren(location, this.preserveCollapseState(children), onDidCreateNode, onDidDeleteNode); + } - const onDidCreateNode = (node: ITreeNode) => { + private _setChildren( + location: number[], + children: ISequence> | undefined, + onDidCreateNode?: (node: ITreeNode) => void, + onDidDeleteNode?: (node: ITreeNode) => void + ): Iterator> { + const insertedElements = new Set(); + + const _onDidCreateNode = (node: ITreeNode) => { insertedElements.add(node.element); this.nodes.set(node.element, node); - }; - const onDidDeleteNode = (node: ITreeNode) => { - if (!insertedElements.has(node.element)) { - this.nodes.delete(node.element); + if (onDidCreateNode) { + onDidCreateNode(node); } }; - return this.model.splice([...location, 0], Number.MAX_VALUE, children, onDidCreateNode, onDidDeleteNode); + const _onDidDeleteNode = (node: ITreeNode) => { + if (!insertedElements.has(node.element)) { + this.nodes.delete(node.element); + } + + if (onDidDeleteNode) { + onDidDeleteNode(node); + } + }; + + return this.model.splice( + [...location, 0], + Number.MAX_VALUE, + children, + _onDidCreateNode, + _onDidDeleteNode + ); + } + + private preserveCollapseState(elements: ISequence> | undefined): ISequence> { + let iterator = elements ? getSequenceIterator(elements) : Iterator.empty>(); + + if (this.sorter) { + iterator = Iterator.fromArray(Iterator.collect(iterator).sort(this.sorter.compare.bind(this.sorter))); + } + + return Iterator.map(iterator, treeElement => { + const node = this.nodes.get(treeElement.element); + + if (!node) { + return { + ...treeElement, + children: this.preserveCollapseState(treeElement.children) + }; + } + + const collapsible = typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : node.collapsible; + const collapsed = typeof treeElement.collapsed !== 'undefined' ? treeElement.collapsed : node.collapsed; + + return { + ...treeElement, + collapsible, + collapsed, + children: this.preserveCollapseState(treeElement.children) + }; + }); + } + + rerender(element: T): void { + const location = this.getElementLocation(element); + this.model.rerender(location); + } + + resort(element: T | null = null, recursive = true): void { + if (!this.sorter) { + return; + } + + const location = this.getElementLocation(element); + const node = this.model.getNode(location); + + this._setChildren(location, this.resortChildren(node, recursive)); + } + + private resortChildren(node: ITreeNode, recursive: boolean, first = true): ISequence> { + let childrenNodes = Iterator.fromArray(node.children as ITreeNode[]); + + if (recursive || first) { + childrenNodes = Iterator.fromArray(Iterator.collect(childrenNodes).sort(this.sorter!.compare.bind(this.sorter))); + } + + return Iterator.map, ITreeElement>(childrenNodes, node => ({ + element: node.element as T, + collapsible: node.collapsible, + collapsed: node.collapsed, + children: this.resortChildren(node, recursive, false) + })); } getParentElement(ref: T | null = null): T | null { @@ -48,14 +153,14 @@ export class ObjectTreeModel, TFilterData = void> imp return this.model.getParentElement(location); } - getFirstChildElement(ref: T | null = null): T | null { + getFirstElementChild(ref: T | null = null): T | null | undefined { const location = this.getElementLocation(ref); - return this.model.getFirstChildElement(location); + return this.model.getFirstElementChild(location); } - getLastAncestorElement(ref: T | null = null): T | null { + getLastElementAncestor(ref: T | null = null): T | null | undefined { const location = this.getElementLocation(ref); - return this.model.getLastAncestorElement(location); + return this.model.getLastElementAncestor(location); } getListIndex(element: T): number { @@ -63,18 +168,14 @@ export class ObjectTreeModel, TFilterData = void> imp return this.model.getListIndex(location); } - setCollapsed(element: T, collapsed: boolean): boolean { + getListRenderCount(element: T): number { const location = this.getElementLocation(element); - return this.model.setCollapsed(location, collapsed); + return this.model.getListRenderCount(location); } - toggleCollapsed(element: T): void { + isCollapsible(element: T): boolean { const location = this.getElementLocation(element); - this.model.toggleCollapsed(location); - } - - collapseAll(): void { - this.model.collapseAll(); + return this.model.isCollapsible(location); } isCollapsed(element: T): boolean { @@ -82,13 +183,32 @@ export class ObjectTreeModel, TFilterData = void> imp return this.model.isCollapsed(location); } + setCollapsed(element: T, collapsed?: boolean, recursive?: boolean): boolean { + const location = this.getElementLocation(element); + return this.model.setCollapsed(location, collapsed, recursive); + } + + expandTo(element: T): void { + const location = this.getElementLocation(element); + this.model.expandTo(location); + } + refilter(): void { this.model.refilter(); } - getNode(element: T | null = null): ITreeNode { - const location = this.getElementLocation(element); - return this.model.getNode(location); + getNode(element: T | null = null): ITreeNode { + if (element === null) { + return this.model.getNode(this.model.rootRef); + } + + const node = this.nodes.get(element); + + if (!node) { + throw new Error(`Tree element not found: ${element}`); + } + + return node; } getNodeLocation(node: ITreeNode): T { @@ -118,4 +238,4 @@ export class ObjectTreeModel, TFilterData = void> imp return this.model.getNodeLocation(node); } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 2d0705b9ccb..cedb6fbd423 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -5,7 +5,8 @@ import { Event } from 'vs/base/common/event'; import { Iterator } from 'vs/base/common/iterator'; -import { IListRenderer } from 'vs/base/browser/ui/list/list'; +import { IListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export const enum TreeVisibility { @@ -32,7 +33,7 @@ export const enum TreeVisibility { export interface ITreeFilterDataResult { /** - * Whether the node should be visibile. + * Whether the node should be visible. */ visibility: boolean | TreeVisibility; @@ -67,6 +68,10 @@ export interface ITreeFilter { filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult; } +export interface ITreeSorter { + compare(element: T, otherElement: T): number; +} + export interface ITreeElement { readonly element: T; readonly children?: Iterator> | ITreeElement[]; @@ -79,38 +84,105 @@ export interface ITreeNode { readonly parent: ITreeNode | undefined; readonly children: ITreeNode[]; readonly depth: number; + readonly visibleChildrenCount: number; + readonly visibleChildIndex: number; readonly collapsible: boolean; readonly collapsed: boolean; readonly visible: boolean; readonly filterData: TFilterData | undefined; } +export interface ICollapseStateChangeEvent { + node: ITreeNode; + deep: boolean; +} + +export interface ITreeModelSpliceEvent { + insertedNodes: ITreeNode[]; + deletedNodes: ITreeNode[]; +} + export interface ITreeModel { - readonly onDidChangeCollapseState: Event>; + readonly rootRef: TRef; + + readonly onDidSplice: Event>; + readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; getListIndex(location: TRef): number; + getListRenderCount(location: TRef): number; getNode(location?: TRef): ITreeNode; getNodeLocation(node: ITreeNode): TRef; - getParentNodeLocation(location: TRef): TRef | null; + getParentNodeLocation(location: TRef): TRef; - getParentElement(location: TRef | null): T | null; - getFirstChildElement(location: TRef | null): T | null; - getLastAncestorElement(location: TRef | null): T | null; + getParentElement(location: TRef): T; + getFirstElementChild(location: TRef): T | undefined; + getLastElementAncestor(location?: TRef): T | undefined; + isCollapsible(location: TRef): boolean; isCollapsed(location: TRef): boolean; - setCollapsed(location: TRef, collapsed: boolean): boolean; - toggleCollapsed(location: TRef): void; - collapseAll(): void; + setCollapsed(location: TRef, collapsed?: boolean, recursive?: boolean): boolean; + expandTo(location: TRef): void; refilter(): void; } -export interface ITreeRenderer extends IListRenderer, TTemplateData> { - renderTwistie?(element: T, twistieElement: HTMLElement): boolean; +export interface ITreeRenderer extends IListRenderer, TTemplateData> { + renderTwistie?(element: T, twistieElement: HTMLElement): void; onDidChangeTwistieState?: Event; } -export interface ITreeModelOptions { - filter?: ITreeFilter; -} \ No newline at end of file +export interface ITreeEvent { + elements: T[]; + browserEvent?: UIEvent; +} + +export interface ITreeMouseEvent { + browserEvent: MouseEvent; + element: T | null; +} + +export interface ITreeContextMenuEvent { + browserEvent: UIEvent; + element: T | null; + anchor: HTMLElement | { x: number; y: number; }; +} + +export interface ITreeNavigator { + current(): T | null; + previous(): T | null; + parent(): T | null; + first(): T | null; + last(): T | null; + next(): T | null; +} + +export interface IDataSource { + getChildren(element: TInput | T): T[]; +} + +export interface IAsyncDataSource { + hasChildren(element: TInput | T): boolean; + getChildren(element: TInput | T): T[] | Promise; +} + +export const enum TreeDragOverBubble { + Down, + Up +} + +export interface ITreeDragOverReaction extends IListDragOverReaction { + bubble?: TreeDragOverBubble; + autoExpand?: boolean; +} + +export const TreeDragOverReactions = { + acceptBubbleUp(): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Up }; }, + acceptBubbleDown(autoExpand = false): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand }; }, + acceptCopyBubbleUp(): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Up, effect: ListDragOverEffect.Copy }; }, + acceptCopyBubbleDown(autoExpand = false): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Down, effect: ListDragOverEffect.Copy, autoExpand }; } +}; + +export interface ITreeDragAndDrop extends IListDragAndDrop { + onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction; +} diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 07db01c6922..067704d7c21 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -20,16 +20,16 @@ export interface IAction extends IDisposable { enabled: boolean; checked: boolean; radio: boolean; - run(event?: any): Thenable; + run(event?: any): Promise; } export interface IActionRunner extends IDisposable { - run(action: IAction, context?: any): Thenable; + run(action: IAction, context?: any): Promise; onDidRun: Event; onDidBeforeRun: Event; } -export interface IActionItem { +export interface IActionViewItem { actionRunner: IActionRunner; setActionContext(context: any): void; render(element: any /* HTMLElement */): void; @@ -60,9 +60,9 @@ export class Action implements IAction { protected _enabled: boolean; protected _checked: boolean; protected _radio: boolean; - protected _actionCallback?: (event?: any) => Thenable; + protected _actionCallback?: (event?: any) => Promise; - constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Thenable) { + constructor(id: string, label: string = '', cssClass: string = '', enabled: boolean = true, actionCallback?: (event?: any) => Promise) { this._id = id; this._label = label; this._cssClass = cssClass; @@ -164,7 +164,7 @@ export class Action implements IAction { } } - run(event?: any, _data?: ITelemetryData): Thenable { + run(event?: any, _data?: ITelemetryData): Promise { if (this._actionCallback) { return this._actionCallback(event); } @@ -191,7 +191,7 @@ export class ActionRunner extends Disposable implements IActionRunner { private _onDidRun = this._register(new Emitter()); readonly onDidRun: Event = this._onDidRun.event; - run(action: IAction, context?: any): Thenable { + run(action: IAction, context?: any): Promise { if (!action.enabled) { return Promise.resolve(null); } @@ -205,7 +205,7 @@ export class ActionRunner extends Disposable implements IActionRunner { }); } - protected runAction(action: IAction, context?: any): Thenable { + protected runAction(action: IAction, context?: any): Promise { const res = context ? action.run(context) : action.run(); return Promise.resolve(res); } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index c6f0dba5d78..8a3bb5bdbc0 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -46,13 +46,13 @@ export function equals(one: ReadonlyArray | undefined, other: ReadonlyArra return true; } -export function binarySearch(array: T[], key: T, comparator: (op1: T, op2: T) => number): number { +export function binarySearch(array: ReadonlyArray, key: T, comparator: (op1: T, op2: T) => number): number { let low = 0, high = array.length - 1; while (low <= high) { - let mid = ((low + high) / 2) | 0; - let comp = comparator(array[mid], key); + const mid = ((low + high) / 2) | 0; + const comp = comparator(array[mid], key); if (comp < 0) { low = mid + 1; } else if (comp > 0) { @@ -69,13 +69,13 @@ export function binarySearch(array: T[], key: T, comparator: (op1: T, op2: T) * are located before all elements where p(x) is true. * @returns the least x for which p(x) is true or array.length if no element fullfills the given function. */ -export function findFirstInSorted(array: T[], p: (x: T) => boolean): number { +export function findFirstInSorted(array: ReadonlyArray, 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); + const mid = Math.floor((low + high) / 2); if (p(array[mid])) { high = mid; } else { @@ -122,7 +122,7 @@ function _sort(a: T[], compare: Compare, lo: number, hi: number, aux: T[]) if (hi <= lo) { return; } - let mid = lo + ((hi - lo) / 2) | 0; + const mid = lo + ((hi - lo) / 2) | 0; _sort(a, compare, lo, mid, aux); _sort(a, compare, mid + 1, hi, aux); if (compare(a[mid], a[mid + 1]) <= 0) { @@ -135,7 +135,7 @@ function _sort(a: T[], compare: Compare, lo: number, hi: number, aux: T[]) } -export function groupBy(data: T[], compare: (a: T, b: T) => number): T[][] { +export function groupBy(data: ReadonlyArray, compare: (a: T, b: T) => number): T[][] { const result: T[][] = []; let currentGroup: T[] | undefined = undefined; for (const element of mergeSort(data.slice(0), compare)) { @@ -156,7 +156,7 @@ interface IMutableSplice extends ISplice { /** * Diffs two *sorted* arrays and computes the splices which apply the diff. */ -export function sortedDiff(before: T[], after: T[], compare: (a: T, b: T) => number): ISplice[] { +export function sortedDiff(before: ReadonlyArray, after: ReadonlyArray, compare: (a: T, b: T) => number): ISplice[] { const result: IMutableSplice[] = []; function pushSplice(start: number, deleteCount: number, toInsert: T[]): void { @@ -211,11 +211,8 @@ export function sortedDiff(before: T[], after: T[], compare: (a: T, b: T) => /** * Takes two *sorted* arrays and computes their delta (removed, added elements). * Finishes in `Math.min(before.length, after.length)` steps. - * @param before - * @param after - * @param compare */ -export function delta(before: T[], after: T[], compare: (a: T, b: T) => number): { removed: T[], added: T[] } { +export function delta(before: ReadonlyArray, after: ReadonlyArray, compare: (a: T, b: T) => number): { removed: T[], added: T[] } { const splices = sortedDiff(before, after, compare); const removed: T[] = []; const added: T[] = []; @@ -238,7 +235,7 @@ export function delta(before: T[], after: T[], compare: (a: T, b: T) => numbe * @param n The number of elements to return. * @return The first n elemnts from array when sorted with compare. */ -export function top(array: T[], compare: (a: T, b: T) => number, n: number): T[] { +export function top(array: ReadonlyArray, compare: (a: T, b: T) => number, n: number): T[] { if (n === 0) { return []; } @@ -284,7 +281,7 @@ export function topAsync(array: T[], compare: (a: T, b: T) => number, n: numb }); } -function topStep(array: T[], compare: (a: T, b: T) => number, result: T[], i: number, m: number): void { +function topStep(array: ReadonlyArray, 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) { @@ -298,7 +295,7 @@ function topStep(array: T[], compare: (a: T, b: T) => number, result: T[], i: /** * @returns a new array with all falsy values removed. The original array IS NOT modified. */ -export function coalesce(array: (T | undefined | null)[]): T[] { +export function coalesce(array: Array): T[] { if (!array) { return array; } @@ -308,7 +305,7 @@ export function coalesce(array: (T | undefined | null)[]): T[] { /** * Remove all falsey values from `array`. The original array IS modified. */ -export function coalesceInPlace(array: (T | undefined | null)[]): void { +export function coalesceInPlace(array: Array): void { if (!array) { return; } @@ -330,15 +327,14 @@ export function move(array: any[], from: number, to: number): void { } /** - * @returns {{false}} if the provided object is an array - * and not empty. + * @returns false if the provided object is an array and not empty. */ export function isFalsyOrEmpty(obj: any): boolean { return !Array.isArray(obj) || obj.length === 0; } /** - * @returns {{true}} if the provided object is an array and has at least one element. + * @returns True if the provided object is an array and has at least one element. */ export function isNonEmptyArray(obj: ReadonlyArray | undefined | null): obj is Array { return Array.isArray(obj) && obj.length > 0; @@ -348,7 +344,7 @@ export function isNonEmptyArray(obj: ReadonlyArray | undefined | null): ob * Removes duplicates from the given array. The optional keyFn allows to specify * how elements are checked for equalness by returning a unique string for each. */ -export function distinct(array: T[], keyFn?: (t: T) => string): T[] { +export function distinct(array: ReadonlyArray, keyFn?: (t: T) => string): T[] { if (!keyFn) { return array.filter((element, position) => { return array.indexOf(element) === position; @@ -368,6 +364,18 @@ export function distinct(array: T[], keyFn?: (t: T) => string): T[] { }); } +export function distinctES6(array: ReadonlyArray): T[] { + const seen = new Set(); + return array.filter(element => { + if (seen.has(element)) { + return false; + } + + seen.add(element); + return true; + }); +} + export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { const seen: { [key: string]: boolean; } = Object.create(null); @@ -383,7 +391,19 @@ export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { }; } -export function firstIndex(array: T[] | ReadonlyArray, fn: (item: T) => boolean): number { +export function lastIndex(array: ReadonlyArray, fn: (item: T) => boolean): number { + for (let i = array.length - 1; i >= 0; i--) { + const element = array[i]; + + if (fn(element)) { + return i; + } + } + + return -1; +} + +export function firstIndex(array: ReadonlyArray, fn: (item: T) => boolean): number { for (let i = 0; i < array.length; i++) { const element = array[i]; @@ -395,14 +415,14 @@ export function firstIndex(array: T[] | ReadonlyArray, fn: (item: T) => bo return -1; } -export function first(array: T[] | ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; -export function first(array: T[] | ReadonlyArray, fn: (item: T) => boolean): T | null; -export function first(array: T[] | ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | null = null): T | null { +export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; +export function first(array: ReadonlyArray, fn: (item: T) => boolean): T | undefined; +export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | undefined = undefined): T | undefined { const index = firstIndex(array, fn); return index < 0 ? notFoundValue : array[index]; } -export function commonPrefixLength(one: T[], other: T[], equals: (a: T, b: T) => boolean = (a, b) => a === b): number { +export function commonPrefixLength(one: ReadonlyArray, other: ReadonlyArray, equals: (a: T, b: T) => boolean = (a, b) => a === b): number { let result = 0; for (let i = 0, len = Math.min(one.length, other.length); i < len && equals(one[i], other[i]); i++) { @@ -443,17 +463,17 @@ export function range(arg: number, to?: number): number[] { return result; } -export function fill(num: number, valueFn: () => T, arr: T[] = []): T[] { +export function fill(num: number, value: T, arr: T[] = []): T[] { for (let i = 0; i < num; i++) { - arr[i] = valueFn(); + arr[i] = value; } return arr; } -export function index(array: T[], indexer: (t: T) => string): { [key: string]: T; }; -export function index(array: T[], indexer: (t: T) => string, merger?: (t: T, r: R) => R): { [key: string]: R; }; -export function index(array: T[], indexer: (t: T) => string, merger: (t: T, r: R) => R = t => t as any): { [key: string]: R; } { +export function index(array: ReadonlyArray, indexer: (t: T) => string): { [key: string]: T; }; +export function index(array: ReadonlyArray, indexer: (t: T) => string, merger?: (t: T, r: R) => R): { [key: string]: R; }; +export function index(array: ReadonlyArray, indexer: (t: T) => string, merger: (t: T, r: R) => R = t => t as any): { [key: string]: R; } { return array.reduce((r, t) => { const key = indexer(t); r[key] = merger(t, r[key]); @@ -488,7 +508,6 @@ export function arrayInsert(target: T[], insertIndex: number, insertArr: T[]) /** * Uses Fisher-Yates shuffle to shuffle the given array - * @param array */ export function shuffle(array: T[], _seed?: number): void { let rand: () => number; @@ -498,7 +517,7 @@ export function shuffle(array: T[], _seed?: number): void { // Seeded random number generator in JS. Modified from: // https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript rand = () => { - var x = Math.sin(seed++) * 179426549; // throw away most significant digits and reduce any potential bias + const x = Math.sin(seed++) * 179426549; // throw away most significant digits and reduce any potential bias return x - Math.floor(x); }; } else { @@ -506,8 +525,8 @@ export function shuffle(array: T[], _seed?: number): void { } for (let i = array.length - 1; i > 0; i -= 1) { - let j = Math.floor(rand() * (i + 1)); - let temp = array[i]; + const j = Math.floor(rand() * (i + 1)); + const temp = array[i]; array[i] = array[j]; array[j] = temp; } @@ -553,3 +572,7 @@ export function mapArrayOrNot(items: T | T[], fn: (_: T) => U): U | U[] { items.map(fn) : fn(items); } + +export function asArray(x: T | T[]): T[] { + return Array.isArray(x) ? x : [x]; +} diff --git a/src/vs/base/common/assert.ts b/src/vs/base/common/assert.ts index 0a54ee2e51b..1e227df640e 100644 --- a/src/vs/base/common/assert.ts +++ b/src/vs/base/common/assert.ts @@ -7,7 +7,7 @@ * Throws an error with the provided message if the provided value does not evaluate to a true Javascript value. */ export function ok(value?: any, message?: string) { - if (!value || value === null) { + if (!value) { throw new Error(message ? 'Assertion failed (' + message + ')' : 'Assertion Failed'); } } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 5f0f1682a87..6cb25fa7056 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -6,18 +6,18 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -export function isThenable(obj: any): obj is Thenable { - return obj && typeof (>obj).then === 'function'; +export function isThenable(obj: any): obj is Promise { + return obj && typeof (>obj).then === 'function'; } export interface CancelablePromise extends Promise { cancel(): void; } -export function createCancelablePromise(callback: (token: CancellationToken) => Thenable): CancelablePromise { +export function createCancelablePromise(callback: (token: CancellationToken) => Promise): CancelablePromise { const source = new CancellationTokenSource(); const thenable = callback(source.token); @@ -38,18 +38,21 @@ export function createCancelablePromise(callback: (token: CancellationToken) cancel() { source.cancel(); } - then(resolve?: ((value: T) => TResult1 | Thenable) | undefined | null, reject?: ((reason: any) => TResult2 | Thenable) | undefined | null): Promise { + then(resolve?: ((value: T) => TResult1 | Promise) | undefined | null, reject?: ((reason: any) => TResult2 | Promise) | undefined | null): Promise { return promise.then(resolve, reject); } - catch(reject?: ((reason: any) => TResult | Thenable) | undefined | null): Promise { + catch(reject?: ((reason: any) => TResult | Promise) | undefined | null): Promise { return this.then(undefined, reject); } + finally(onfinally?: (() => void) | undefined | null): Promise { + return promise.finally(onfinally); + } }; } -export function asThenable(callback: () => T | Thenable): Promise { +export function asPromise(callback: () => T | Thenable): Promise { return new Promise((resolve, reject) => { - let item = callback(); + const item = callback(); if (isThenable(item)) { item.then(resolve, reject); } else { @@ -90,9 +93,9 @@ export interface ITask { */ export class Throttler { - private activePromise: Thenable | null; - private queuedPromise: Thenable | null; - private queuedPromiseFactory: ITask> | null; + private activePromise: Promise | null; + private queuedPromise: Promise | null; + private queuedPromiseFactory: ITask> | null; constructor() { this.activePromise = null; @@ -100,7 +103,7 @@ export class Throttler { this.queuedPromiseFactory = null; } - queue(promiseFactory: ITask>): Thenable { + queue(promiseFactory: ITask>): Promise { if (this.activePromise) { this.queuedPromiseFactory = promiseFactory; @@ -142,7 +145,7 @@ export class Sequencer { private current: Promise = Promise.resolve(null); - queue(promiseTask: ITask>): Thenable { + queue(promiseTask: ITask>): Promise { return this.current = this.current.then(() => promiseTask()); } } @@ -173,10 +176,10 @@ export class Sequencer { export class Delayer implements IDisposable { private timeout: any; - private completionPromise: Thenable | null; - private doResolve: ((value?: any | Thenable) => void) | null; - private doReject: (err: any) => void; - private task: ITask> | null; + private completionPromise: Promise | null; + private doResolve: ((value?: any | Promise) => void) | null; + private doReject?: (err: any) => void; + private task: ITask> | null; constructor(public defaultDelay: number) { this.timeout = null; @@ -185,7 +188,7 @@ export class Delayer implements IDisposable { this.task = null; } - trigger(task: ITask>, delay: number = this.defaultDelay): Thenable { + trigger(task: ITask>, delay: number = this.defaultDelay): Promise { this.task = task; this.cancelTimeout(); @@ -219,7 +222,7 @@ export class Delayer implements IDisposable { this.cancelTimeout(); if (this.completionPromise) { - this.doReject(errors.canceled()); + this.doReject!(errors.canceled()); this.completionPromise = null; } } @@ -247,7 +250,7 @@ export class Delayer implements IDisposable { */ export class ThrottledDelayer { - private delayer: Delayer>; + private delayer: Delayer>; private throttler: Throttler; constructor(defaultDelay: number) { @@ -255,8 +258,8 @@ export class ThrottledDelayer { this.throttler = new Throttler(); } - trigger(promiseFactory: ITask>, delay?: number): Thenable { - return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Thenable; + trigger(promiseFactory: ITask>, delay?: number): Promise { + return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Promise; } isTriggered(): boolean { @@ -279,7 +282,7 @@ export class Barrier { private _isOpen: boolean; private _promise: Promise; - private _completePromise: (v: boolean) => void; + private _completePromise!: (v: boolean) => void; constructor() { this._isOpen = false; @@ -302,12 +305,9 @@ export class Barrier { } } -/** - * Replacement for `WinJS.TPromise.timeout`. - */ export function timeout(millis: number): CancelablePromise; -export function timeout(millis: number, token: CancellationToken): Thenable; -export function timeout(millis: number, token?: CancellationToken): CancelablePromise | Thenable { +export function timeout(millis: number, token: CancellationToken): Promise; +export function timeout(millis: number, token?: CancellationToken): CancelablePromise | Promise { if (!token) { return createCancelablePromise(token => timeout(millis, token)); } @@ -321,32 +321,13 @@ export function timeout(millis: number, token?: CancellationToken): CancelablePr }); } -export function disposableTimeout(handler: Function, timeout = 0): IDisposable { +export function disposableTimeout(handler: () => void, timeout = 0): IDisposable { const timer = setTimeout(handler, timeout); - return { - dispose() { - clearTimeout(timer); - } - }; + return toDisposable(() => clearTimeout(timer)); } -/** - * Returns a new promise that joins the provided promise. Upon completion of - * the provided promise the provided function will always be called. This - * method is comparable to a try-finally code block. - * @param promise a promise - * @param callback a function that will be call in the success and error case. - */ -export function always(promise: Thenable, callback: () => void): Promise { - function safeCallback() { - try { - callback(); - } catch (err) { - errors.onUnexpectedError(err); - } - } - promise.then(_ => safeCallback(), _ => safeCallback()); - return Promise.resolve(promise); +export function ignoreErrors(promise: Promise): Promise { + return promise.then(undefined, _ => undefined); } /** @@ -354,16 +335,16 @@ export function always(promise: Thenable, callback: () => void): Promise(promiseFactories: ITask>[]): Promise { +export function sequence(promiseFactories: ITask>[]): Promise { const results: T[] = []; let index = 0; const len = promiseFactories.length; - function next(): Thenable | null { + function next(): Promise | null { return index < len ? promiseFactories[index++]() : null; } - function thenHandler(result: any): Thenable { + function thenHandler(result: any): Promise { if (result !== undefined && result !== null) { results.push(result); } @@ -379,7 +360,7 @@ export function sequence(promiseFactories: ITask>[]): Promise(promiseFactories: ITask>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null): Promise { +export function first(promiseFactories: ITask>[], shouldStop: (t: T) => boolean = t => !!t, defaultValue: T | null = null): Promise { let index = 0; const len = promiseFactories.length; @@ -404,8 +385,8 @@ export function first(promiseFactories: ITask>[], shouldStop: (t: } interface ILimitedTaskFactory { - factory: ITask>; - c: (value?: T | Thenable) => void; + factory: ITask>; + c: (value?: T | Promise) => void; e: (error?: any) => void; } @@ -437,7 +418,7 @@ export class Limiter { // return this.runningPromises + this.outstandingPromises.length; } - queue(factory: ITask>): Thenable { + queue(factory: ITask>): Promise { this._size++; return new Promise((c, e) => { @@ -678,19 +659,6 @@ export class RunOnceWorker extends RunOnceScheduler { } } -export function nfcall(fn: Function, ...args: any[]): Promise; -export function nfcall(fn: Function, ...args: any[]): Promise; -export function nfcall(fn: Function, ...args: any[]): any { - return new Promise((c, e) => fn(...args, (err: any, result: any) => err ? e(err) : c(result))); -} - -export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Thenable; -export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Thenable; -export function ninvoke(thisArg: any, fn: Function, ...args: any[]): any { - return new Promise((resolve, reject) => fn.call(thisArg, ...args, (err: any, result: any) => err ? reject(err) : resolve(result))); -} - - //#region -- run on idle tricks ------------ export interface IdleDeadline { @@ -707,12 +675,12 @@ declare function cancelIdleCallback(handle: number): void; (function () { if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') { - let dummyIdle: IdleDeadline = Object.freeze({ + const dummyIdle: IdleDeadline = Object.freeze({ didTimeout: true, timeRemaining() { return 15; } }); - runWhenIdle = (runner, timeout = 0) => { - let handle = setTimeout(() => runner(dummyIdle), timeout); + runWhenIdle = (runner) => { + const handle = setTimeout(() => runner(dummyIdle)); let disposed = false; return { dispose() { @@ -726,7 +694,7 @@ declare function cancelIdleCallback(handle: number): void; }; } else { runWhenIdle = (runner, timeout?) => { - let handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined); + const handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined); let disposed = false; return { dispose() { @@ -750,8 +718,8 @@ export class IdleValue { private readonly _executor: () => void; private readonly _handle: IDisposable; - private _didRun: boolean; - private _value: T; + private _didRun: boolean = false; + private _value?: T; private _error: any; constructor(executor: () => T) { @@ -779,8 +747,24 @@ export class IdleValue { if (this._error) { throw this._error; } - return this._value; + return this._value!; } } //#endregion + +export async function retry(task: ITask>, delay: number, retries: number): Promise { + let lastError: Error | undefined; + + for (let i = 0; i < retries; i++) { + try { + return await task(); + } catch (error) { + lastError = error; + + await timeout(delay); + } + } + + return Promise.reject(lastError); +} \ No newline at end of file diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts new file mode 100644 index 00000000000..cf34296e74c --- /dev/null +++ b/src/vs/base/common/buffer.ts @@ -0,0 +1,440 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare var Buffer: any; +export const hasBuffer = (typeof Buffer !== 'undefined'); + +let textEncoder: TextEncoder | null; +let textDecoder: TextDecoder | null; + +export class VSBuffer { + + static alloc(byteLength: number): VSBuffer { + if (hasBuffer) { + return new VSBuffer(Buffer.allocUnsafe(byteLength)); + } else { + return new VSBuffer(new Uint8Array(byteLength)); + } + } + + static wrap(actual: Uint8Array): VSBuffer { + if (hasBuffer && !(Buffer.isBuffer(actual))) { + // https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length + // Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array + actual = Buffer.from(actual.buffer, actual.byteOffset, actual.byteLength); + } + return new VSBuffer(actual); + } + + static fromString(source: string): VSBuffer { + if (hasBuffer) { + return new VSBuffer(Buffer.from(source)); + } else { + if (!textEncoder) { + textEncoder = new TextEncoder(); + } + return new VSBuffer(textEncoder.encode(source)); + } + } + + static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer { + if (typeof totalLength === 'undefined') { + totalLength = 0; + for (let i = 0, len = buffers.length; i < len; i++) { + totalLength += buffers[i].byteLength; + } + } + + const ret = VSBuffer.alloc(totalLength); + let offset = 0; + for (let i = 0, len = buffers.length; i < len; i++) { + const element = buffers[i]; + ret.set(element, offset); + offset += element.byteLength; + } + + return ret; + } + + readonly buffer: Uint8Array; + readonly byteLength: number; + + private constructor(buffer: Uint8Array) { + this.buffer = buffer; + this.byteLength = this.buffer.byteLength; + } + + toString(): string { + if (hasBuffer) { + return this.buffer.toString(); + } else { + if (!textDecoder) { + textDecoder = new TextDecoder(); + } + return textDecoder.decode(this.buffer); + } + } + + slice(start?: number, end?: number): VSBuffer { + return new VSBuffer(this.buffer.slice(start, end)); + } + + set(array: VSBuffer, offset?: number): void { + this.buffer.set(array.buffer, offset); + } + + readUInt32BE(offset: number): number { + return readUInt32BE(this.buffer, offset); + } + + writeUInt32BE(value: number, offset: number): void { + writeUInt32BE(this.buffer, value, offset); + } + + readUInt8(offset: number): number { + return readUInt8(this.buffer, offset); + } + + writeUInt8(value: number, offset: number): void { + writeUInt8(this.buffer, value, offset); + } +} + +function readUInt32BE(source: Uint8Array, offset: number): number { + return ( + source[offset] * 2 ** 24 + + source[offset + 1] * 2 ** 16 + + source[offset + 2] * 2 ** 8 + + source[offset + 3] + ); +} + +function writeUInt32BE(destination: Uint8Array, value: number, offset: number): void { + destination[offset + 3] = value; + value = value >>> 8; + destination[offset + 2] = value; + value = value >>> 8; + destination[offset + 1] = value; + value = value >>> 8; + destination[offset] = value; +} + +function readUInt8(source: Uint8Array, offset: number): number { + return source[offset]; +} + +function writeUInt8(destination: Uint8Array, value: number, offset: number): void { + destination[offset] = value; +} + +export interface VSBufferReadable { + + /** + * Read data from the underlying source. Will return + * null to indicate that no more data can be read. + */ + read(): VSBuffer | null; +} + +/** + * A buffer readable stream emits data to listeners. The stream + * will only start emitting when the first data listener has + * been added or the resume() method has been called. + */ +export interface VSBufferReadableStream { + + /** + * The 'data' event is emitted whenever the stream is + * relinquishing ownership of a chunk of data to a consumer. + */ + on(event: 'data', callback: (chunk: VSBuffer) => void): void; + + /** + * Emitted when any error occurs. + */ + on(event: 'error', callback: (err: any) => void): void; + + /** + * The 'end' event is emitted when there is no more data + * to be consumed from the stream. The 'end' event will + * not be emitted unless the data is completely consumed. + */ + on(event: 'end', callback: () => void): void; + + /** + * Stops emitting any events until resume() is called. + */ + pause(): void; + + /** + * Starts emitting events again after pause() was called. + */ + resume(): void; + + /** + * Destroys the stream and stops emitting any event. + */ + destroy(): void; +} + +/** + * Helper to fully read a VSBuffer readable into a single buffer. + */ +export function readableToBuffer(readable: VSBufferReadable): VSBuffer { + const chunks: VSBuffer[] = []; + + let chunk: VSBuffer | null; + while (chunk = readable.read()) { + chunks.push(chunk); + } + + return VSBuffer.concat(chunks); +} + +/** + * Helper to convert a buffer into a readable buffer. + */ +export function bufferToReadable(buffer: VSBuffer): VSBufferReadable { + let done = false; + + return { + read: () => { + if (done) { + return null; + } + + done = true; + + return buffer; + } + }; +} + +/** + * Helper to fully read a VSBuffer stream into a single buffer. + */ +export function streamToBuffer(stream: VSBufferReadableStream): Promise { + return new Promise((resolve, reject) => { + const chunks: VSBuffer[] = []; + + stream.on('data', chunk => chunks.push(chunk)); + stream.on('error', error => reject(error)); + stream.on('end', () => resolve(VSBuffer.concat(chunks))); + }); +} + +/** + * Helper to create a VSBufferStream from an existing VSBuffer. + */ +export function bufferToStream(buffer: VSBuffer): VSBufferReadableStream { + const stream = writeableBufferStream(); + + stream.end(buffer); + + return stream; +} + +/** + * Helper to create a VSBufferStream that can be pushed + * buffers to. Will only start to emit data when a listener + * is added. + */ +export function writeableBufferStream(): VSBufferWriteableStream { + return new VSBufferWriteableStreamImpl(); +} + +export interface VSBufferWriteableStream extends VSBufferReadableStream { + write(chunk: VSBuffer): void; + error(error: Error): void; + end(result?: VSBuffer | Error): void; +} + +class VSBufferWriteableStreamImpl implements VSBufferWriteableStream { + + private readonly state = { + flowing: false, + ended: false, + destroyed: false + }; + + private readonly buffer = { + data: [] as VSBuffer[], + error: [] as Error[] + }; + + private readonly listeners = { + data: [] as { (chunk: VSBuffer): void }[], + error: [] as { (error: Error): void }[], + end: [] as { (): void }[] + }; + + pause(): void { + if (this.state.destroyed) { + return; + } + + this.state.flowing = false; + } + + resume(): void { + if (this.state.destroyed) { + return; + } + + if (!this.state.flowing) { + this.state.flowing = true; + + // emit buffered events + this.flowData(); + this.flowErrors(); + this.flowEnd(); + } + } + + write(chunk: VSBuffer): void { + if (this.state.destroyed) { + return; + } + + // flowing: directly send the data to listeners + if (this.state.flowing) { + this.listeners.data.forEach(listener => listener(chunk)); + } + + // not yet flowing: buffer data until flowing + else { + this.buffer.data.push(chunk); + } + } + + error(error: Error): void { + if (this.state.destroyed) { + return; + } + + // flowing: directly send the error to listeners + if (this.state.flowing) { + this.listeners.error.forEach(listener => listener(error)); + } + + // not yet flowing: buffer errors until flowing + else { + this.buffer.error.push(error); + } + } + + end(result?: VSBuffer | Error): void { + if (this.state.destroyed) { + return; + } + + // end with data or error if provided + if (result instanceof Error) { + this.error(result); + } else if (result) { + this.write(result); + } + + // flowing: send end event to listeners + if (this.state.flowing) { + this.listeners.end.forEach(listener => listener()); + + this.destroy(); + } + + // not yet flowing: remember state + else { + this.state.ended = true; + } + } + + on(event: 'data', callback: (chunk: VSBuffer) => void): void; + on(event: 'error', callback: (err: any) => void): void; + on(event: 'end', callback: () => void): void; + on(event: 'data' | 'error' | 'end', callback: (arg0?: any) => void): void { + if (this.state.destroyed) { + return; + } + + switch (event) { + case 'data': + this.listeners.data.push(callback); + + // switch into flowing mode as soon as the first 'data' + // listener is added and we are not yet in flowing mode + this.resume(); + + break; + + case 'end': + this.listeners.end.push(callback); + + // emit 'end' event directly if we are flowing + // and the end has already been reached + // + // finish() when it went through + if (this.state.flowing && this.flowEnd()) { + this.destroy(); + } + + break; + + case 'error': + this.listeners.error.push(callback); + + // emit buffered 'error' events unless done already + // now that we know that we have at least one listener + if (this.state.flowing) { + this.flowErrors(); + } + + break; + } + } + + private flowData(): void { + if (this.buffer.data.length > 0) { + const fullDataBuffer = VSBuffer.concat(this.buffer.data); + + this.listeners.data.forEach(listener => listener(fullDataBuffer)); + + this.buffer.data.length = 0; + } + } + + private flowErrors(): void { + if (this.listeners.error.length > 0) { + for (const error of this.buffer.error) { + this.listeners.error.forEach(listener => listener(error)); + } + + this.buffer.error.length = 0; + } + } + + private flowEnd(): boolean { + if (this.state.ended) { + this.listeners.end.forEach(listener => listener()); + + return this.listeners.end.length > 0; + } + + return false; + } + + destroy(): void { + if (!this.state.destroyed) { + this.state.destroyed = true; + this.state.ended = true; + + this.buffer.data.length = 0; + this.buffer.error.length = 0; + + this.listeners.data.length = 0; + this.listeners.error.length = 0; + this.listeners.end.length = 0; + } + } +} \ No newline at end of file diff --git a/src/vs/base/common/buildunit.json b/src/vs/base/common/buildunit.json index fecb5c6eac6..50e3d750676 100644 --- a/src/vs/base/common/buildunit.json +++ b/src/vs/base/common/buildunit.json @@ -1,7 +1,10 @@ { "name": "vs/base", "dependencies": [ - { "name": "vs", "internal": false } + { + "name": "vs", + "internal": false + } ], "libs": [ "lib.core.d.ts" @@ -9,7 +12,5 @@ "sources": [ "**/*.ts" ], - "declares": [ - "vs/base/winjs.base.d.ts" - ] -} \ No newline at end of file + "declares": [] +} diff --git a/src/vs/base/common/cache.ts b/src/vs/base/common/cache.ts index 42c07e4d289..f46d663f773 100644 --- a/src/vs/base/common/cache.ts +++ b/src/vs/base/common/cache.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { always } from 'vs/base/common/async'; export interface CacheResult { promise: Promise; @@ -23,7 +22,7 @@ export class Cache { const cts = new CancellationTokenSource(); const promise = this.task(cts.token); - always(promise, () => cts.dispose()); + promise.finally(() => cts.dispose()); this.result = { promise, diff --git a/src/vs/base/common/cancellation.ts b/src/vs/base/common/cancellation.ts index 39f672c9751..fb274fb941f 100644 --- a/src/vs/base/common/cancellation.ts +++ b/src/vs/base/common/cancellation.ts @@ -16,7 +16,7 @@ export interface CancellationToken { } const shortcutEvent = Object.freeze(function (callback, context?): IDisposable { - let handle = setTimeout(callback.bind(context), 0); + const handle = setTimeout(callback.bind(context), 0); return { dispose() { clearTimeout(handle); } }; } as Event); @@ -87,7 +87,12 @@ class MutableToken implements CancellationToken { export class CancellationTokenSource { - private _token: CancellationToken; + private _token?: CancellationToken = undefined; + private _parentListener?: IDisposable = undefined; + + constructor(parent?: CancellationToken) { + this._parentListener = parent && parent.onCancellationRequested(this.cancel, this); + } get token(): CancellationToken { if (!this._token) { @@ -112,6 +117,9 @@ export class CancellationTokenSource { } dispose(): void { + if (this._parentListener) { + this._parentListener.dispose(); + } if (!this._token) { // ensure to initialize with an empty token if we had none this._token = CancellationToken.None; diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index 05758a81246..f5c5cf5408b 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -58,7 +58,7 @@ export function first(from: IStringDictionary | INumberDictionary): T | * Iterates over each entry in the provided set. The iterator allows * to remove elements and will stop when the callback returns {{false}}. */ -export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: Function) => any): void { +export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T; }, remove: () => void) => any): void { for (let key in from) { if (hasOwnProperty.call(from, key)) { const result = callback({ key: key, value: (from as any)[key] }, function () { @@ -71,18 +71,6 @@ export function forEach(from: IStringDictionary | INumberDictionary, ca } } -/** - * Removes an element from the dictionary. Returns {{false}} if the property - * does not exists. - */ -export function remove(from: IStringDictionary | INumberDictionary, key: string): boolean { - if (!hasOwnProperty.call(from, key)) { - return false; - } - delete (from as any)[key]; - return true; -} - /** * Groups the collection into a dictionary based on the provided * group function. @@ -99,3 +87,13 @@ export function groupBy(data: T[], groupFn: (element: T) => string): IStringD } return result; } + +export function fromMap(original: Map): IStringDictionary { + const result: IStringDictionary = Object.create(null); + if (original) { + original.forEach((value, key) => { + result[key] = value; + }); + } + return result; +} \ No newline at end of file diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index a8df525c7cb..163ecbe74cd 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -260,7 +260,7 @@ export class Color { } readonly rgba: RGBA; - private _hsla: HSLA; + private _hsla?: HSLA; get hsla(): HSLA { if (this._hsla) { return this._hsla; @@ -269,7 +269,7 @@ export class Color { } } - private _hsva: HSVA; + private _hsva?: HSVA; get hsva(): HSVA { if (this._hsva) { return this._hsva; @@ -387,8 +387,8 @@ export class Color { const thisA = this.rgba.a; const colorA = rgba.a; - let a = thisA + colorA * (1 - thisA); - if (a < 1.0e-6) { + const a = thisA + colorA * (1 - thisA); + if (a < 1e-6) { return Color.transparent; } diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index 3a73fa7d4f4..adf5859c794 100644 --- a/src/vs/base/common/comparers.ts +++ b/src/vs/base/common/comparers.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from 'vs/base/common/strings'; -import * as paths from 'vs/base/common/paths'; +import { sep } from 'vs/base/common/path'; import { IdleValue } from 'vs/base/common/async'; let intlFileNameCollator: IdleValue<{ collator: Intl.Collator, collatorIsNumeric: boolean }>; @@ -13,7 +13,7 @@ export function setFileNameComparer(collator: IdleValue<{ collator: Intl.Collato intlFileNameCollator = collator; } -export function compareFileNames(one: string, other: string, caseSensitive = false): number { +export function compareFileNames(one: string | null, other: string | null, caseSensitive = false): number { if (intlFileNameCollator) { const a = one || ''; const b = other || ''; @@ -33,7 +33,7 @@ export function compareFileNames(one: string, other: string, caseSensitive = fal const FileNameMatch = /^(.*?)(\.([^.]*))?$/; -export function noIntlCompareFileNames(one: string, other: string, caseSensitive = false): number { +export function noIntlCompareFileNames(one: string | null, other: string | null, caseSensitive = false): number { if (!caseSensitive) { one = one && one.toLowerCase(); other = other && other.toLowerCase(); @@ -53,7 +53,7 @@ export function noIntlCompareFileNames(one: string, other: string, caseSensitive return oneExtension < otherExtension ? -1 : 1; } -export function compareFileExtensions(one: string, other: string): number { +export function compareFileExtensions(one: string | null, other: string | null): number { if (intlFileNameCollator) { const [oneName, oneExtension] = extractNameAndExtension(one); const [otherName, otherExtension] = extractNameAndExtension(other); @@ -81,7 +81,7 @@ export function compareFileExtensions(one: string, other: string): number { return noIntlCompareFileExtensions(one, other); } -function noIntlCompareFileExtensions(one: string, other: string): number { +function noIntlCompareFileExtensions(one: string | null, other: string | null): number { const [oneName, oneExtension] = extractNameAndExtension(one && one.toLowerCase()); const [otherName, otherExtension] = extractNameAndExtension(other && other.toLowerCase()); @@ -96,7 +96,7 @@ function noIntlCompareFileExtensions(one: string, other: string): number { return oneName < otherName ? -1 : 1; } -function extractNameAndExtension(str?: string): [string, string] { +function extractNameAndExtension(str?: string | null): [string, string] { const match = str ? FileNameMatch.exec(str) as Array : ([] as Array); return [(match && match[1]) || '', (match && match[3]) || '']; @@ -116,8 +116,8 @@ function comparePathComponents(one: string, other: string, caseSensitive = false } export function comparePaths(one: string, other: string, caseSensitive = false): number { - const oneParts = one.split(paths.nativeSep); - const otherParts = other.split(paths.nativeSep); + const oneParts = one.split(sep); + const otherParts = other.split(sep); const lastOne = oneParts.length - 1; const lastOther = otherParts.length - 1; @@ -144,8 +144,8 @@ export function comparePaths(one: string, other: string, caseSensitive = false): } export function compareAnything(one: string, other: string, lookFor: string): number { - let elementAName = one.toLowerCase(); - let elementBName = other.toLowerCase(); + const elementAName = one.toLowerCase(); + const elementBName = other.toLowerCase(); // Sort prefix matches over non prefix matches const prefixCompare = compareByPrefix(one, other, lookFor); @@ -154,14 +154,14 @@ export function compareAnything(one: string, other: string, lookFor: string): nu } // Sort suffix matches over non suffix matches - let elementASuffixMatch = strings.endsWith(elementAName, lookFor); - let elementBSuffixMatch = strings.endsWith(elementBName, lookFor); + const elementASuffixMatch = strings.endsWith(elementAName, lookFor); + const elementBSuffixMatch = strings.endsWith(elementBName, lookFor); if (elementASuffixMatch !== elementBSuffixMatch) { return elementASuffixMatch ? -1 : 1; } // Understand file names - let r = compareFileNames(elementAName, elementBName); + const r = compareFileNames(elementAName, elementBName); if (r !== 0) { return r; } @@ -171,12 +171,12 @@ export function compareAnything(one: string, other: string, lookFor: string): nu } export function compareByPrefix(one: string, other: string, lookFor: string): number { - let elementAName = one.toLowerCase(); - let elementBName = other.toLowerCase(); + const elementAName = one.toLowerCase(); + const elementBName = other.toLowerCase(); // Sort prefix matches over non prefix matches - let elementAPrefixMatch = strings.startsWith(elementAName, lookFor); - let elementBPrefixMatch = strings.startsWith(elementBName, lookFor); + const elementAPrefixMatch = strings.startsWith(elementAName, lookFor); + const elementBPrefixMatch = strings.startsWith(elementBName, lookFor); if (elementAPrefixMatch !== elementBPrefixMatch) { return elementAPrefixMatch ? -1 : 1; } diff --git a/src/vs/base/node/console.ts b/src/vs/base/common/console.ts similarity index 99% rename from src/vs/base/node/console.ts rename to src/vs/base/common/console.ts index 047bca70f27..ccd30d07a1e 100644 --- a/src/vs/base/node/console.ts +++ b/src/vs/base/common/console.ts @@ -79,11 +79,11 @@ export function getFirstFrame(arg0: IRemoteConsoleLog | string | undefined): ISt uri: URI.file(matches[1]), line: Number(matches[2]), column: Number(matches[3]) - } as IStackFrame; + }; } } - return void 0; + return undefined; } function findFirstFrame(stack: string | undefined): string | undefined { diff --git a/src/vs/base/common/decorators.ts b/src/vs/base/common/decorators.ts index 01478d3c3ad..e48ad1564e5 100644 --- a/src/vs/base/common/decorators.ts +++ b/src/vs/base/common/decorators.ts @@ -71,7 +71,7 @@ export function debounce(delay: number, reducer?: IDebouceReducer, initial return function (this: any, ...args: any[]) { if (!this[resultKey]) { - this[resultKey] = initialValueProvider ? initialValueProvider() : void 0; + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; } clearTimeout(this[timerKey]); @@ -83,7 +83,7 @@ export function debounce(delay: number, reducer?: IDebouceReducer, initial this[timerKey] = setTimeout(() => { fn.apply(this, args); - this[resultKey] = initialValueProvider ? initialValueProvider() : void 0; + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; }, delay); }; }); diff --git a/src/vs/base/common/diff/diff.ts b/src/vs/base/common/diff/diff.ts index c7f09694da7..6eb26b885af 100644 --- a/src/vs/base/common/diff/diff.ts +++ b/src/vs/base/common/diff/diff.ts @@ -763,7 +763,7 @@ export class LcsDiff { change.modifiedStart++; } - let mergedChangeArr: (DiffChange | null)[] = [null]; + let mergedChangeArr: Array = [null]; if (i < changes.length - 1 && this.ChangesOverlap(changes[i], changes[i + 1], mergedChangeArr)) { changes[i] = mergedChangeArr[0]!; changes.splice(i + 1, 1); @@ -913,7 +913,7 @@ export class LcsDiff { * @param mergedChange The merged change if the two overlap, null otherwise * @returns True if the two changes overlap */ - private ChangesOverlap(left: DiffChange, right: DiffChange, mergedChangeArr: (DiffChange | null)[]): boolean { + private ChangesOverlap(left: DiffChange, right: DiffChange, mergedChangeArr: Array): boolean { Debug.Assert(left.originalStart <= right.originalStart, 'Left change is not less than or equal to right change'); Debug.Assert(left.modifiedStart <= right.modifiedStart, 'Left change is not less than or equal to right change'); diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index e760a55d65b..da6f814332f 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -3,59 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise, IPromiseError, IPromiseErrorDetail } from 'vs/base/common/winjs.base'; - -// ------ BEGIN Hook up error listeners to winjs promises - -let outstandingPromiseErrors: { [id: string]: IPromiseErrorDetail; } = {}; -function promiseErrorHandler(e: IPromiseError): void { - - // - // e.detail looks like: { exception, error, promise, handler, id, parent } - // - const details = e.detail; - const id = details.id; - - // If the error has a parent promise then this is not the origination of the - // error so we check if it has a handler, and if so we mark that the error - // was handled by removing it from outstandingPromiseErrors - // - if (details.parent) { - if (details.handler && outstandingPromiseErrors) { - delete outstandingPromiseErrors[id]; - } - return; - } - - // Indicate that this error was originated and needs to be handled - outstandingPromiseErrors[id] = details; - - // The first time the queue fills up this iteration, schedule a timeout to - // check if any errors are still unhandled. - if (Object.keys(outstandingPromiseErrors).length === 1) { - setTimeout(function () { - const errors = outstandingPromiseErrors; - outstandingPromiseErrors = {}; - Object.keys(errors).forEach(function (errorId) { - const error = errors[errorId]; - if (error.exception) { - onUnexpectedError(error.exception); - } else if (error.error) { - onUnexpectedError(error.error); - } - console.log('WARNING: Promise with no error callback:' + error.id); - console.log(error); - if (error.exception) { - console.log(error.exception.stack); - } - }); - }, 0); - } -} -TPromise.addEventListener('error', promiseErrorHandler); - -// ------ END Hook up error listeners to winjs promises - export interface ErrorListenerCallback { (error: any): void; } @@ -155,7 +102,7 @@ export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { if (error instanceof Error) { let { name, message } = error; - let stack: string = (error).stacktrace || (error).stack; + const stack: string = (error).stacktrace || (error).stack; return { $isError: true, name, @@ -199,7 +146,7 @@ export function isPromiseCanceledError(error: any): boolean { * Returns an error that signals cancellation. */ export function canceled(): Error { - let error = new Error(canceledName); + const error = new Error(canceledName); error.name = error.message; return error; } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 066ea8ffe6d..0ef9379e6a5 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -19,9 +19,338 @@ export interface Event { export namespace Event { const _disposable = { dispose() { } }; export const None: Event = function () { return _disposable; }; + + /** + * Given an event, returns another event which only fires once. + */ + export function once(event: Event): Event { + return (listener, thisArgs = null, disposables?) => { + // we need this, in case the event fires during the listener call + let didFire = false; + let result: IDisposable; + result = event(e => { + if (didFire) { + return; + } else if (result) { + result.dispose(); + } else { + didFire = true; + } + + return listener.call(thisArgs, e); + }, null, disposables); + + if (didFire) { + result.dispose(); + } + + return result; + }; + } + + /** + * Given an event and a `map` function, returns another event which maps each element + * throught the mapping function. + */ + export function map(event: Event, map: (i: I) => O): Event { + return snapshot((listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables)); + } + + /** + * Given an event and an `each` function, returns another identical event and calls + * the `each` function per each element. + */ + export function forEach(event: Event, each: (i: I) => void): Event { + return snapshot((listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables)); + } + + /** + * Given an event and a `filter` function, returns another event which emits those + * elements for which the `filter` function returns `true`. + */ + export function filter(event: Event, filter: (e: T) => boolean): Event; + export function filter(event: Event, filter: (e: T | R) => e is R): Event; + export function filter(event: Event, filter: (e: T) => boolean): Event { + return snapshot((listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables)); + } + + /** + * Given an event, returns the same event but typed as `Event`. + */ + export function signal(event: Event): Event { + return event as Event as Event; + } + + /** + * Given a collection of events, returns a single event which emits + * whenever any of the provided events emit. + */ + export function any(...events: Event[]): Event { + return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); + } + + /** + * Given an event and a `merge` function, returns another event which maps each element + * and the cummulative result throught the `merge` function. Similar to `map`, but with memory. + */ + export function reduce(event: Event, merge: (last: O | undefined, event: I) => O, initial?: O): Event { + let output: O | undefined = initial; + + return map(event, e => { + output = merge(output, e); + return output; + }); + } + + /** + * Given a chain of event processing functions (filter, map, etc), each + * function will be invoked per event & per listener. Snapshotting an event + * chain allows each function to be invoked just once per event. + */ + export function snapshot(event: Event): Event { + let listener: IDisposable; + const emitter = new Emitter({ + onFirstListenerAdd() { + listener = event(emitter.fire, emitter); + }, + onLastListenerRemove() { + listener.dispose(); + } + }); + + return emitter.event; + } + + /** + * Debounces the provided event, given a `merge` function. + * + * @param event The input event. + * @param merge The reducing function. + * @param delay The debouncing delay in millis. + * @param leading Whether the event should fire in the leading phase of the timeout. + * @param leakWarningThreshold The leak warning threshold override. + */ + export function debounce(event: Event, merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event; + export function debounce(event: Event, merge: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event; + export function debounce(event: Event, merge: (last: O | undefined, event: I) => O, delay: number = 100, leading = false, leakWarningThreshold?: number): Event { + + let subscription: IDisposable; + let output: O | undefined = undefined; + let handle: any = undefined; + let numDebouncedCalls = 0; + + const emitter = new Emitter({ + leakWarningThreshold, + onFirstListenerAdd() { + subscription = event(cur => { + numDebouncedCalls++; + output = merge(output, cur); + + if (leading && !handle) { + emitter.fire(output); + } + + clearTimeout(handle); + handle = setTimeout(() => { + const _output = output; + output = undefined; + handle = undefined; + if (!leading || numDebouncedCalls > 1) { + emitter.fire(_output!); + } + + numDebouncedCalls = 0; + }, delay); + }); + }, + onLastListenerRemove() { + subscription.dispose(); + } + }); + + return emitter.event; + } + + /** + * Given an event, it returns another event which fires only once and as soon as + * the input event emits. The event data is the number of millis it took for the + * event to fire. + */ + export function stopwatch(event: Event): Event { + const start = new Date().getTime(); + return map(once(event), _ => new Date().getTime() - start); + } + + /** + * Given an event, it returns another event which fires only when the event + * element changes. + */ + export function latch(event: Event): Event { + let firstCall = true; + let cache: T; + + return filter(event, value => { + const shouldEmit = firstCall || value !== cache; + firstCall = false; + cache = value; + return shouldEmit; + }); + } + + /** + * Buffers the provided event until a first listener comes + * along, at which point fire all the events at once and + * pipe the event from then on. + * + * ```typescript + * const emitter = new Emitter(); + * const event = emitter.event; + * const bufferedEvent = buffer(event); + * + * emitter.fire(1); + * emitter.fire(2); + * emitter.fire(3); + * // nothing... + * + * const listener = bufferedEvent(num => console.log(num)); + * // 1, 2, 3 + * + * emitter.fire(4); + * // 4 + * ``` + */ + export function buffer(event: Event, nextTick = false, _buffer: T[] = []): Event { + let buffer: T[] | null = _buffer.slice(); + + let listener: IDisposable | null = event(e => { + if (buffer) { + buffer.push(e); + } else { + emitter.fire(e); + } + }); + + const flush = () => { + if (buffer) { + buffer.forEach(e => emitter.fire(e)); + } + buffer = null; + }; + + const emitter = new Emitter({ + onFirstListenerAdd() { + if (!listener) { + listener = event(e => emitter.fire(e)); + } + }, + + onFirstListenerDidAdd() { + if (buffer) { + if (nextTick) { + setTimeout(flush); + } else { + flush(); + } + } + }, + + onLastListenerRemove() { + if (listener) { + listener.dispose(); + } + listener = null; + } + }); + + return emitter.event; + } + + export interface IChainableEvent { + event: Event; + map(fn: (i: T) => O): IChainableEvent; + forEach(fn: (i: T) => void): IChainableEvent; + filter(fn: (e: T) => boolean): IChainableEvent; + reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent; + latch(): IChainableEvent; + on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; + once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; + } + + class ChainableEvent implements IChainableEvent { + + constructor(readonly event: Event) { } + + map(fn: (i: T) => O): IChainableEvent { + return new ChainableEvent(map(this.event, fn)); + } + + forEach(fn: (i: T) => void): IChainableEvent { + return new ChainableEvent(forEach(this.event, fn)); + } + + filter(fn: (e: T) => boolean): IChainableEvent { + return new ChainableEvent(filter(this.event, fn)); + } + + reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent { + return new ChainableEvent(reduce(this.event, merge, initial)); + } + + latch(): IChainableEvent { + return new ChainableEvent(latch(this.event)); + } + + on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { + return this.event(listener, thisArgs, disposables); + } + + once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { + return once(this.event)(listener, thisArgs, disposables); + } + } + + export function chain(event: Event): IChainableEvent { + return new ChainableEvent(event); + } + + export interface NodeEventEmitter { + on(event: string | symbol, listener: Function): this; + removeListener(event: string | symbol, listener: Function): this; + } + + export function fromNodeEventEmitter(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event { + const fn = (...args: any[]) => result.fire(map(...args)); + const onFirstListenerAdd = () => emitter.on(eventName, fn); + const onLastListenerRemove = () => emitter.removeListener(eventName, fn); + const result = new Emitter({ onFirstListenerAdd, onLastListenerRemove }); + + return result.event; + } + + export function fromPromise(promise: Promise): Event { + const emitter = new Emitter(); + let shouldEmit = false; + + promise + .then(undefined, () => null) + .then(() => { + if (!shouldEmit) { + setTimeout(() => emitter.fire(undefined), 0); + } else { + emitter.fire(undefined); + } + }); + + shouldEmit = true; + return emitter.event; + } + + export function toPromise(event: Event): Promise { + return new Promise(c => once(event)(c)); + } } -type Listener = [Function, any] | Function; +type Listener = [(e: T) => void, any] | ((e: T) => void); export interface EmitterOptions { onFirstListenerAdd?: Function; @@ -33,7 +362,7 @@ export interface EmitterOptions { let _globalLeakWarningThreshold = -1; export function setGlobalLeakWarningThreshold(n: number): IDisposable { - let oldValue = _globalLeakWarningThreshold; + const oldValue = _globalLeakWarningThreshold; _globalLeakWarningThreshold = n; return { dispose() { @@ -58,40 +387,48 @@ class LeakageMonitor { } } - check(listenerCount: number): void { + check(listenerCount: number): undefined | (() => void) { let threshold = _globalLeakWarningThreshold; if (typeof this.customThreshold === 'number') { threshold = this.customThreshold; } - if (threshold > 1 && threshold < listenerCount) { - if (!this._stacks) { - this._stacks = new Map(); - } - let stack = new Error().stack!.split('\n').slice(3).join('\n'); - let count = (this._stacks.get(stack) || 0) + 1; - this._stacks.set(stack, count); - this._warnCountdown -= 1; - if (this._warnCountdown <= 0) { - // only warn on first exceed and then every time the limit - // is exceeded by 50% again - this._warnCountdown = threshold * .5; - - // find most frequent listener and print warning - let topStack: string; - let topCount: number = 0; - this._stacks.forEach((count, stack) => { - if (!topStack || topCount < count) { - topStack = stack; - topCount = count; - } - }); - - console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`); - console.warn(topStack!); - } + if (threshold <= 0 || listenerCount < threshold) { + return undefined; } + + if (!this._stacks) { + this._stacks = new Map(); + } + const stack = new Error().stack!.split('\n').slice(3).join('\n'); + const count = (this._stacks.get(stack) || 0); + this._stacks.set(stack, count + 1); + this._warnCountdown -= 1; + + if (this._warnCountdown <= 0) { + // only warn on first exceed and then every time the limit + // is exceeded by 50% again + this._warnCountdown = threshold * 0.5; + + // find most frequent listener and print warning + let topStack: string; + let topCount: number = 0; + this._stacks.forEach((count, stack) => { + if (!topStack || topCount < count) { + topStack = stack; + topCount = count; + } + }); + + console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`); + console.warn(topStack!); + } + + return () => { + const count = (this._stacks!.get(stack) || 0); + this._stacks!.set(stack, count - 1); + }; } } @@ -120,16 +457,18 @@ export class Emitter { private static readonly _noop = function () { }; - private readonly _options: EmitterOptions | undefined; - private readonly _leakageMon: LeakageMonitor; + private readonly _options?: EmitterOptions; + private readonly _leakageMon?: LeakageMonitor; private _disposed: boolean = false; - private _event: Event | undefined; - private _deliveryQueue: [Listener, (T | undefined)][] | undefined; - protected _listeners: LinkedList | undefined; + private _event?: Event; + private _deliveryQueue?: LinkedList<[Listener, T]>; + protected _listeners?: LinkedList>; constructor(options?: EmitterOptions) { this._options = options; - this._leakageMon = new LeakageMonitor(this._options && this._options.leakWarningThreshold); + this._leakageMon = _globalLeakWarningThreshold > 0 + ? new LeakageMonitor(this._options && this._options.leakWarningThreshold) + : undefined; } /** @@ -160,11 +499,17 @@ export class Emitter { } // check and record this emitter for potential leakage - this._leakageMon.check(this._listeners.size); + let removeMonitor: (() => void) | undefined; + if (this._leakageMon) { + removeMonitor = this._leakageMon.check(this._listeners.size); + } let result: IDisposable; result = { dispose: () => { + if (removeMonitor) { + removeMonitor(); + } result.dispose = Emitter._noop; if (!this._disposed) { remove(); @@ -191,21 +536,21 @@ export class Emitter { * To be kept private to fire an event to * subscribers */ - fire(event?: T): any { + fire(event: T): void { if (this._listeners) { // put all [listener,event]-pairs into delivery queue // then emit all event. an inner/nested event might be // the driver of this if (!this._deliveryQueue) { - this._deliveryQueue = []; + this._deliveryQueue = new LinkedList(); } for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { this._deliveryQueue.push([e.value, event]); } - while (this._deliveryQueue.length > 0) { + while (this._deliveryQueue.size > 0) { const [listener, event] = this._deliveryQueue.shift()!; try { if (typeof listener === 'function') { @@ -222,25 +567,72 @@ export class Emitter { dispose() { if (this._listeners) { - this._listeners = undefined; + this._listeners.clear(); } if (this._deliveryQueue) { - this._deliveryQueue.length = 0; + this._deliveryQueue.clear(); + } + if (this._leakageMon) { + this._leakageMon.dispose(); } - this._leakageMon.dispose(); this._disposed = true; } } +export class PauseableEmitter extends Emitter { + + private _isPaused = 0; + private _eventQueue = new LinkedList(); + private _mergeFn?: (input: T[]) => T; + + constructor(options?: EmitterOptions & { merge?: (input: T[]) => T }) { + super(options); + this._mergeFn = options && options.merge; + } + + pause(): void { + this._isPaused++; + } + + resume(): void { + if (this._isPaused !== 0 && --this._isPaused === 0) { + if (this._mergeFn) { + // use the merge function to create a single composite + // event. make a copy in case firing pauses this emitter + const events = this._eventQueue.toArray(); + this._eventQueue.clear(); + super.fire(this._mergeFn(events)); + + } else { + // no merging, fire each event individually and test + // that this emitter isn't paused halfway through + while (!this._isPaused && this._eventQueue.size !== 0) { + super.fire(this._eventQueue.shift()!); + } + } + } + } + + fire(event: T): void { + if (this._listeners) { + if (this._isPaused !== 0) { + this._eventQueue.push(event); + } else { + super.fire(event); + } + } + } +} + export interface IWaitUntil { - waitUntil(thenable: Thenable): void; + waitUntil(thenable: Promise): void; } export class AsyncEmitter extends Emitter { - private _asyncDeliveryQueue: [Listener, T, Thenable[]][]; + private _asyncDeliveryQueue: [Listener, T, Promise[]][]; - async fireAsync(eventFn: (thenables: Thenable[], listener: Function) => T): Promise { + async fireAsync(eventFn: (thenables: Promise[], listener: Function) => T): Promise { if (!this._listeners) { return; } @@ -253,7 +645,7 @@ export class AsyncEmitter extends Emitter { } for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { - let thenables: Thenable[] = []; + const thenables: Promise[] = []; this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]); } @@ -341,99 +733,8 @@ export class EventMultiplexer implements IDisposable { } } -export function fromPromise(promise: Thenable): Event { - const emitter = new Emitter(); - let shouldEmit = false; - - promise - .then(undefined, () => null) - .then(() => { - if (!shouldEmit) { - setTimeout(() => emitter.fire(), 0); - } else { - emitter.fire(); - } - }); - - shouldEmit = true; - return emitter.event; -} - -export function toPromise(event: Event): Thenable { - return new Promise(c => once(event)(c)); -} - -export function once(event: Event): Event { - return (listener, thisArgs = null, disposables?) => { - // we need this, in case the event fires during the listener call - let didFire = false; - - const result = event(e => { - if (didFire) { - return; - } else if (result) { - result.dispose(); - } else { - didFire = true; - } - - return listener.call(thisArgs, e); - }, null, disposables); - - if (didFire) { - result.dispose(); - } - - return result; - }; -} - -export function anyEvent(...events: Event[]): Event { - return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); -} - -export function debounceEvent(event: Event, merger: (last: T, event: T) => T, delay?: number, leading?: boolean): Event; -export function debounceEvent(event: Event, merger: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean): Event; -export function debounceEvent(event: Event, merger: (last: O | undefined, event: I) => O, delay: number = 100, leading = false): Event { - - let subscription: IDisposable; - let output: O | undefined = undefined; - let handle: any = undefined; - let numDebouncedCalls = 0; - - const emitter = new Emitter({ - onFirstListenerAdd() { - subscription = event(cur => { - numDebouncedCalls++; - output = merger(output, cur); - - if (leading && !handle) { - emitter.fire(output); - } - - clearTimeout(handle); - handle = setTimeout(() => { - let _output = output; - output = undefined; - handle = undefined; - if (!leading || numDebouncedCalls > 1) { - emitter.fire(_output); - } - - numDebouncedCalls = 0; - }, delay); - }); - }, - onLastListenerRemove() { - subscription.dispose(); - } - }); - - return emitter.event; -} - /** - * The EventDelayer is useful in situations in which you want + * The EventBufferer is useful in situations in which you want * to delay firing your events during some code. * You can wrap that code and be sure that the event will not * be fired during that wrap. @@ -466,12 +767,12 @@ export class EventBufferer { } else { listener.call(thisArgs, i); } - }, void 0, disposables); + }, undefined, disposables); }; } bufferEvents(fn: () => R): R { - const buffer: Function[] = []; + const buffer: Array<() => R> = []; this.buffers.push(buffer); const r = fn(); this.buffers.pop(); @@ -480,169 +781,12 @@ export class EventBufferer { } } -export interface IChainableEvent { - event: Event; - map(fn: (i: T) => O): IChainableEvent; - forEach(fn: (i: T) => void): IChainableEvent; - filter(fn: (e: T) => boolean): IChainableEvent; - latch(): IChainableEvent; - on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; - once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; -} - -export function mapEvent(event: Event, map: (i: I) => O): Event { - return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables); -} - -export function forEach(event: Event, each: (i: I) => void): Event { - return (listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables); -} - -export function filterEvent(event: Event, filter: (e: T) => boolean): Event; -export function filterEvent(event: Event, filter: (e: T | R) => e is R): Event; -export function filterEvent(event: Event, filter: (e: T) => boolean): Event { - return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); -} - -export function signalEvent(event: Event): Event { - return event as Event as Event; -} - -class ChainableEvent implements IChainableEvent { - - get event(): Event { return this._event; } - - constructor(private _event: Event) { } - - map(fn: (i: T) => O): IChainableEvent { - return new ChainableEvent(mapEvent(this._event, fn)); - } - - forEach(fn: (i: T) => void): IChainableEvent { - return new ChainableEvent(forEach(this._event, fn)); - } - - filter(fn: (e: T) => boolean): IChainableEvent { - return new ChainableEvent(filterEvent(this._event, fn)); - } - - latch(): IChainableEvent { - return new ChainableEvent(latch(this._event)); - } - - on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { - return this._event(listener, thisArgs, disposables); - } - - once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { - return once(this._event)(listener, thisArgs, disposables); - } -} - -export function chain(event: Event): IChainableEvent { - return new ChainableEvent(event); -} - -export function stopwatch(event: Event): Event { - const start = new Date().getTime(); - return mapEvent(once(event), _ => new Date().getTime() - start); -} - /** - * Buffers the provided event until a first listener comes - * along, at which point fire all the events at once and - * pipe the event from then on. - * - * ```typescript - * const emitter = new Emitter(); - * const event = emitter.event; - * const bufferedEvent = buffer(event); - * - * emitter.fire(1); - * emitter.fire(2); - * emitter.fire(3); - * // nothing... - * - * const listener = bufferedEvent(num => console.log(num)); - * // 1, 2, 3 - * - * emitter.fire(4); - * // 4 - * ``` + * A Relay is an event forwarder which functions as a replugabble event pipe. + * Once created, you can connect an input event to it and it will simply forward + * events from that input event through its own `event` property. The `input` + * can be changed at any point in time. */ -export function buffer(event: Event, nextTick = false, _buffer: T[] = []): Event { - let buffer: T[] | null = _buffer.slice(); - - let listener: IDisposable | null = event(e => { - if (buffer) { - buffer.push(e); - } else { - emitter.fire(e); - } - }); - - const flush = () => { - if (buffer) { - buffer.forEach(e => emitter.fire(e)); - } - buffer = null; - }; - - const emitter = new Emitter({ - onFirstListenerAdd() { - if (!listener) { - listener = event(e => emitter.fire(e)); - } - }, - - onFirstListenerDidAdd() { - if (buffer) { - if (nextTick) { - setTimeout(flush); - } else { - flush(); - } - } - }, - - onLastListenerRemove() { - if (listener) { - listener.dispose(); - } - listener = null; - } - }); - - return emitter.event; -} - -/** - * Similar to `buffer` but it buffers indefinitely and repeats - * the buffered events to every new listener. - */ -export function echo(event: Event, nextTick = false, buffer: T[] = []): Event { - buffer = buffer.slice(); - - event(e => { - buffer.push(e); - emitter.fire(e); - }); - - const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e)); - - const emitter = new Emitter({ - onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) { - if (nextTick) { - setTimeout(() => flush(listener, thisArgs)); - } else { - flush(listener, thisArgs); - } - } - }); - - return emitter.event; -} - export class Relay implements IDisposable { private listening = false; @@ -676,29 +820,3 @@ export class Relay implements IDisposable { this.emitter.dispose(); } } - -export interface NodeEventEmitter { - on(event: string | symbol, listener: Function): this; - removeListener(event: string | symbol, listener: Function): this; -} - -export function fromNodeEventEmitter(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event { - const fn = (...args: any[]) => result.fire(map(...args)); - const onFirstListenerAdd = () => emitter.on(eventName, fn); - const onLastListenerRemove = () => emitter.removeListener(eventName, fn); - const result = new Emitter({ onFirstListenerAdd, onLastListenerRemove }); - - return result.event; -} - -export function latch(event: Event): Event { - let firstCall = true; - let cache: T; - - return filterEvent(event, value => { - let shouldEmit = firstCall || value !== cache; - firstCall = false; - cache = value; - return shouldEmit; - }); -} diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts new file mode 100644 index 00000000000..8f47ac2cc5e --- /dev/null +++ b/src/vs/base/common/extpath.ts @@ -0,0 +1,285 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isWindows } from 'vs/base/common/platform'; +import { startsWithIgnoreCase, equalsIgnoreCase, endsWith, rtrim } from 'vs/base/common/strings'; +import { CharCode } from 'vs/base/common/charCode'; +import { sep, posix, isAbsolute, join, normalize } from 'vs/base/common/path'; + +function isPathSeparator(code: number) { + return code === CharCode.Slash || code === CharCode.Backslash; +} + +/** + * Takes a Windows OS path and changes backward slashes to forward slashes. + * This should only be done for OS paths from Windows (or user provided paths potentially from Windows). + * Using it on a Linux or MaxOS path might change it. + */ +export function toSlashes(osPath: string) { + return osPath.replace(/[\\/]/g, posix.sep); +} + +/** + * Computes the _root_ this path, like `getRoot('c:\files') === c:\`, + * `getRoot('files:///files/path') === files:///`, + * or `getRoot('\\server\shares\path') === \\server\shares\` + */ +export function getRoot(path: string, sep: string = posix.sep): string { + + if (!path) { + return ''; + } + + const len = path.length; + const firstLetter = path.charCodeAt(0); + if (isPathSeparator(firstLetter)) { + if (isPathSeparator(path.charCodeAt(1))) { + // UNC candidate \\localhost\shares\ddd + // ^^^^^^^^^^^^^^^^^^^ + if (!isPathSeparator(path.charCodeAt(2))) { + let pos = 3; + const start = pos; + for (; pos < len; pos++) { + if (isPathSeparator(path.charCodeAt(pos))) { + break; + } + } + if (start !== pos && !isPathSeparator(path.charCodeAt(pos + 1))) { + pos += 1; + for (; pos < len; pos++) { + if (isPathSeparator(path.charCodeAt(pos))) { + return path.slice(0, pos + 1) // consume this separator + .replace(/[\\/]/g, sep); + } + } + } + } + } + + // /user/far + // ^ + return sep; + + } else if (isWindowsDriveLetter(firstLetter)) { + // check for windows drive letter c:\ or c: + + if (path.charCodeAt(1) === CharCode.Colon) { + if (isPathSeparator(path.charCodeAt(2))) { + // C:\fff + // ^^^ + return path.slice(0, 2) + sep; + } else { + // C: + // ^^ + return path.slice(0, 2); + } + } + } + + // check for URI + // scheme://authority/path + // ^^^^^^^^^^^^^^^^^^^ + let pos = path.indexOf('://'); + if (pos !== -1) { + pos += 3; // 3 -> "://".length + for (; pos < len; pos++) { + if (isPathSeparator(path.charCodeAt(pos))) { + return path.slice(0, pos + 1); // consume this separator + } + } + } + + return ''; +} + +/** + * Check if the path follows this pattern: `\\hostname\sharename`. + * + * @see https://msdn.microsoft.com/en-us/library/gg465305.aspx + * @return A boolean indication if the path is a UNC path, on none-windows + * always false. + */ +export function isUNC(path: string): boolean { + if (!isWindows) { + // UNC is a windows concept + return false; + } + + if (!path || path.length < 5) { + // at least \\a\b + return false; + } + + let code = path.charCodeAt(0); + if (code !== CharCode.Backslash) { + return false; + } + code = path.charCodeAt(1); + if (code !== CharCode.Backslash) { + return false; + } + let pos = 2; + const start = pos; + for (; pos < path.length; pos++) { + code = path.charCodeAt(pos); + if (code === CharCode.Backslash) { + break; + } + } + if (start === pos) { + return false; + } + code = path.charCodeAt(pos + 1); + if (isNaN(code) || code === CharCode.Backslash) { + return false; + } + return true; +} + +// Reference: https://en.wikipedia.org/wiki/Filename +const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g; +const UNIX_INVALID_FILE_CHARS = /[\\/]/g; +const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; +export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean { + const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS; + + if (!name || name.length === 0 || /^\s+$/.test(name)) { + return false; // require a name that is not just whitespace + } + + invalidFileChars.lastIndex = 0; // the holy grail of software development + if (invalidFileChars.test(name)) { + return false; // check for certain invalid file characters + } + + if (isWindowsOS && WINDOWS_FORBIDDEN_NAMES.test(name)) { + return false; // check for certain invalid file names + } + + if (name === '.' || name === '..') { + return false; // check for reserved values + } + + if (isWindowsOS && name[name.length - 1] === '.') { + return false; // Windows: file cannot end with a "." + } + + if (isWindowsOS && name.length !== name.trim().length) { + return false; // Windows: file cannot end with a whitespace + } + + if (name.length > 255) { + return false; // most file systems do not allow files > 255 length + } + + return true; +} + +export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boolean { + const identityEquals = (pathA === pathB); + if (!ignoreCase || identityEquals) { + return identityEquals; + } + + if (!pathA || !pathB) { + return false; + } + + return equalsIgnoreCase(pathA, pathB); +} + +export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean, separator = sep): boolean { + if (path === candidate) { + return true; + } + + if (!path || !candidate) { + return false; + } + + if (candidate.length > path.length) { + return false; + } + + if (ignoreCase) { + const beginsWith = startsWithIgnoreCase(path, candidate); + if (!beginsWith) { + return false; + } + + if (candidate.length === path.length) { + return true; // same path, different casing + } + + let sepOffset = candidate.length; + 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) === separator; + } + + if (candidate.charAt(candidate.length - 1) !== separator) { + candidate += separator; + } + + return path.indexOf(candidate) === 0; +} + +export function isWindowsDriveLetter(char0: number): boolean { + return char0 >= CharCode.A && char0 <= CharCode.Z || char0 >= CharCode.a && char0 <= CharCode.z; +} + +export function sanitizeFilePath(candidate: string, cwd: string): string { + + // Special case: allow to open a drive letter without trailing backslash + if (isWindows && endsWith(candidate, ':')) { + candidate += sep; + } + + // Ensure absolute + if (!isAbsolute(candidate)) { + candidate = join(cwd, candidate); + } + + // Ensure normalized + candidate = normalize(candidate); + + // Ensure no trailing slash/backslash + if (isWindows) { + candidate = rtrim(candidate, sep); + + // Special case: allow to open drive root ('C:\') + if (endsWith(candidate, ':')) { + candidate += sep; + } + + } else { + candidate = rtrim(candidate, sep); + + // Special case: allow to open root ('/') + if (!candidate) { + candidate = sep; + } + } + + return candidate; +} + +export function isRootOrDriveLetter(path: string): boolean { + const pathNormalized = normalize(path); + + if (isWindows) { + if (path.length > 3) { + return false; + } + + return isWindowsDriveLetter(pathNormalized.charCodeAt(0)) + && pathNormalized.charCodeAt(1) === CharCode.Colon + && (path.length === 2 || pathNormalized.charCodeAt(2) === CharCode.Backslash); + } + + return pathNormalized === posix.sep; +} \ No newline at end of file diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 3186312fd7a..76bb974d567 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -28,7 +28,7 @@ export interface IMatch { export function or(...filter: IFilter[]): IFilter { return function (word: string, wordToMatchAgainst: string): IMatch[] | null { for (let i = 0, len = filter.length; i < len; i++) { - let match = filter[i](word, wordToMatchAgainst); + const match = filter[i](word, wordToMatchAgainst); if (match) { return match; } @@ -64,7 +64,7 @@ function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: s // Contiguous Substring export function matchesContiguousSubString(word: string, wordToMatchAgainst: string): IMatch[] | null { - let index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase()); + const index = wordToMatchAgainst.toLowerCase().indexOf(word.toLowerCase()); if (index === -1) { return null; } @@ -119,6 +119,15 @@ function isWhitespace(code: number): boolean { ); } +const wordSeparators = new Set(); +'`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?' + .split('') + .forEach(s => wordSeparators.add(s.charCodeAt(0))); + +function isWordSeparator(code: number): boolean { + return wordSeparators.has(code); +} + function isAlphanumeric(code: number): boolean { return isLower(code) || isUpper(code) || isNumber(code); } @@ -136,7 +145,7 @@ function join(head: IMatch, tail: IMatch[]): IMatch[] { function nextAnchor(camelCaseWord: string, start: number): number { for (let i = start; i < camelCaseWord.length; i++) { - let c = camelCaseWord.charCodeAt(i); + const c = camelCaseWord.charCodeAt(i); if (isUpper(c) || isNumber(c) || (i > 0 && !isAlphanumeric(camelCaseWord.charCodeAt(i - 1)))) { return i; } @@ -184,10 +193,10 @@ function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis { if (isNumber(code)) { numeric++; } } - let upperPercent = upper / word.length; - let lowerPercent = lower / word.length; - let alphaPercent = alpha / word.length; - let numericPercent = numeric / word.length; + const upperPercent = upper / word.length; + const lowerPercent = lower / word.length; + const alphaPercent = alpha / word.length; + const numericPercent = numeric / word.length; return { upperPercent, lowerPercent, alphaPercent, numericPercent }; } @@ -307,8 +316,9 @@ function _matchesWords(word: string, target: string, i: number, j: number, conti function nextWord(word: string, start: number): number { for (let i = start; i < word.length; i++) { - let c = word.charCodeAt(i); - if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1)))) { + const c = word.charCodeAt(i); + if (isWhitespace(c) || (i > 0 && isWhitespace(word.charCodeAt(i - 1))) || + isWordSeparator(c) || (i > 0 && isWordSeparator(word.charCodeAt(i - 1)))) { return i; } } @@ -317,7 +327,7 @@ function nextWord(word: string, start: number): number { // Fuzzy -export const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContiguousSubString); +const fuzzyContiguousFilter = or(matchesPrefix, matchesCamelCase, matchesContiguousSubString); const fuzzySeparateFilter = or(matchesPrefix, matchesCamelCase, matchesSubString); const fuzzyRegExpCache = new LRUCache(10000); // bounded to 10000 elements @@ -334,7 +344,7 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep } // RegExp Filter - let match = regexp.exec(wordToMatchAgainst); + const match = regexp.exec(wordToMatchAgainst); if (match) { return [{ start: match.index, end: match.index + match[0].length }]; } @@ -343,55 +353,68 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst); } -export function anyScore(pattern: string, word: string, patternMaxWhitespaceIgnore?: number): FuzzyScore { - pattern = pattern.toLowerCase(); - word = word.toLowerCase(); +/** + * Match pattern againt word in a fuzzy way. As in IntelliSense and faster and more + * powerfull than `matchesFuzzy` + */ +export function matchesFuzzy2(pattern: string, word: string): IMatch[] | null { + const score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, true); + return score ? createMatches(score) : null; +} - const matches: number[] = []; - let idx = 0; - for (let pos = 0; pos < pattern.length; ++pos) { - const thisIdx = word.indexOf(pattern.charAt(pos), idx); - if (thisIdx >= 0) { - matches.push(thisIdx); - idx = thisIdx + 1; +export function anyScore(pattern: string, lowPattern: string, _patternPos: number, word: string, lowWord: string, _wordPos: number): FuzzyScore { + const result = fuzzyScore(pattern, lowPattern, 0, word, lowWord, 0, true); + if (result) { + return result; + } + let matches = 0; + let score = 0; + let idx = _wordPos; + for (let patternPos = 0; patternPos < lowPattern.length && patternPos < _maxLen; ++patternPos) { + const wordPos = lowWord.indexOf(lowPattern.charAt(patternPos), idx); + if (wordPos >= 0) { + score += 1; + matches += 2 ** wordPos; + idx = wordPos + 1; } } - return [matches.length, matches]; + return [score, matches, _wordPos]; } //#region --- fuzzyScore --- -export function createMatches(offsetOrScore: number[] | FuzzyScore): IMatch[] { - let ret: IMatch[] = []; - if (!offsetOrScore) { - return ret; +export function createMatches(score: undefined | FuzzyScore): IMatch[] { + if (typeof score === 'undefined') { + return []; } - let offsets: number[]; - if (Array.isArray(offsetOrScore[1])) { - offsets = (offsetOrScore as FuzzyScore)[1]; - } else { - offsets = offsetOrScore as number[]; - } - let last: IMatch | undefined; - for (const pos of offsets) { - if (last && last.end === pos) { - last.end += 1; - } else { - last = { start: pos, end: pos + 1 }; - ret.push(last); + + const matches = score[1].toString(2); + const wordStart = score[2]; + const res: IMatch[] = []; + + for (let pos = wordStart; pos < _maxLen; pos++) { + if (matches[matches.length - (pos + 1)] === '1') { + const last = res[res.length - 1]; + if (last && last.end === pos) { + last.end = pos + 1; + } else { + res.push({ start: pos, end: pos + 1 }); + } } } - return ret; + return res; } +const _maxLen = 53; + function initTable() { const table: number[][] = []; const row: number[] = [0]; - for (let i = 1; i <= 100; i++) { + for (let i = 1; i <= _maxLen; i++) { row.push(-i); } - for (let i = 0; i <= 100; i++) { - let thisRow = row.slice(0); + for (let i = 0; i <= _maxLen; i++) { + const thisRow = row.slice(0); thisRow[0] = -i; table.push(thisRow); } @@ -438,6 +461,7 @@ function isSeparatorAtPos(value: string, index: number): boolean { case CharCode.SingleQuote: case CharCode.DoubleQuote: case CharCode.Colon: + case CharCode.DollarSign: return true; default: return false; @@ -458,18 +482,49 @@ function isWhitespaceAtPos(value: string, index: number): boolean { } } +function isUpperCaseAtPos(pos: number, word: string, wordLow: string): boolean { + return word[pos] !== wordLow[pos]; +} + +function isPatternInWord(patternLow: string, patternPos: number, patternLen: number, wordLow: string, wordPos: number, wordLen: number): boolean { + while (patternPos < patternLen && wordPos < wordLen) { + if (patternLow[patternPos] === wordLow[wordPos]) { + patternPos += 1; + } + wordPos += 1; + } + return patternPos === patternLen; // pattern must be exhausted +} + const enum Arrow { Top = 0b1, Diag = 0b10, Left = 0b100 } -export type FuzzyScore = [number, number[]]; +/** + * A tuple of three values. + * 0. the score + * 1. the matches encoded as bitmask (2^53) + * 2. the offset at which matching started + */ +export type FuzzyScore = [number, number, number]; + +export namespace FuzzyScore { + /** + * No matches and value `-100` + */ + export const Default: [-100, 0, 0] = [-100, 0, 0]; + + export function isDefault(score?: FuzzyScore): score is [-100, 0, 0] { + return !score || (score[0] === -100 && score[1] === 0 && score[2] === 0); + } +} export interface FuzzyScorer { (pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined; } -export function fuzzyScore(pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined { +export function fuzzyScore(pattern: string, patternLow: string, patternPos: number, word: string, wordLow: string, wordPos: number, firstMatchCanBeWeak: boolean): FuzzyScore | undefined { - const patternLen = pattern.length > 100 ? 100 : pattern.length; - const wordLen = word.length > 100 ? 100 : word.length; + const patternLen = pattern.length > _maxLen ? _maxLen : pattern.length; + const wordLen = word.length > _maxLen ? _maxLen : word.length; if (patternPos >= patternLen || wordPos >= wordLen || patternLen > wordLen) { return undefined; @@ -478,20 +533,12 @@ export function fuzzyScore(pattern: string, lowPattern: string, patternPos: numb // Run a simple check if the characters of pattern occur // (in order) at all in word. If that isn't the case we // stop because no match will be possible - const patternStartPos = patternPos; - const wordStartPos = wordPos; - while (patternPos < patternLen && wordPos < wordLen) { - if (lowPattern[patternPos] === lowWord[wordPos]) { - patternPos += 1; - } - wordPos += 1; - } - if (patternPos !== patternLen) { + if (!isPatternInWord(patternLow, patternPos, patternLen, wordLow, wordPos, wordLen)) { return undefined; } - patternPos = patternStartPos; - wordPos = wordStartPos; + const patternStartPos = patternPos; + const wordStartPos = wordPos; // There will be a mach, fill in tables for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) { @@ -499,24 +546,27 @@ export function fuzzyScore(pattern: string, lowPattern: string, patternPos: numb for (wordPos = 1; wordPos <= wordLen; wordPos++) { let score = -1; - let lowWordChar = lowWord[wordPos - 1]; - if (lowPattern[patternPos - 1] === lowWordChar) { + if (patternLow[patternPos - 1] === wordLow[wordPos - 1]) { + if (wordPos === (patternPos - patternStartPos)) { // common prefix: `foobar <-> foobaz` + // ^^^^^ if (pattern[patternPos - 1] === word[wordPos - 1]) { score = 7; } else { score = 5; } - } else if (lowWordChar !== word[wordPos - 1] && (wordPos === 1 || lowWord[wordPos - 2] === word[wordPos - 2])) { + } else if (isUpperCaseAtPos(wordPos - 1, word, wordLow) && (wordPos === 1 || !isUpperCaseAtPos(wordPos - 2, word, wordLow))) { // hitting upper-case: `foo <-> forOthers` + // ^^ ^ if (pattern[patternPos - 1] === word[wordPos - 1]) { score = 7; } else { score = 5; } - } else if (isSeparatorAtPos(lowWord, wordPos - 2) || isWhitespaceAtPos(lowWord, wordPos - 2)) { + } else if (isSeparatorAtPos(wordLow, wordPos - 2) || isWhitespaceAtPos(wordLow, wordPos - 2)) { // post separator: `foo <-> bar_foo` + // ^^^ score = 5; } else { @@ -526,9 +576,9 @@ export function fuzzyScore(pattern: string, lowPattern: string, patternPos: numb _scores[patternPos][wordPos] = score; - let diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score); - let top = _table[patternPos - 1][wordPos] + -1; - let left = _table[patternPos][wordPos - 1] + -1; + const diag = _table[patternPos - 1][wordPos - 1] + (score > 1 ? 1 : score); + const top = _table[patternPos - 1][wordPos] + -1; + const left = _table[patternPos][wordPos - 1] + -1; if (left >= top) { // left or diag @@ -564,29 +614,26 @@ export function fuzzyScore(pattern: string, lowPattern: string, patternPos: numb console.log(printTable(_scores, pattern, patternLen, word, wordLen)); } - // _bucket is an array of [PrefixArray] we use to keep - // track of scores and matches. After calling `_findAllMatches` - // the best match (if available) is the first item in the array _matchesCount = 0; _topScore = -100; _patternStartPos = patternStartPos; _firstMatchCanBeWeak = firstMatchCanBeWeak; - _findAllMatches(patternLen, wordLen, patternLen === wordLen ? 1 : 0, new LazyArray(), false); - + _findAllMatches2(patternLen, wordLen, patternLen === wordLen ? 1 : 0, 0, false); if (_matchesCount === 0) { return undefined; } - return [_topScore, _topMatch.toArray()]; + return [_topScore, _topMatch2, wordStartPos]; } + let _matchesCount: number = 0; -let _topMatch: LazyArray; +let _topMatch2: number = 0; let _topScore: number = 0; let _patternStartPos: number = 0; let _firstMatchCanBeWeak: boolean = false; -function _findAllMatches(patternPos: number, wordPos: number, total: number, matches: LazyArray, lastMatched: boolean): void { +function _findAllMatches2(patternPos: number, wordPos: number, total: number, matches: number, lastMatched: boolean): void { if (_matchesCount >= 10 || total < -25) { // stop when having already 10 results, or @@ -598,15 +645,15 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat while (patternPos > _patternStartPos && wordPos > 0) { - let score = _scores[patternPos][wordPos]; - let arrow = _arrows[patternPos][wordPos]; + const score = _scores[patternPos][wordPos]; + const arrow = _arrows[patternPos][wordPos]; if (arrow === Arrow.Left) { - // left + // left -> no match, skip a word character wordPos -= 1; if (lastMatched) { total -= 5; // new gap penalty - } else if (!matches.isEmpty()) { + } else if (matches !== 0) { total -= 1; // gap penalty after first match } lastMatched = false; @@ -616,11 +663,11 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat if (arrow & Arrow.Left) { // left - _findAllMatches( + _findAllMatches2( patternPos, wordPos - 1, - !matches.isEmpty() ? total - 1 : total, // gap penalty after first match - matches.slice(), + matches !== 0 ? total - 1 : total, // gap penalty after first match + matches, lastMatched ); } @@ -629,9 +676,11 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat total += score; patternPos -= 1; wordPos -= 1; - matches.unshift(wordPos); lastMatched = true; + // match -> set a 1 at the word pos + matches += 2 ** wordPos; + // count simple matches and boost a row of // simple matches when they yield in a // strong match. @@ -662,47 +711,7 @@ function _findAllMatches(patternPos: number, wordPos: number, total: number, mat _matchesCount += 1; if (total > _topScore) { _topScore = total; - _topMatch = matches; - } -} - -class LazyArray { - - private _parent: LazyArray; - private _parentLen: number; - private _data: number[]; - - isEmpty(): boolean { - return !this._data && (!this._parent || this._parent.isEmpty()); - } - - unshift(n: number) { - if (!this._data) { - this._data = [n]; - } else { - this._data.unshift(n); - } - } - - slice(): LazyArray { - const ret = new LazyArray(); - ret._parent = this; - ret._parentLen = this._data ? this._data.length : 0; return ret; - } - - toArray(): number[] { - if (!this._data) { - return this._parent.toArray(); - } - const bucket: number[][] = []; - let element = this; - while (element) { - if (element._parent && element._parent._data) { - bucket.push(element._parent._data.slice(element._parent._data.length - element._parentLen)); - } - element = element._parent; - } - return Array.prototype.concat.apply(this._data, bucket); + _topMatch2 = matches; } } @@ -734,11 +743,11 @@ function fuzzyScoreWithPermutations(pattern: string, lowPattern: string, pattern // permutations of the pattern to find a better match. The // permutations only swap neighbouring characters, e.g // `cnoso` becomes `conso`, `cnsoo`, `cnoos`. - let tries = Math.min(7, pattern.length - 1); + const tries = Math.min(7, pattern.length - 1); for (let movingPatternPos = patternPos + 1; movingPatternPos < tries; movingPatternPos++) { - let newPattern = nextTypoPermutation(pattern, movingPatternPos); + const newPattern = nextTypoPermutation(pattern, movingPatternPos); if (newPattern) { - let candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, firstMatchCanBeWeak); + const candidate = fuzzyScore(newPattern, newPattern.toLowerCase(), patternPos, word, lowWord, wordPos, firstMatchCanBeWeak); if (candidate) { candidate[0] -= 3; // permutation penalty if (!top || candidate[0] > top[0]) { @@ -758,8 +767,8 @@ function nextTypoPermutation(pattern: string, patternPos: number): string | unde return undefined; } - let swap1 = pattern[patternPos]; - let swap2 = pattern[patternPos + 1]; + const swap1 = pattern[patternPos]; + const swap2 = pattern[patternPos + 1]; if (swap1 === swap2) { return undefined; diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index 29828e1e7a6..8cc3b53ba33 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -5,19 +5,19 @@ import * as arrays from 'vs/base/common/arrays'; import * as strings from 'vs/base/common/strings'; -import * as paths from 'vs/base/common/paths'; +import * as extpath from 'vs/base/common/extpath'; +import * as paths from 'vs/base/common/path'; import { LRUCache } from 'vs/base/common/map'; import { CharCode } from 'vs/base/common/charCode'; import { isThenable } from 'vs/base/common/async'; export interface IExpression { - [pattern: string]: boolean | SiblingClause | any; + [pattern: string]: boolean | SiblingClause; } export interface IRelativePattern { base: string; pattern: string; - pathToRelative(from: string, to: string): string; } export function getEmptyExpression(): IExpression { @@ -53,16 +53,13 @@ export function splitGlobAware(pattern: string, splitChar: string): string[] { return []; } - let segments: string[] = []; + const segments: string[] = []; let inBraces = false; let inBrackets = false; - let char: string; let curVal = ''; - for (let i = 0; i < pattern.length; i++) { - char = pattern[i]; - + for (const char of pattern) { switch (char) { case splitChar: if (!inBraces && !inBrackets) { @@ -105,7 +102,7 @@ function parseRegExp(pattern: string): string { let regEx = ''; // Split up into segments for each slash found - let segments = splitGlobAware(pattern, GLOB_SPLIT); + const segments = splitGlobAware(pattern, GLOB_SPLIT); // Special case where we only have globstars if (segments.every(s => s === GLOBSTAR)) { @@ -136,10 +133,7 @@ function parseRegExp(pattern: string): string { let inBrackets = false; let bracketVal = ''; - let char: string; - for (let i = 0; i < segment.length; i++) { - char = segment[i]; - + for (const char of segment) { // Support brace expansion if (char !== '}' && inBraces) { braceVal += char; @@ -185,10 +179,10 @@ function parseRegExp(pattern: string): string { continue; case '}': - let choices = splitGlobAware(braceVal, ','); + const choices = splitGlobAware(braceVal, ','); // Converts {foo,bar} => [foo|bar] - let braceRegExp = `(?:${choices.map(c => parseRegExp(c)).join('|')})`; + const braceRegExp = `(?:${choices.map(c => parseRegExp(c)).join('|')})`; regEx += braceRegExp; @@ -308,7 +302,7 @@ function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): P if (T1.test(pattern)) { // common pattern: **/*.txt just need endsWith check const base = pattern.substr(4); // '**/*'.length === 4 parsedPattern = function (path, basename) { - return path && strings.endsWith(path, base) ? pattern : null; + return typeof path === 'string' && strings.endsWith(path, base) ? pattern : null; }; } else if (match = T2.exec(trimForExclusions(pattern, options))) { // common pattern: **/some.txt just need basename check parsedPattern = trivia2(match[1], pattern); @@ -337,11 +331,11 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string | } return function (path, basename) { - if (!paths.isEqualOrParent(path, arg2.base)) { + if (!extpath.isEqualOrParent(path, arg2.base)) { return null; } - return parsedPattern(arg2.pathToRelative(arg2.base, path), basename); + return parsedPattern(paths.relative(arg2.base, path), basename); }; } @@ -354,7 +348,7 @@ function trivia2(base: string, originalPattern: string): ParsedStringPattern { const slashBase = `/${base}`; const backslashBase = `\\${base}`; const parsedPattern: ParsedStringPattern = function (path, basename) { - if (!path) { + if (typeof path !== 'string') { return null; } if (basename) { @@ -402,12 +396,12 @@ function trivia3(pattern: string, options: IGlobOptions): ParsedStringPattern { // common patterns: **/something/else just need endsWith check, something/else just needs and equals check function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): ParsedStringPattern { - const nativePath = paths.nativeSep !== paths.sep ? path.replace(ALL_FORWARD_SLASHES, paths.nativeSep) : path; - const nativePathEnd = paths.nativeSep + nativePath; + const nativePath = paths.sep !== paths.posix.sep ? path.replace(ALL_FORWARD_SLASHES, paths.sep) : path; + const nativePathEnd = paths.sep + nativePath; const parsedPattern: ParsedStringPattern = matchPathEnds ? function (path, basename) { - return path && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null; + return typeof path === 'string' && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null; } : function (path, basename) { - return path && path === nativePath ? pattern : null; + return typeof path === 'string' && path === nativePath ? pattern : null; }; parsedPattern.allPaths = [(matchPathEnds ? '*/' : './') + path]; return parsedPattern; @@ -418,7 +412,7 @@ function toRegExp(pattern: string): ParsedStringPattern { const regExp = new RegExp(`^${parseRegExp(pattern)}$`); return function (path: string, basename: string) { regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it! - return path && regExp.test(path) ? pattern : null; + return typeof path === 'string' && regExp.test(path) ? pattern : null; }; } catch (error) { return NULL; @@ -435,8 +429,8 @@ function toRegExp(pattern: string): ParsedStringPattern { */ export function match(pattern: string | IRelativePattern, path: string): boolean; export function match(expression: IExpression, path: string, hasSibling?: (name: string) => boolean): string /* the matching pattern */; -export function match(arg1: string | IExpression | IRelativePattern, path: string, hasSibling?: (name: string) => boolean): any { - if (!arg1 || !path) { +export function match(arg1: string | IExpression | IRelativePattern, path: string, hasSibling?: (name: string) => boolean): boolean | string | null | Promise { + if (!arg1 || typeof path !== 'string') { return false; } @@ -453,14 +447,14 @@ export function match(arg1: string | IExpression | IRelativePattern, path: strin */ export function parse(pattern: string | IRelativePattern, options?: IGlobOptions): ParsedPattern; export function parse(expression: IExpression, options?: IGlobOptions): ParsedExpression; -export function parse(arg1: string | IExpression | IRelativePattern, options: IGlobOptions = {}): any { +export function parse(arg1: string | IExpression | IRelativePattern, options: IGlobOptions = {}): ParsedPattern | ParsedExpression { if (!arg1) { return FALSE; } // Glob with String if (typeof arg1 === 'string' || isRelativePattern(arg1)) { - const parsedPattern = parsePattern(arg1 as string | IRelativePattern, options); + const parsedPattern = parsePattern(arg1, options); if (parsedPattern === NULL) { return FALSE; } @@ -518,21 +512,10 @@ function listToMap(list: string[]) { return map; } -export function isRelativePattern(obj: any): obj is IRelativePattern { +export function isRelativePattern(obj: unknown): obj is IRelativePattern { const rp = obj as IRelativePattern; - return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string' && typeof rp.pathToRelative === 'function'; -} - -/** - * Same as `parse`, but the ParsedExpression is guaranteed to return a Promise - */ -export function parseToAsync(expression: IExpression, options?: IGlobOptions): ParsedExpression { - const parsedExpression = parse(expression, options); - return (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise => { - const result = parsedExpression(path, basename, hasSibling); - return isThenable(result) ? result : Promise.resolve(result); - }; + return rp && typeof rp.base === 'string' && typeof rp.pattern === 'string'; } export function getBasenameTerms(patternOrExpression: ParsedPattern | ParsedExpression): string[] { @@ -619,7 +602,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return resultExpression; } -function parseExpressionPattern(pattern: string, value: any, options: IGlobOptions): (ParsedStringPattern | ParsedExpressionPattern) { +function parseExpressionPattern(pattern: string, value: boolean | SiblingClause, options: IGlobOptions): (ParsedStringPattern | ParsedExpressionPattern) { if (value === false) { return NULL; // pattern is disabled } @@ -658,7 +641,7 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio return parsedPattern; } -function aggregateBasenameMatches(parsedPatterns: (ParsedStringPattern | ParsedExpressionPattern)[], result?: string): (ParsedStringPattern | ParsedExpressionPattern)[] { +function aggregateBasenameMatches(parsedPatterns: Array, result?: string): Array { const basenamePatterns = parsedPatterns.filter(parsedPattern => !!(parsedPattern).basenames); if (basenamePatterns.length < 2) { return parsedPatterns; @@ -681,7 +664,7 @@ function aggregateBasenameMatches(parsedPatterns: (ParsedStringPattern | ParsedE }, []); } const aggregate: ParsedStringPattern = function (path, basename) { - if (!path) { + if (typeof path !== 'string') { return null; } if (!basename) { diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 6c0025b1d74..0a5106d3bd6 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -69,4 +69,4 @@ export class Hasher { this._value = hash(obj, this._value); return this._value; } -} \ No newline at end of file +} diff --git a/src/vs/base/common/history.ts b/src/vs/base/common/history.ts index 95551eb1a7a..19dada92a3e 100644 --- a/src/vs/base/common/history.ts +++ b/src/vs/base/common/history.ts @@ -66,7 +66,7 @@ export class HistoryNavigator implements INavigator { } private _reduceToLimit() { - let data = this._elements; + const data = this._elements; if (data.length > this._limit) { this._initialize(data.slice(data.length - this._limit)); } diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index b7e99fbf6c7..201e378e11e 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -16,7 +16,6 @@ export class MarkdownString implements IMarkdownString { value: string; isTrusted?: boolean; - sanitize: boolean = true; constructor(value: string = '') { this.value = value; @@ -43,7 +42,7 @@ export class MarkdownString implements IMarkdownString { } } -export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownString[]): boolean { +export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownString[] | null | undefined): boolean { if (isMarkdownString(oneOrMany)) { return !oneOrMany.value; } else if (Array.isArray(oneOrMany)) { @@ -58,7 +57,7 @@ export function isMarkdownString(thing: any): thing is IMarkdownString { return true; } else if (thing && typeof thing === 'object') { return typeof (thing).value === 'string' - && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === void 0); + && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === undefined); } return false; } diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 9158a73fe1e..b3a62ede9ff 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -205,11 +205,11 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON token: SyntaxKind = SyntaxKind.Unknown, scanError: ScanError = ScanError.None; - function scanHexDigits(count: number, exact?: boolean): number { + function scanHexDigits(count: number): number { let digits = 0; let value = 0; - while (digits < count || !exact) { - let ch = text.charCodeAt(pos); + while (digits < count) { + const ch = text.charCodeAt(pos); if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) { value = value * 16 + ch - CharacterCodes._0; } @@ -240,7 +240,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON } function scanNumber(): string { - let start = pos; + const start = pos; if (text.charCodeAt(pos) === CharacterCodes._0) { pos++; } else { @@ -331,7 +331,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON result += '\t'; break; case CharacterCodes.u: - let ch = scanHexDigits(4, true); + const ch = scanHexDigits(4); if (ch >= 0) { result += String.fromCharCode(ch); } else { @@ -344,7 +344,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON start = pos; continue; } - if (ch >= 0 && ch <= 0x1f) { + if (ch >= 0 && ch <= 0x1F) { if (isLineBreak(ch)) { result += text.substring(start, pos); scanError = ScanError.UnexpectedEndOfString; @@ -424,7 +424,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON // comments case CharacterCodes.slash: - let start = pos - 1; + const start = pos - 1; // Single-line comment if (text.charCodeAt(pos + 1) === CharacterCodes.slash) { pos += 2; @@ -444,10 +444,10 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { pos += 2; - let safeLength = len - 1; // For lookahead. + const safeLength = len - 1; // For lookahead. let commentClosed = false; while (pos < safeLength) { - let ch = text.charCodeAt(pos); + const ch = text.charCodeAt(pos); if (ch === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { pos += 2; @@ -668,7 +668,7 @@ const enum CharacterCodes { W = 0x57, X = 0x58, Y = 0x59, - Z = 0x5a, + Z = 0x5A, ampersand = 0x26, // & asterisk = 0x2A, // * @@ -720,15 +720,15 @@ interface NodeImpl extends Node { * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index. */ export function getLocation(text: string, position: number): Location { - let segments: Segment[] = []; // strings or numbers - let earlyReturnException = new Object(); - let previousNode: NodeImpl | undefined = void 0; + const segments: Segment[] = []; // strings or numbers + const earlyReturnException = new Object(); + let previousNode: NodeImpl | undefined = undefined; const previousNodeInst: NodeImpl = { value: {}, offset: 0, length: 0, type: 'object', - parent: void 0 + parent: undefined }; let isAtPropertyKey = false; function setPreviousNode(value: string, offset: number, length: number, type: NodeType) { @@ -736,7 +736,7 @@ export function getLocation(text: string, position: number): Location { previousNodeInst.offset = offset; previousNodeInst.length = length; previousNodeInst.type = type; - previousNodeInst.colonOffset = void 0; + previousNodeInst.colonOffset = undefined; previousNode = previousNodeInst; } try { @@ -746,7 +746,7 @@ export function getLocation(text: string, position: number): Location { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; isAtPropertyKey = position > offset; segments.push(''); // push a placeholder (will be replaced) }, @@ -764,21 +764,21 @@ export function getLocation(text: string, position: number): Location { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; segments.pop(); }, onArrayBegin: (offset: number, length: number) => { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; segments.push(0); }, onArrayEnd: (offset: number, length: number) => { if (position <= offset) { throw earlyReturnException; } - previousNode = void 0; + previousNode = undefined; segments.pop(); }, onLiteralValue: (value: any, offset: number, length: number) => { @@ -798,16 +798,16 @@ export function getLocation(text: string, position: number): Location { if (sep === ':' && previousNode && previousNode.type === 'property') { previousNode.colonOffset = offset; isAtPropertyKey = false; - previousNode = void 0; + previousNode = undefined; } else if (sep === ',') { - let last = segments[segments.length - 1]; + const last = segments[segments.length - 1]; if (typeof last === 'number') { segments[segments.length - 1] = last + 1; } else { isAtPropertyKey = true; segments[segments.length - 1] = ''; } - previousNode = void 0; + previousNode = undefined; } } }); @@ -843,7 +843,7 @@ export function getLocation(text: string, position: number): Location { export function parse(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): any { let currentProperty: string | null = null; let currentParent: any = []; - let previousParents: any[] = []; + const previousParents: any[] = []; function onValue(value: any) { if (Array.isArray(currentParent)) { @@ -853,9 +853,9 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt } } - let visitor: JSONVisitor = { + const visitor: JSONVisitor = { onObjectBegin: () => { - let object = {}; + const object = {}; onValue(object); previousParents.push(currentParent); currentParent = object; @@ -868,7 +868,7 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt currentParent = previousParents.pop(); }, onArrayBegin: () => { - let array: any[] = []; + const array: any[] = []; onValue(array); previousParents.push(currentParent); currentParent = array; @@ -891,7 +891,7 @@ export function parse(text: string, errors: ParseError[] = [], options: ParseOpt * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result. */ export function parseTree(text: string, errors: ParseError[] = [], options: ParseOptions = ParseOptions.DEFAULT): Node { - let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: void 0 }; // artificial root + let currentParent: NodeImpl = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root function ensurePropertyComplete(endOffset: number) { if (currentParent.type === 'property') { @@ -905,7 +905,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars return valueNode; } - let visitor: JSONVisitor = { + const visitor: JSONVisitor = { onObjectBegin: (offset: number) => { currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] }); }, @@ -945,7 +945,7 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars }; visit(text, visitor, options); - let result = currentParent.children![0]; + const result = currentParent.children![0]; if (result) { delete result.parent; } @@ -957,13 +957,13 @@ export function parseTree(text: string, errors: ParseError[] = [], options: Pars */ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined { if (!root) { - return void 0; + return undefined; } let node = root; for (let segment of path) { if (typeof segment === 'string') { if (node.type !== 'object' || !Array.isArray(node.children)) { - return void 0; + return undefined; } let found = false; for (const propertyNode of node.children) { @@ -974,12 +974,12 @@ export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined } } if (!found) { - return void 0; + return undefined; } } else { - let index = segment; + const index = segment; if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) { - return void 0; + return undefined; } node = node.children[index]; } @@ -994,12 +994,12 @@ export function getNodePath(node: Node): JSONPath { if (!node.parent || !node.parent.children) { return []; } - let path = getNodePath(node.parent); + const path = getNodePath(node.parent); if (node.parent.type === 'property') { - let key = node.parent.children[0].value; + const key = node.parent.children[0].value; path.push(key); } else if (node.parent.type === 'array') { - let index = node.parent.children.indexOf(node); + const index = node.parent.children.indexOf(node); if (index !== -1) { path.push(index); } @@ -1015,9 +1015,9 @@ export function getNodeValue(node: Node): any { case 'array': return node.children!.map(getNodeValue); case 'object': - let obj = Object.create(null); + const obj = Object.create(null); for (let prop of node.children!) { - let valueNode = prop.children![1]; + const valueNode = prop.children![1]; if (valueNode) { obj[prop.children![0].value] = getNodeValue(valueNode); } @@ -1029,7 +1029,7 @@ export function getNodeValue(node: Node): any { case 'boolean': return node.value; default: - return void 0; + return undefined; } } @@ -1043,10 +1043,10 @@ export function contains(node: Node, offset: number, includeRightBound = false): */ export function findNodeAtOffset(node: Node, offset: number, includeRightBound = false): Node | undefined { if (contains(node, offset, includeRightBound)) { - let children = node.children; + const children = node.children; if (Array.isArray(children)) { for (let i = 0; i < children.length && children[i].offset <= offset; i++) { - let item = findNodeAtOffset(children[i], offset, includeRightBound); + const item = findNodeAtOffset(children[i], offset, includeRightBound); if (item) { return item; } @@ -1055,7 +1055,7 @@ export function findNodeAtOffset(node: Node, offset: number, includeRightBound = } return node; } - return void 0; + return undefined; } @@ -1064,7 +1064,7 @@ export function findNodeAtOffset(node: Node, offset: number, includeRightBound = */ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions = ParseOptions.DEFAULT): any { - let _scanner = createScanner(text, false); + const _scanner = createScanner(text, false); function toNoArgVisit(visitFunction?: (offset: number, length: number) => void): () => void { return visitFunction ? () => visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; @@ -1073,7 +1073,7 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions return visitFunction ? (arg: T) => visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength()) : () => true; } - let onObjectBegin = toNoArgVisit(visitor.onObjectBegin), + const onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), @@ -1083,11 +1083,11 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError); - let disallowComments = options && options.disallowComments; - let allowTrailingComma = options && options.allowTrailingComma; + const disallowComments = options && options.disallowComments; + const allowTrailingComma = options && options.allowTrailingComma; function scanNext(): SyntaxKind { while (true) { - let token = _scanner.scan(); + const token = _scanner.scan(); switch (_scanner.getTokenError()) { case ScanError.InvalidUnicode: handleError(ParseErrorCode.InvalidUnicode); @@ -1148,7 +1148,7 @@ export function visit(text: string, visitor: JSONVisitor, options: ParseOptions } function parseString(isValue: boolean): boolean { - let value = _scanner.getTokenValue(); + const value = _scanner.getTokenValue(); if (isValue) { onLiteralValue(value); } else { @@ -1322,7 +1322,7 @@ export function stripComments(text: string, replaceCh?: string): string { if (offset !== pos) { parts.push(text.substring(offset, pos)); } - if (replaceCh !== void 0) { + if (replaceCh !== undefined) { parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh)); } offset = _scanner.getPosition(); diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 485d9f48b4c..a1538870749 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -5,23 +5,24 @@ import { ParseError, Node, JSONPath, Segment, parseTree, findNodeAtLocation } from './json'; import { Edit, format, isEOL, FormattingOptions } from './jsonFormatter'; +import { mergeSort } from 'vs/base/common/arrays'; export function removeProperty(text: string, path: JSONPath, formattingOptions: FormattingOptions): Edit[] { - return setProperty(text, path, void 0, formattingOptions); + return setProperty(text, path, undefined, formattingOptions); } export function setProperty(text: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions, getInsertionIndex?: (properties: string[]) => number): Edit[] { - let path = originalPath.slice(); - let errors: ParseError[] = []; - let root = parseTree(text, errors); - let parent: Node | undefined = void 0; + const path = originalPath.slice(); + const errors: ParseError[] = []; + const root = parseTree(text, errors); + let parent: Node | undefined = undefined; - let lastSegment: Segment | undefined = void 0; + let lastSegment: Segment | undefined = undefined; while (path.length > 0) { lastSegment = path.pop(); parent = findNodeAtLocation(root, path); - if (parent === void 0 && value !== void 0) { + if (parent === undefined && value !== undefined) { if (typeof lastSegment === 'string') { value = { [lastSegment]: value }; } else { @@ -34,29 +35,29 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo if (!parent) { // empty document - if (value === void 0) { // delete + if (value === undefined) { // delete throw new Error('Can not delete in empty document'); } return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, formattingOptions); } else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) { - let existing = findNodeAtLocation(parent, [lastSegment]); - if (existing !== void 0) { - if (value === void 0) { // delete + const existing = findNodeAtLocation(parent, [lastSegment]); + if (existing !== undefined) { + if (value === undefined) { // delete if (!existing.parent) { throw new Error('Malformed AST'); } - let propertyIndex = parent.children.indexOf(existing.parent); + const propertyIndex = parent.children.indexOf(existing.parent); let removeBegin: number; let removeEnd = existing.parent.offset + existing.parent.length; if (propertyIndex > 0) { // remove the comma of the previous node - let previous = parent.children[propertyIndex - 1]; + const previous = parent.children[propertyIndex - 1]; removeBegin = previous.offset + previous.length; } else { removeBegin = parent.offset + 1; if (parent.children.length > 1) { // remove the comma of the next node - let next = parent.children[1]; + const next = parent.children[1]; removeEnd = next.offset; } } @@ -66,14 +67,14 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, formattingOptions); } } else { - if (value === void 0) { // delete + if (value === undefined) { // delete return []; // property does not exist, nothing to do } - let newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`; - let index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length; + const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`; + const index = getInsertionIndex ? getInsertionIndex(parent.children.map(p => p.children![0].value)) : parent.children.length; let edit: Edit; if (index > 0) { - let previous = parent.children[index - 1]; + const previous = parent.children[index - 1]; edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; } else if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; @@ -83,32 +84,32 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo return withFormatting(text, edit, formattingOptions); } } else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) { - let insertIndex = lastSegment; + const insertIndex = lastSegment; if (insertIndex === -1) { // Insert - let newProperty = `${JSON.stringify(value)}`; + const newProperty = `${JSON.stringify(value)}`; let edit: Edit; if (parent.children.length === 0) { edit = { offset: parent.offset + 1, length: 0, content: newProperty }; } else { - let previous = parent.children[parent.children.length - 1]; + const previous = parent.children[parent.children.length - 1]; edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; } return withFormatting(text, edit, formattingOptions); } else { - if (value === void 0 && parent.children.length >= 0) { + if (value === undefined && parent.children.length >= 0) { //Removal - let removalIndex = lastSegment; - let toRemove = parent.children[removalIndex]; + const removalIndex = lastSegment; + const toRemove = parent.children[removalIndex]; let edit: Edit; if (parent.children.length === 1) { // only item edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' }; } else if (parent.children.length - 1 === removalIndex) { // last item - let previous = parent.children[removalIndex - 1]; - let offset = previous.offset + previous.length; - let parentEndOffset = parent.offset + parent.length; + const previous = parent.children[removalIndex - 1]; + const offset = previous.offset + previous.length; + const parentEndOffset = parent.offset + parent.length; edit = { offset, length: parentEndOffset - 2 - offset, content: '' }; } else { edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' }; @@ -139,18 +140,18 @@ function withFormatting(text: string, edit: Edit, formattingOptions: FormattingO } } - let edits = format(newText, { offset: begin, length: end - begin }, formattingOptions); + const edits = format(newText, { offset: begin, length: end - begin }, formattingOptions); // apply the formatting edits and track the begin and end offsets of the changes for (let i = edits.length - 1; i >= 0; i--) { - let edit = edits[i]; + const edit = edits[i]; newText = applyEdit(newText, edit); begin = Math.min(begin, edit.offset); end = Math.max(end, edit.offset + edit.length); end += edit.content.length - edit.length; } // create a single edit with all changes - let editLength = text.length - (newText.length - end) - begin; + const editLength = text.length - (newText.length - end) - begin; return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }]; } @@ -158,6 +159,27 @@ export function applyEdit(text: string, edit: Edit): string { return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length); } +export function applyEdits(text: string, edits: Edit[]): string { + let sortedEdits = mergeSort(edits, (a, b) => { + const diff = a.offset - b.offset; + if (diff === 0) { + return a.length - b.length; + } + return diff; + }); + let lastModifiedOffset = text.length; + for (let i = sortedEdits.length - 1; i >= 0; i--) { + let e = sortedEdits[i]; + if (e.offset + e.length <= lastModifiedOffset) { + text = applyEdit(text, e); + } else { + throw new Error('Overlapping edit'); + } + lastModifiedOffset = e.offset; + } + return text; +} + export function isWS(text: string, offset: number) { return '\r\n \t'.indexOf(text.charAt(offset)) !== -1; } \ No newline at end of file diff --git a/src/vs/base/common/jsonFormatter.ts b/src/vs/base/common/jsonFormatter.ts index df2a97ee5cd..a25c493c05d 100644 --- a/src/vs/base/common/jsonFormatter.ts +++ b/src/vs/base/common/jsonFormatter.ts @@ -80,7 +80,7 @@ export function format(documentText: string, range: Range | undefined, options: rangeStart = 0; rangeEnd = documentText.length; } - let eol = getEOL(options, documentText); + const eol = getEOL(options, documentText); let lineBreak = false; let indentLevel = 0; @@ -91,7 +91,7 @@ export function format(documentText: string, range: Range | undefined, options: indentValue = '\t'; } - let scanner = createScanner(formatText, false); + const scanner = createScanner(formatText, false); let hasError = false; function newLineAndIndent(): string { @@ -107,7 +107,7 @@ export function format(documentText: string, range: Range | undefined, options: hasError = token === SyntaxKind.Unknown || scanner.getTokenError() !== ScanError.None; return token; } - let editOperations: Edit[] = []; + const editOperations: Edit[] = []; function addEdit(text: string, startOffset: number, endOffset: number) { if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) { editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text }); @@ -117,8 +117,8 @@ export function format(documentText: string, range: Range | undefined, options: let firstToken = scanNext(); if (firstToken !== SyntaxKind.EOF) { - let firstTokenStart = scanner.getTokenOffset() + formatTextStart; - let initialIndent = repeat(indentValue, initialIndentLevel); + const firstTokenStart = scanner.getTokenOffset() + formatTextStart; + const initialIndent = repeat(indentValue, initialIndentLevel); addEdit(initialIndent, formatTextStart, firstTokenStart); } @@ -129,7 +129,7 @@ export function format(documentText: string, range: Range | undefined, options: let replaceContent = ''; while (!lineBreak && (secondToken === SyntaxKind.LineCommentTrivia || secondToken === SyntaxKind.BlockCommentTrivia)) { // comments on the same line: keep them on the same line, but ignore them otherwise - let commentTokenStart = scanner.getTokenOffset() + formatTextStart; + const commentTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(' ', firstTokenEnd, commentTokenStart); firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart; replaceContent = secondToken === SyntaxKind.LineCommentTrivia ? newLineAndIndent() : ''; @@ -195,7 +195,7 @@ export function format(documentText: string, range: Range | undefined, options: } } - let secondTokenStart = scanner.getTokenOffset() + formatTextStart; + const secondTokenStart = scanner.getTokenOffset() + formatTextStart; addEdit(replaceContent, firstTokenEnd, secondTokenStart); firstToken = secondToken; } @@ -213,9 +213,9 @@ function repeat(s: string, count: number): string { function computeIndentLevel(content: string, options: FormattingOptions): number { let i = 0; let nChars = 0; - let tabSize = options.tabSize || 4; + const tabSize = options.tabSize || 4; while (i < content.length) { - let ch = content.charAt(i); + const ch = content.charAt(i); if (ch === ' ') { nChars++; } else if (ch === '\t') { @@ -230,7 +230,7 @@ function computeIndentLevel(content: string, options: FormattingOptions): number function getEOL(options: FormattingOptions, text: string): string { for (let i = 0; i < text.length; i++) { - let ch = text.charAt(i); + const ch = text.charAt(i); if (ch === '\r') { if (i + 1 < text.length && text.charAt(i + 1) === '\n') { return '\r\n'; diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts index c72cef22ae3..ec08bea7e91 100644 --- a/src/vs/base/common/keyCodes.ts +++ b/src/vs/base/common/keyCodes.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { OperatingSystem } from 'vs/base/common/platform'; +import { illegalArgument } from 'vs/base/common/errors'; /** * Virtual Key Codes, the value does not hold any inherent meaning. @@ -395,7 +396,7 @@ const enum BinaryKeybindingsMask { Shift = (1 << 10) >>> 0, Alt = (1 << 9) >>> 0, WinCtrl = (1 << 8) >>> 0, - KeyCode = 0x000000ff + KeyCode = 0x000000FF } export const enum KeyMod { @@ -406,7 +407,7 @@ export const enum KeyMod { } export function KeyChord(firstPart: number, secondPart: number): number { - let chordPart = ((secondPart & 0x0000ffff) << 16) >>> 0; + const chordPart = ((secondPart & 0x0000FFFF) << 16) >>> 0; return (firstPart | chordPart) >>> 0; } @@ -414,15 +415,15 @@ export function createKeybinding(keybinding: number, OS: OperatingSystem): Keybi if (keybinding === 0) { return null; } - const firstPart = (keybinding & 0x0000ffff) >>> 0; - const chordPart = (keybinding & 0xffff0000) >>> 16; + const firstPart = (keybinding & 0x0000FFFF) >>> 0; + const chordPart = (keybinding & 0xFFFF0000) >>> 16; if (chordPart !== 0) { - return new ChordKeybinding( + return new ChordKeybinding([ createSimpleKeybinding(firstPart, OS), - createSimpleKeybinding(chordPart, OS), - ); + createSimpleKeybinding(chordPart, OS) + ]); } - return createSimpleKeybinding(firstPart, OS); + return new ChordKeybinding([createSimpleKeybinding(firstPart, OS)]); } export function createSimpleKeybinding(keybinding: number, OS: OperatingSystem): SimpleKeybinding { @@ -439,14 +440,7 @@ export function createSimpleKeybinding(keybinding: number, OS: OperatingSystem): return new SimpleKeybinding(ctrlKey, shiftKey, altKey, metaKey, keyCode); } -export const enum KeybindingType { - Simple = 1, - Chord = 2 -} - export class SimpleKeybinding { - public readonly type = KeybindingType.Simple; - public readonly ctrlKey: boolean; public readonly shiftKey: boolean; public readonly altKey: boolean; @@ -461,10 +455,7 @@ export class SimpleKeybinding { this.keyCode = keyCode; } - public equals(other: Keybinding): boolean { - if (other.type !== KeybindingType.Simple) { - return false; - } + public equals(other: SimpleKeybinding): boolean { return ( this.ctrlKey === other.ctrlKey && this.shiftKey === other.shiftKey @@ -475,10 +466,10 @@ export class SimpleKeybinding { } public getHashCode(): string { - let ctrl = this.ctrlKey ? '1' : '0'; - let shift = this.shiftKey ? '1' : '0'; - let alt = this.altKey ? '1' : '0'; - let meta = this.metaKey ? '1' : '0'; + const ctrl = this.ctrlKey ? '1' : '0'; + const shift = this.shiftKey ? '1' : '0'; + const alt = this.altKey ? '1' : '0'; + const meta = this.metaKey ? '1' : '0'; return `${ctrl}${shift}${alt}${meta}${this.keyCode}`; } @@ -492,6 +483,10 @@ export class SimpleKeybinding { ); } + public toChord(): ChordKeybinding { + return new ChordKeybinding([this]); + } + /** * Does this keybinding refer to the key code of a modifier and it also has the modifier flag? */ @@ -506,22 +501,43 @@ export class SimpleKeybinding { } export class ChordKeybinding { - public readonly type = KeybindingType.Chord; + public readonly parts: SimpleKeybinding[]; - public readonly firstPart: SimpleKeybinding; - public readonly chordPart: SimpleKeybinding; - - constructor(firstPart: SimpleKeybinding, chordPart: SimpleKeybinding) { - this.firstPart = firstPart; - this.chordPart = chordPart; + constructor(parts: SimpleKeybinding[]) { + if (parts.length === 0) { + throw illegalArgument(`parts`); + } + this.parts = parts; } public getHashCode(): string { - return `${this.firstPart.getHashCode()};${this.chordPart.getHashCode()}`; + let result = ''; + for (let i = 0, len = this.parts.length; i < len; i++) { + if (i !== 0) { + result += ';'; + } + result += this.parts[i].getHashCode(); + } + return result; + } + + public equals(other: ChordKeybinding | null): boolean { + if (other === null) { + return false; + } + if (this.parts.length !== other.parts.length) { + return false; + } + for (let i = 0; i < this.parts.length; i++) { + if (!this.parts[i].equals(other.parts[i])) { + return false; + } + } + return true; } } -export type Keybinding = SimpleKeybinding | ChordKeybinding; +export type Keybinding = ChordKeybinding; export class ResolvedKeybindingPart { readonly ctrlKey: boolean; @@ -574,12 +590,13 @@ export abstract class ResolvedKeybinding { public abstract isChord(): boolean; /** - * Returns the firstPart, chordPart that should be used for dispatching. + * Returns the parts that comprise of the keybinding. + * Simple keybindings return one element. */ - public abstract getDispatchParts(): [string | null, string | null]; + public abstract getParts(): ResolvedKeybindingPart[]; + /** - * Returns the firstPart, chordPart of the keybinding. - * For simple keybindings, the second element will be null. + * Returns the parts that should be used for dispatching. */ - public abstract getParts(): [ResolvedKeybindingPart, ResolvedKeybindingPart | null]; + public abstract getDispatchParts(): (string | null)[]; } diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts index 30344e4d724..671816830bd 100644 --- a/src/vs/base/common/keybindingLabels.ts +++ b/src/vs/base/common/keybindingLabels.ts @@ -21,6 +21,10 @@ export interface Modifiers { readonly metaKey: boolean; } +export interface KeyLabelProvider { + (keybinding: T): string | null; +} + export class ModifierLabelProvider { public readonly modifierLabels: ModifierLabels[]; @@ -32,11 +36,22 @@ export class ModifierLabelProvider { this.modifierLabels[OperatingSystem.Linux] = linux; } - public toLabel(firstPartMod: Modifiers | null, firstPartKey: string | null, chordPartMod: Modifiers | null, chordPartKey: string | null, OS: OperatingSystem): string | null { - if (firstPartMod === null || firstPartKey === null) { + public toLabel(OS: OperatingSystem, parts: T[], keyLabelProvider: KeyLabelProvider): string | null { + if (parts.length === 0) { return null; } - return _asString(firstPartMod, firstPartKey, chordPartMod, chordPartKey, this.modifierLabels[OS]); + + const result: string[] = []; + for (let i = 0, len = parts.length; i < len; i++) { + const part = parts[i]; + const keyLabel = keyLabelProvider(part); + if (keyLabel === null) { + // this keybinding cannot be expressed... + return null; + } + result[i] = _simpleAsString(part, keyLabel, this.modifierLabels[OS]); + } + return result.join(' '); } } @@ -147,7 +162,7 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe return ''; } - let result: string[] = []; + const result: string[] = []; // translate modifier keys: Ctrl-Shift-Alt-Meta if (modifiers.ctrlKey) { @@ -171,14 +186,3 @@ function _simpleAsString(modifiers: Modifiers, key: string, labels: ModifierLabe return result.join(labels.separator); } - -function _asString(firstPartMod: Modifiers, firstPartKey: string, chordPartMod: Modifiers | null, chordPartKey: string | null, labels: ModifierLabels): string { - let result = _simpleAsString(firstPartMod, firstPartKey, labels); - - if (chordPartMod !== null && chordPartKey !== null) { - result += ' '; - result += _simpleAsString(chordPartMod, chordPartKey, labels); - } - - return result; -} \ No newline at end of file diff --git a/src/vs/base/common/keybindingParser.ts b/src/vs/base/common/keybindingParser.ts index 1d2a0db8914..f7e120acc53 100644 --- a/src/vs/base/common/keybindingParser.ts +++ b/src/vs/base/common/keybindingParser.ts @@ -85,16 +85,14 @@ export class KeybindingParser { return null; } - let [firstPart, remains] = this.parseSimpleKeybinding(input); - let chordPart: SimpleKeybinding | null = null; - if (remains.length > 0) { - [chordPart] = this.parseSimpleKeybinding(remains); - } + const parts: SimpleKeybinding[] = []; + let part: SimpleKeybinding; - if (chordPart) { - return new ChordKeybinding(firstPart, chordPart); - } - return firstPart; + do { + [part, input] = this.parseSimpleKeybinding(input); + parts.push(part); + } while (input.length > 0); + return new ChordKeybinding(parts); } private static parseSimpleUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding, string] { @@ -109,16 +107,18 @@ export class KeybindingParser { return [new SimpleKeybinding(mods.ctrl, mods.shift, mods.alt, mods.meta, keyCode), mods.remains]; } - static parseUserBinding(input: string): [SimpleKeybinding | ScanCodeBinding | null, SimpleKeybinding | ScanCodeBinding | null] { + static parseUserBinding(input: string): (SimpleKeybinding | ScanCodeBinding)[] { if (!input) { - return [null, null]; + return []; } - let [firstPart, remains] = this.parseSimpleUserBinding(input); - let chordPart: SimpleKeybinding | ScanCodeBinding | null = null; - if (remains.length > 0) { - [chordPart] = this.parseSimpleUserBinding(remains); + const parts: (SimpleKeybinding | ScanCodeBinding)[] = []; + let part: SimpleKeybinding | ScanCodeBinding; + + while (input.length > 0) { + [part, input] = this.parseSimpleUserBinding(input); + parts.push(part); } - return [firstPart, chordPart]; + return parts; } } \ No newline at end of file diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 94f776bb64b..39effeb6fdf 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { nativeSep, normalize, basename as pathsBasename, sep } from 'vs/base/common/paths'; -import { endsWith, ltrim, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings'; +import { sep, posix, normalize } from 'vs/base/common/path'; +import { endsWith, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings'; import { Schemas } from 'vs/base/common/network'; import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, basename, relativePath } from 'vs/base/common/resources'; +import { CharCode } from 'vs/base/common/charCode'; export interface IWorkspaceFolderProvider { - getWorkspaceFolder(resource: URI): { uri: URI, name?: string }; + getWorkspaceFolder(resource: URI): { uri: URI, name?: string } | null; getWorkspace(): { folders: { uri: URI, name?: string }[]; }; @@ -36,14 +37,14 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom const hasMultipleRoots = rootProvider.getWorkspace().folders.length > 1; let pathLabel: string; - if (isEqual(baseResource.uri, resource, !isLinux)) { + if (isEqual(baseResource.uri, resource)) { pathLabel = ''; // no label if paths are identical } else { - pathLabel = normalize(ltrim(resource.path.substr(baseResource.uri.path.length), sep)!, true); + pathLabel = relativePath(baseResource.uri, resource)!; } if (hasMultipleRoots) { - const rootName = (baseResource && baseResource.name) ? baseResource.name : pathsBasename(baseResource.uri.fsPath); + const rootName = (baseResource && baseResource.name) ? baseResource.name : basename(baseResource.uri); pathLabel = pathLabel ? (rootName + ' • ' + pathLabel) : rootName; // always show root basename if there are multiple } @@ -58,11 +59,11 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom // convert c:\something => C:\something if (hasDriveLetter(resource.fsPath)) { - return normalize(normalizeDriveLetter(resource.fsPath), true); + return normalize(normalizeDriveLetter(resource.fsPath)); } // normalize and tildify (macOS, Linux only) - let res = normalize(resource.fsPath, true); + let res = normalize(resource.fsPath); if (!isWindows && userHomeProvider) { res = tildify(res, userHomeProvider.userHome); } @@ -70,7 +71,9 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom return res; } -export function getBaseLabel(resource: URI | string): string | undefined { +export function getBaseLabel(resource: URI | string): string; +export function getBaseLabel(resource: URI | string | undefined): string | undefined; +export function getBaseLabel(resource: URI | string | undefined): string | undefined { if (!resource) { return undefined; } @@ -79,7 +82,7 @@ export function getBaseLabel(resource: URI | string): string | undefined { resource = URI.file(resource); } - const base = pathsBasename(resource.path) || (resource.scheme === Schemas.file ? resource.fsPath : resource.path) /* can be empty string if '/' is passed in */; + const base = basename(resource) || (resource.scheme === Schemas.file ? resource.fsPath : resource.path) /* can be empty string if '/' is passed in */; // convert c: => C: if (hasDriveLetter(base)) { @@ -108,9 +111,9 @@ export function tildify(path: string, userHome: string): string { } // Keep a normalized user home path as cache to prevent accumulated string creation - let normalizedUserHome = normalizedUserHomeCached.original === userHome ? normalizedUserHomeCached.normalized : void 0; + let normalizedUserHome = normalizedUserHomeCached.original === userHome ? normalizedUserHomeCached.normalized : undefined; if (!normalizedUserHome) { - normalizedUserHome = `${rtrim(userHome, sep)}${sep}`; + normalizedUserHome = `${rtrim(userHome, posix.sep)}${posix.sep}`; normalizedUserHomeCached = { original: userHome, normalized: normalizedUserHome }; } @@ -167,7 +170,7 @@ export function shorten(paths: string[]): string[] { let path = paths[pathIndex]; if (path === '') { - shortenedPaths[pathIndex] = `.${nativeSep}`; + shortenedPaths[pathIndex] = `.${sep}`; continue; } @@ -183,20 +186,20 @@ export function shorten(paths: string[]): string[] { if (path.indexOf(unc) === 0) { prefix = path.substr(0, path.indexOf(unc) + unc.length); path = path.substr(path.indexOf(unc) + unc.length); - } else if (path.indexOf(nativeSep) === 0) { - prefix = path.substr(0, path.indexOf(nativeSep) + nativeSep.length); - path = path.substr(path.indexOf(nativeSep) + nativeSep.length); + } else if (path.indexOf(sep) === 0) { + prefix = path.substr(0, path.indexOf(sep) + sep.length); + path = path.substr(path.indexOf(sep) + sep.length); } else if (path.indexOf(home) === 0) { prefix = path.substr(0, path.indexOf(home) + home.length); path = path.substr(path.indexOf(home) + home.length); } // pick the first shortest subpath found - const segments: string[] = path.split(nativeSep); + const segments: string[] = path.split(sep); for (let subpathLength = 1; match && subpathLength <= segments.length; subpathLength++) { for (let start = segments.length - subpathLength; match && start >= 0; start--) { match = false; - let subpath = segments.slice(start, start + subpathLength).join(nativeSep); + let subpath = segments.slice(start, start + subpathLength).join(sep); // that is unique to any other path for (let otherPathIndex = 0; !match && otherPathIndex < paths.length; otherPathIndex++) { @@ -207,7 +210,7 @@ export function shorten(paths: string[]): string[] { // Adding separator as prefix for subpath, such that 'endsWith(src, trgt)' considers subpath as directory name instead of plain string. // prefix is not added when either subpath is root directory or path[otherPathIndex] does not have multiple directories. - const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(nativeSep) > -1) ? nativeSep + subpath : subpath; + const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(sep) > -1) ? sep + subpath : subpath; const isOtherPathEnding: boolean = endsWith(paths[otherPathIndex], subpathWithSep); match = !isSubpathEnding || isOtherPathEnding; @@ -224,11 +227,11 @@ export function shorten(paths: string[]): string[] { // extend subpath to include disk drive prefix start = 0; subpathLength++; - subpath = segments[0] + nativeSep + subpath; + subpath = segments[0] + sep + subpath; } if (start > 0) { - result = segments[0] + nativeSep; + result = segments[0] + sep; } result = prefix + result; @@ -236,14 +239,14 @@ export function shorten(paths: string[]): string[] { // add ellipsis at the beginning if neeeded if (start > 0) { - result = result + ellipsis + nativeSep; + result = result + ellipsis + sep; } result = result + subpath; // add ellipsis at the end if needed if (start + subpathLength < segments.length) { - result = result + nativeSep + ellipsis; + result = result + sep + ellipsis; } shortenedPaths[pathIndex] = result; @@ -280,15 +283,12 @@ interface ISegment { * @param value string to which templating is applied * @param values the values of the templates to use */ -export function template(template: string, values: { [key: string]: string | ISeparator } = Object.create(null)): string { +export function template(template: string, values: { [key: string]: string | ISeparator | null } = Object.create(null)): string { const segments: ISegment[] = []; let inVariable = false; - let char: string; let curVal = ''; - for (let i = 0; i < template.length; i++) { - char = template[i]; - + for (const char of template) { // Beginning of variable if (char === '$' || (inVariable && char === '{')) { if (curVal) { @@ -356,26 +356,43 @@ export function template(template: string, values: { [key: string]: string | ISe */ export function mnemonicMenuLabel(label: string, forceDisableMnemonics?: boolean): string { if (isMacintosh || forceDisableMnemonics) { - return label.replace(/\(&&\w\)|&&/g, ''); + return label.replace(/\(&&\w\)|&&/g, '').replace(/&/g, isMacintosh ? '&' : '&&'); } - return label.replace(/&&/g, '&'); + return label.replace(/&&|&/g, m => m === '&' ? '&&' : '&'); } /** * Handles mnemonics for buttons. Depending on OS: - * - Windows: Supported via & character (replace && with &) + * - Windows: Supported via & character (replace && with & and & with && for escaping) * - Linux: Supported via _ character (replace && with _) * - macOS: Unsupported (replace && with empty string) */ -export function mnemonicButtonLabel(label: string): string { - if (isMacintosh) { +export function mnemonicButtonLabel(label: string, forceDisableMnemonics?: boolean): string { + if (isMacintosh || forceDisableMnemonics) { return label.replace(/\(&&\w\)|&&/g, ''); } - return label.replace(/&&/g, isWindows ? '&' : '_'); + if (isWindows) { + return label.replace(/&&|&/g, m => m === '&' ? '&&' : '&'); + } + + return label.replace(/&&/g, '_'); } export function unmnemonicLabel(label: string): string { return label.replace(/&/g, '&&'); } + +/** + * Splits a path in name and parent path, supporting both '/' and '\' + */ +export function splitName(fullPath: string): { name: string, parentPath: string } { + for (let i = fullPath.length - 1; i >= 1; i--) { + const code = fullPath.charCodeAt(i); + if (code === CharCode.Slash || code === CharCode.Backslash) { + return { parentPath: fullPath.substr(0, i), name: fullPath.substr(i + 1) }; + } + } + return { parentPath: '', name: fullPath }; +} diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 1be7705190f..512d2d1225f 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -15,7 +15,7 @@ export function isDisposable(thing: E): thing is E & IDisposab } export function dispose(disposable: T): T; -export function dispose(...disposables: (T | undefined)[]): T[]; +export function dispose(...disposables: Array): T[]; export function dispose(disposables: T[]): T[]; export function dispose(first: T | T[], ...rest: T[]): T | T[] | undefined { if (Array.isArray(first)) { @@ -49,12 +49,20 @@ export abstract class Disposable implements IDisposable { protected _toDispose: IDisposable[] = []; protected get toDispose(): IDisposable[] { return this._toDispose; } + private _lifecycle_disposable_isDisposed = false; + public dispose(): void { + this._lifecycle_disposable_isDisposed = true; this._toDispose = dispose(this._toDispose); } protected _register(t: T): T { - this._toDispose.push(t); + if (this._lifecycle_disposable_isDisposed) { + console.warn('Registering disposable on object that has already been disposed.'); + t.dispose(); + } else { + this._toDispose.push(t); + } return t; } diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index 0867506c499..54bb9253de6 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -6,19 +6,24 @@ import { Iterator, IteratorResult, FIN } from 'vs/base/common/iterator'; class Node { + + static readonly Undefined = new Node(undefined); + element: E; - next: Node | undefined; - prev: Node | undefined; + next: Node; + prev: Node; constructor(element: E) { this.element = element; + this.next = Node.Undefined; + this.prev = Node.Undefined; } } export class LinkedList { - private _first: Node | undefined; - private _last: Node | undefined; + private _first: Node = Node.Undefined; + private _last: Node = Node.Undefined; private _size: number = 0; get size(): number { @@ -26,25 +31,26 @@ export class LinkedList { } isEmpty(): boolean { - return !this._first; + return this._first === Node.Undefined; } clear(): void { - this._first = undefined; - this._last = undefined; + this._first = Node.Undefined; + this._last = Node.Undefined; + this._size = 0; } - unshift(element: E) { - return this.insert(element, false); + unshift(element: E): () => void { + return this._insert(element, false); } - push(element: E) { - return this.insert(element, true); + push(element: E): () => void { + return this._insert(element, true); } - private insert(element: E, atTheEnd: boolean) { + private _insert(element: E, atTheEnd: boolean): () => void { const newNode = new Node(element); - if (!this._first) { + if (this._first === Node.Undefined) { this._first = newNode; this._last = newNode; @@ -64,48 +70,68 @@ export class LinkedList { } this._size += 1; + let didRemove = false; return () => { - let candidate: Node | undefined = this._first; - while (candidate instanceof Node) { - if (candidate !== newNode) { - candidate = candidate.next; - continue; - } - if (candidate.prev && candidate.next) { - // middle - let anchor = candidate.prev; - anchor.next = candidate.next; - candidate.next.prev = anchor; - - } else if (!candidate.prev && !candidate.next) { - // only node - this._first = undefined; - this._last = undefined; - - } else if (!candidate.next) { - // last - this._last = this._last!.prev!; - this._last.next = undefined; - - } else if (!candidate.prev) { - // first - this._first = this._first!.next!; - this._first.prev = undefined; - } - - // done - this._size -= 1; - break; + if (!didRemove) { + didRemove = true; + this._remove(newNode); } }; } + shift(): E | undefined { + if (this._first === Node.Undefined) { + return undefined; + } else { + const res = this._first.element; + this._remove(this._first); + return res; + } + } + + pop(): E | undefined { + if (this._last === Node.Undefined) { + return undefined; + } else { + const res = this._last.element; + this._remove(this._last); + return res; + } + } + + private _remove(node: Node): void { + if (node.prev !== Node.Undefined && node.next !== Node.Undefined) { + // middle + const anchor = node.prev; + anchor.next = node.next; + node.next.prev = anchor; + + } else if (node.prev === Node.Undefined && node.next === Node.Undefined) { + // only node + this._first = Node.Undefined; + this._last = Node.Undefined; + + } else if (node.next === Node.Undefined) { + // last + this._last = this._last!.prev!; + this._last.next = Node.Undefined; + + } else if (node.prev === Node.Undefined) { + // first + this._first = this._first!.next!; + this._first.prev = Node.Undefined; + } + + // done + this._size -= 1; + } + iterator(): Iterator { let element: { done: false; value: E; }; let node = this._first; return { next(): IteratorResult { - if (!node) { + if (node === Node.Undefined) { return FIN; } @@ -121,8 +147,8 @@ export class LinkedList { } toArray(): E[] { - let result: E[] = []; - for (let node = this._first; node instanceof Node; node = node.next) { + const result: E[] = []; + for (let node = this._first; node !== Node.Undefined; node = node.next) { result.push(node.element); } return result; diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index a23d09b66d2..39ec64381aa 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -9,7 +9,7 @@ import { Iterator, IteratorResult, FIN } from './iterator'; export function values(set: Set): V[]; export function values(map: Map): V[]; -export function values(forEachable: { forEach(callback: (value: V, ...more: any[]) => any) }): V[] { +export function values(forEachable: { forEach(callback: (value: V, ...more: any[]) => any): void }): V[] { const result: V[] = []; forEachable.forEach(value => result.push(value)); return result; @@ -24,7 +24,7 @@ export function keys(map: Map): K[] { export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); - if (result === void 0) { + if (result === undefined) { result = value; map.set(key, result); } @@ -50,6 +50,26 @@ export function setToString(set: Set): string { return `Set(${set.size}) {${entries.join(', ')}}`; } +export function mapToSerializable(map: Map): [string, string][] { + const serializable: [string, string][] = []; + + map.forEach((value, key) => { + serializable.push([key, value]); + }); + + return serializable; +} + +export function serializableToMap(serializable: [string, string][]): Map { + const items = new Map(); + + for (const [key, value] of serializable) { + items.set(key, value); + } + + return items; +} + export interface IKeyIterator { reset(key: string): this; next(): this; @@ -80,8 +100,8 @@ export class StringIterator implements IKeyIterator { } cmp(a: string): number { - let aCode = a.charCodeAt(0); - let thisCode = this._value.charCodeAt(this._pos); + const aCode = a.charCodeAt(0); + const thisCode = this._value.charCodeAt(this._pos); return aCode - thisCode; } @@ -129,11 +149,11 @@ export class PathIterator implements IKeyIterator { cmp(a: string): number { let aPos = 0; - let aLen = a.length; + const aLen = a.length; let thisPos = this._from; while (aPos < aLen && thisPos < this._to) { - let cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos); + const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos); if (cmp !== 0) { return cmp; } @@ -190,7 +210,7 @@ export class TernarySearchTree { } set(key: string, element: E): E | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node: TernarySearchTreeNode; if (!this._root) { @@ -200,7 +220,7 @@ export class TernarySearchTree { node = this._root; while (true) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left if (!node.left) { @@ -236,10 +256,10 @@ export class TernarySearchTree { } get(key: string): E | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node = this._root; while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left node = node.left; @@ -259,13 +279,13 @@ export class TernarySearchTree { delete(key: string): void { - let iter = this._iter.reset(key); - let stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; + const iter = this._iter.reset(key); + const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; let node = this._root; // find and unset node while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left stack.push([1, node]); @@ -299,11 +319,11 @@ export class TernarySearchTree { } findSubstr(key: string): E | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node = this._root; let candidate: E | undefined = undefined; while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left node = node.left; @@ -323,10 +343,10 @@ export class TernarySearchTree { } findSuperstr(key: string): Iterator | undefined { - let iter = this._iter.reset(key); + const iter = this._iter.reset(key); let node = this._root; while (node) { - let val = iter.cmp(node.segment); + const val = iter.cmp(node.segment); if (val > 0) { // left node = node.left; @@ -353,7 +373,7 @@ export class TernarySearchTree { let res: { done: false; value: E; }; let idx: number; let data: E[]; - let next = (): IteratorResult => { + const next = (): IteratorResult => { if (!data) { // lazy till first invocation data = []; @@ -407,35 +427,35 @@ export class ResourceMap { this.ignoreCase = false; // in the future this should be an uri-comparator } - public set(resource: URI, value: T): void { + set(resource: URI, value: T): void { this.map.set(this.toKey(resource), value); } - public get(resource: URI): T { + get(resource: URI): T | undefined { return this.map.get(this.toKey(resource)); } - public has(resource: URI): boolean { + has(resource: URI): boolean { return this.map.has(this.toKey(resource)); } - public get size(): number { + get size(): number { return this.map.size; } - public clear(): void { + clear(): void { this.map.clear(); } - public delete(resource: URI): boolean { + delete(resource: URI): boolean { return this.map.delete(this.toKey(resource)); } - public forEach(clb: (value: T) => void): void { + forEach(clb: (value: T) => void): void { this.map.forEach(clb); } - public values(): T[] { + values(): T[] { return values(this.map); } @@ -448,11 +468,11 @@ export class ResourceMap { return key; } - public keys(): URI[] { + keys(): URI[] { return keys(this.map).map(k => URI.parse(k)); } - public clone(): ResourceMap { + clone(): ResourceMap { const resourceMap = new ResourceMap(); this.map.forEach((value, key) => resourceMap.map.set(key, value)); @@ -490,26 +510,26 @@ export class LinkedMap { this._size = 0; } - public clear(): void { + clear(): void { this._map.clear(); this._head = undefined; this._tail = undefined; this._size = 0; } - public isEmpty(): boolean { + isEmpty(): boolean { return !this._head && !this._tail; } - public get size(): number { + get size(): number { return this._size; } - public has(key: K): boolean { + has(key: K): boolean { return this._map.has(key); } - public get(key: K, touch: Touch = Touch.None): V | undefined { + get(key: K, touch: Touch = Touch.None): V | undefined { const item = this._map.get(key); if (!item) { return undefined; @@ -520,7 +540,7 @@ export class LinkedMap { return item.value; } - public set(key: K, value: V, touch: Touch = Touch.None): void { + set(key: K, value: V, touch: Touch = Touch.None): void { let item = this._map.get(key); if (item) { item.value = value; @@ -548,11 +568,11 @@ export class LinkedMap { } } - public delete(key: K): boolean { + delete(key: K): boolean { return !!this.remove(key); } - public remove(key: K): V | undefined { + remove(key: K): V | undefined { const item = this._map.get(key); if (!item) { return undefined; @@ -563,7 +583,7 @@ export class LinkedMap { return item.value; } - public shift(): V | undefined { + shift(): V | undefined { if (!this._head && !this._tail) { return undefined; } @@ -577,7 +597,7 @@ export class LinkedMap { return item.value; } - public forEach(callbackfn: (value: V, key: K, map: LinkedMap) => void, thisArg?: any): void { + forEach(callbackfn: (value: V, key: K, map: LinkedMap) => void, thisArg?: any): void { let current = this._head; while (current) { if (thisArg) { @@ -589,8 +609,8 @@ export class LinkedMap { } } - public values(): V[] { - let result: V[] = []; + values(): V[] { + const result: V[] = []; let current = this._head; while (current) { result.push(current.value); @@ -599,8 +619,8 @@ export class LinkedMap { return result; } - public keys(): K[] { - let result: K[] = []; + keys(): K[] { + const result: K[] = []; let current = this._head; while (current) { result.push(current.key); @@ -610,15 +630,15 @@ export class LinkedMap { } /* VS Code / Monaco editor runs on es5 which has no Symbol.iterator - public keys(): IterableIterator { - let current = this._head; - let iterator: IterableIterator = { + keys(): IterableIterator { + const current = this._head; + const iterator: IterableIterator = { [Symbol.iterator]() { return iterator; }, next():IteratorResult { if (current) { - let result = { value: current.key, done: false }; + const result = { value: current.key, done: false }; current = current.next; return result; } else { @@ -629,15 +649,15 @@ export class LinkedMap { return iterator; } - public values(): IterableIterator { - let current = this._head; - let iterator: IterableIterator = { + values(): IterableIterator { + const current = this._head; + const iterator: IterableIterator = { [Symbol.iterator]() { return iterator; }, next():IteratorResult { if (current) { - let result = { value: current.value, done: false }; + const result = { value: current.value, done: false }; current = current.next; return result; } else { @@ -667,7 +687,7 @@ export class LinkedMap { this._head = current; this._size = currentSize; if (current) { - current.previous = void 0; + current.previous = undefined; } } @@ -699,13 +719,25 @@ export class LinkedMap { private removeItem(item: Item): void { if (item === this._head && item === this._tail) { - this._head = void 0; - this._tail = void 0; + this._head = undefined; + this._tail = undefined; } else if (item === this._head) { + // This can only happend if size === 1 which is handle + // by the case above. + if (!item.next) { + throw new Error('Invalid list'); + } + item.next.previous = undefined; this._head = item.next; } else if (item === this._tail) { + // This can only happend if size === 1 which is handle + // by the case above. + if (!item.previous) { + throw new Error('Invalid list'); + } + item.previous.next = undefined; this._tail = item.previous; } else { @@ -717,6 +749,8 @@ export class LinkedMap { next.previous = previous; previous.next = next; } + item.next = undefined; + item.previous = undefined; } private touch(item: Item, touch: Touch): void { @@ -739,7 +773,7 @@ export class LinkedMap { if (item === this._tail) { // previous must be defined since item was not head but is tail // So there are more than on item in the map - previous!.next = void 0; + previous!.next = undefined; this._tail = previous; } else { @@ -749,7 +783,7 @@ export class LinkedMap { } // Insert the node at head - item.previous = void 0; + item.previous = undefined; item.next = this._head; this._head.previous = item; this._head = item; @@ -765,21 +799,21 @@ export class LinkedMap { if (item === this._head) { // next must be defined since item was not tail but is head // So there are more than on item in the map - next!.previous = void 0; + next!.previous = undefined; this._head = next; } else { // Both next and previous are not undefined since item was neither head nor tail. next!.previous = previous; previous!.next = next; } - item.next = void 0; + item.next = undefined; item.previous = this._tail; this._tail.next = item; this._tail = item; } } - public toJSON(): [K, V][] { + toJSON(): [K, V][] { const data: [K, V][] = []; this.forEach((value, key) => { @@ -789,7 +823,7 @@ export class LinkedMap { return data; } - public fromJSON(data: [K, V][]): void { + fromJSON(data: [K, V][]): void { this.clear(); for (const [key, value] of data) { @@ -809,33 +843,33 @@ export class LRUCache extends LinkedMap { this._ratio = Math.min(Math.max(0, ratio), 1); } - public get limit(): number { + get limit(): number { return this._limit; } - public set limit(limit: number) { + set limit(limit: number) { this._limit = limit; this.checkTrim(); } - public get ratio(): number { + get ratio(): number { return this._ratio; } - public set ratio(ratio: number) { + set ratio(ratio: number) { this._ratio = Math.min(Math.max(0, ratio), 1); this.checkTrim(); } - public get(key: K): V | undefined { + get(key: K): V | undefined { return super.get(key, Touch.AsNew); } - public peek(key: K): V | undefined { + peek(key: K): V | undefined { return super.get(key, Touch.None); } - public set(key: K, value: V): void { + set(key: K, value: V): void { super.set(key, value, Touch.AsNew); this.checkTrim(); } diff --git a/src/vs/base/common/marked/cgmanifest.json b/src/vs/base/common/marked/cgmanifest.json index b0c1ce8f3d5..f3477931d13 100644 --- a/src/vs/base/common/marked/cgmanifest.json +++ b/src/vs/base/common/marked/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "marked", "repositoryUrl": "https://github.com/markedjs/marked", - "commitHash": "78c977bc3a47f9e2fb146477d1ca3dad0cb134e6" + "commitHash": "529a8d4e185a8aa561e4d8d2891f8556b5717cd4" } }, "license": "MIT", - "version": "0.5.0" + "version": "0.6.2" } ], "version": 1 diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index bde18dff976..1288f459647 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -23,7 +23,7 @@ var block = { heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/, nptable: noop, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, + list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -31,8 +31,8 @@ var block = { + '|\\n*' // (4) + '|\\n*' // (5) + '|)[\\s\\S]*?(?:\\n{2,}|$)' // (6) - + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag - + '|(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag + + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag + + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag + ')', def: /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/, table: noop, @@ -48,8 +48,8 @@ block.def = edit(block.def) .replace('title', block._title) .getRegex(); -block.bullet = /(?:[*+-]|\d+\.)/; -block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; +block.bullet = /(?:[*+-]|\d{1,9}\.)/; +block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/; block.item = edit(block.item, 'gm') .replace(/bull/g, block.bullet) .getRegex(); @@ -95,7 +95,7 @@ block.normal = merge({}, block); */ block.gfm = merge({}, block.normal, { - fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/, + fences: /^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/, paragraph: /^/, heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ }); @@ -235,7 +235,7 @@ Lexer.prototype.token = function(src, top) { src = src.substring(cap[0].length); this.tokens.push({ type: 'code', - lang: cap[2], + lang: cap[2] ? cap[2].trim() : cap[2], text: cap[3] || '' }); continue; @@ -253,7 +253,7 @@ Lexer.prototype.token = function(src, top) { } // table no leading pipe (gfm) - if (top && (cap = this.rules.nptable.exec(src))) { + if (cap = this.rules.nptable.exec(src)) { item = { type: 'table', header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), @@ -346,7 +346,7 @@ Lexer.prototype.token = function(src, top) { // Remove the list item's bullet // so it is seen as the next token. space = item.length; - item = item.replace(/^ *([*+-]|\d+\.) +/, ''); + item = item.replace(/^ *([*+-]|\d+\.) */, ''); // Outdent whatever the // list item contains. Hacky. @@ -359,9 +359,10 @@ Lexer.prototype.token = function(src, top) { // Determine whether the next list item belongs here. // Backpedal if it does not belong in this list. - if (this.options.smartLists && i !== l - 1) { + if (i !== l - 1) { b = block.bullet.exec(cap[i + 1])[0]; - if (bull !== b && !(bull.length > 1 && b.length > 1)) { + if (bull.length > 1 ? b.length === 1 + : (b.length > 1 || (this.options.smartLists && b !== bull))) { src = cap.slice(i + 1).join('\n') + src; i = l - 1; } @@ -450,12 +451,12 @@ Lexer.prototype.token = function(src, top) { } // table (gfm) - if (top && (cap = this.rules.table.exec(src))) { + if (cap = this.rules.table.exec(src)) { item = { type: 'table', header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : [] + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] }; if (item.header.length === item.align.length) { @@ -544,14 +545,19 @@ var inline = { link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/, reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/, nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/, - strong: /^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/, - em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/, - code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/, + strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/, + em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/, + code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, br: /^( {2,}|\\)\n(?!\s*$)/, del: noop, - text: /^[\s\S]+?(?=[\\?@\\[^_{|}~'; +inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex(); + inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g; inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; @@ -568,8 +574,8 @@ inline.tag = edit(inline.tag) .replace('attribute', inline._attribute) .getRegex(); -inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/; -inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/; +inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|`(?!`)|[^\[\]\\`])*?/; +inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)/; inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; inline.link = edit(inline.link) @@ -609,24 +615,23 @@ inline.pedantic = merge({}, inline.normal, { inline.gfm = merge({}, inline.normal, { escape: edit(inline.escape).replace('])', '~|])').getRegex(), - url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/) - .replace('email', inline._email) - .getRegex(), + _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/, + url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, del: /^~+(?=\S)([\s\S]*?\S)~+/, - text: edit(inline.text) - .replace(']|', '~]|') - .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|') - .getRegex() + text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(cap[0])) { this.inLink = false; } + if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.inRawBlock = true; + } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.inRawBlock = false; + } + src = src.substring(cap[0].length); out += this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]) - : cap[0] + : cap[0]; continue; } // link if (cap = this.rules.link.exec(src)) { + var lastParenIndex = findClosingBracket(cap[2], '()'); + if (lastParenIndex > -1) { + var linkLen = cap[0].length - (cap[2].length - lastParenIndex) - (cap[3] || '').length; + cap[2] = cap[2].substring(0, lastParenIndex); + cap[0] = cap[0].substring(0, linkLen).trim(); + cap[3] = ''; + } src = src.substring(cap[0].length); this.inLink = true; href = cap[2]; @@ -821,10 +803,51 @@ InlineLexer.prototype.output = function(src) { continue; } + // autolink + if (cap = this.rules.autolink.exec(src)) { + src = src.substring(cap[0].length); + if (cap[2] === '@') { + text = escape(this.mangle(cap[1])); + href = 'mailto:' + text; + } else { + text = escape(cap[1]); + href = text; + } + out += this.renderer.link(href, null, text); + continue; + } + + // url (gfm) + if (!this.inLink && (cap = this.rules.url.exec(src))) { + if (cap[2] === '@') { + text = escape(cap[0]); + href = 'mailto:' + text; + } else { + // do extended autolink path validation + do { + prevCapZero = cap[0]; + cap[0] = this.rules._backpedal.exec(cap[0])[0]; + } while (prevCapZero !== cap[0]); + text = escape(cap[0]); + if (cap[1] === 'www.') { + href = 'http://' + text; + } else { + href = text; + } + } + src = src.substring(cap[0].length); + out += this.renderer.link(href, null, text); + continue; + } + // text if (cap = this.rules.text.exec(src)) { src = src.substring(cap[0].length); - out += this.renderer.text(escape(this.smartypants(cap[0]))); + if (this.inRawBlock) { + out += this.renderer.text(cap[0]); + } else { + out += this.renderer.text(escape(this.smartypants(cap[0]))); + } continue; } @@ -838,7 +861,7 @@ InlineLexer.prototype.output = function(src) { InlineLexer.escapes = function(text) { return text ? text.replace(InlineLexer.rules._escapes, '$1') : text; -} +}; /** * Compile Link @@ -906,7 +929,8 @@ function Renderer(options) { this.options = options || marked.defaults; } -Renderer.prototype.code = function(code, lang, escaped) { +Renderer.prototype.code = function(code, infostring, escaped) { + var lang = (infostring || '').match(/\S*/)[0]; if (this.options.highlight) { var out = this.options.highlight(code, lang); if (out != null && out !== code) { @@ -937,13 +961,13 @@ Renderer.prototype.html = function(html) { return html; }; -Renderer.prototype.heading = function(text, level, raw) { +Renderer.prototype.heading = function(text, level, raw, slugger) { if (this.options.headerIds) { return '' + text + ' '; -} +}; Renderer.prototype.paragraph = function(text) { return '

' + text + '

\n'; @@ -1025,24 +1049,8 @@ Renderer.prototype.del = function(text) { }; Renderer.prototype.link = function(href, title, text) { - if (this.options.sanitize) { - try { - var prot = decodeURIComponent(unescape(href)) - .replace(/[^\w:]/g, '') - .toLowerCase(); - } catch (e) { - return text; - } - if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { - return text; - } - } - if (this.options.baseUrl && !originIndependentUrl.test(href)) { - href = resolveUrl(this.options.baseUrl, href); - } - try { - href = encodeURI(href).replace(/%25/g, '%'); - } catch (e) { + href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); + if (href === null) { return text; } var out = '?@[\]^`{|}~]/g, '') + .replace(/\s/g, '-'); + + if (this.seen.hasOwnProperty(slug)) { + var originalSlug = slug; + do { + this.seen[originalSlug]++; + slug = originalSlug + '-' + this.seen[originalSlug]; + } while (this.seen.hasOwnProperty(slug)); + } + this.seen[slug] = 0; + + return slug; +}; + /** * Helpers */ function escape(html, encode) { - return html - .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + if (encode) { + if (escape.escapeTest.test(html)) { + return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; }); + } + } else { + if (escape.escapeTestNoEncode.test(html)) { + return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; }); + } + } + + return html; } +escape.escapeTest = /[&<>"']/; +escape.escapeReplace = /[&<>"']/g; +escape.replacements = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' +}; + +escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/; +escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g; + function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) { @@ -1316,6 +1386,30 @@ function edit(regex, opt) { }; } +function cleanUrl(sanitize, base, href) { + if (sanitize) { + try { + var prot = decodeURIComponent(unescape(href)) + .replace(/[^\w:]/g, '') + .toLowerCase(); + } catch (e) { + return null; + } + if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { + return null; + } + } + if (base && !originIndependentUrl.test(href)) { + href = resolveUrl(base, href); + } + try { + href = encodeURI(href).replace(/%25/g, '%'); + } catch (e) { + return null; + } + return href; +} + function resolveUrl(base, href) { if (!baseUrls[' ' + base]) { // we can ignore everything in base after the last slash of its path component, @@ -1418,6 +1512,26 @@ function rtrim(str, c, invert) { return str.substr(0, str.length - suffLen); } +function findClosingBracket(str, b) { + if (str.indexOf(b[1]) === -1) { + return -1; + } + var level = 0; + for (var i = 0; i < str.length; i++) { + if (str[i] === '\\') { + i++; + } else if (str[i] === b[0]) { + level++; + } else if (str[i] === b[1]) { + level--; + if (level < 0) { + return i; + } + } + } + return -1; +} + /** * Marked */ @@ -1446,7 +1560,7 @@ function marked(src, opt, callback) { i = 0; try { - tokens = Lexer.lex(src, opt) + tokens = Lexer.lex(src, opt); } catch (e) { return callback(e); } @@ -1545,7 +1659,7 @@ marked.getDefaults = function () { tables: true, xhtml: false }; -} +}; marked.defaults = marked.getDefaults(); @@ -1565,6 +1679,8 @@ marked.lexer = Lexer.lex; marked.InlineLexer = InlineLexer; marked.inlineLexer = InlineLexer.output; +marked.Slugger = Slugger; + marked.parse = marked; // BEGIN MONACOCHANGE @@ -1582,7 +1698,7 @@ __marked_exports = marked; // ESM-comment-begin define(function() { return __marked_exports; }); // ESM-comment-end - + // ESM-uncomment-begin // export var marked = __marked_exports; // export var Parser = __marked_exports.Parser; diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 5acdcab444c..e479a2db973 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; +import { regExpFlags } from 'vs/base/common/strings'; export function stringify(obj: any): string { return JSON.stringify(obj, replacer); @@ -24,8 +25,8 @@ function replacer(key: string, value: any): any { if (value instanceof RegExp) { return { $mid: 2, - source: (value).source, - flags: ((value).global ? 'g' : '') + ((value).ignoreCase ? 'i' : '') + ((value).multiline ? 'm' : ''), + source: value.source, + flags: regExpFlags(value), }; } return value; diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index d7777d9dd13..70e70de33a1 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as paths from 'vs/base/common/paths'; -import * as strings from 'vs/base/common/strings'; -import * as arrays from 'vs/base/common/arrays'; +import { basename, posix, extname } from 'vs/base/common/path'; +import { endsWith, startsWithUTF8BOM, startsWith } from 'vs/base/common/strings'; +import { coalesce } from 'vs/base/common/arrays'; import { match } from 'vs/base/common/glob'; export const MIME_TEXT = 'text/plain'; @@ -13,20 +13,20 @@ export const MIME_BINARY = 'application/octet-stream'; export const MIME_UNKNOWN = 'application/unknown'; export interface ITextMimeAssociation { - id: string; - mime: string; - filename?: string; - extension?: string; - filepattern?: string; - firstline?: RegExp; - userConfigured?: boolean; + readonly id: string; + readonly mime: string; + readonly filename?: string; + readonly extension?: string; + readonly filepattern?: string; + readonly firstline?: RegExp; + readonly userConfigured?: boolean; } interface ITextMimeAssociationItem extends ITextMimeAssociation { - filenameLowercase?: string; - extensionLowercase?: string; - filepatternLowercase?: string; - filepatternOnPath?: boolean; + readonly filenameLowercase?: string; + readonly extensionLowercase?: string; + readonly filepatternLowercase?: string; + readonly filepatternOnPath?: boolean; } let registeredAssociations: ITextMimeAssociationItem[] = []; @@ -82,10 +82,10 @@ function toTextMimeAssociationItem(association: ITextMimeAssociation): ITextMime filepattern: association.filepattern, firstline: association.firstline, userConfigured: association.userConfigured, - filenameLowercase: association.filename ? association.filename.toLowerCase() : void 0, - extensionLowercase: association.extension ? association.extension.toLowerCase() : void 0, - filepatternLowercase: association.filepattern ? association.filepattern.toLowerCase() : void 0, - filepatternOnPath: association.filepattern ? association.filepattern.indexOf(paths.sep) >= 0 : false + filenameLowercase: association.filename ? association.filename.toLowerCase() : undefined, + extensionLowercase: association.extension ? association.extension.toLowerCase() : undefined, + filepatternLowercase: association.filepattern ? association.filepattern.toLowerCase() : undefined, + filepatternOnPath: association.filepattern ? association.filepattern.indexOf(posix.sep) >= 0 : false }; } @@ -106,20 +106,18 @@ export function clearTextMimes(onlyUserConfigured?: boolean): void { /** * Given a file, return the best matching mime type for it */ -export function guessMimeTypes(path: string, firstLine?: string, skipUserAssociations: boolean = false): string[] { +export function guessMimeTypes(path: string | null, firstLine?: string): string[] { if (!path) { return [MIME_UNKNOWN]; } path = path.toLowerCase(); - const filename = paths.basename(path); + const filename = basename(path); - if (!skipUserAssociations) { - // 1.) User configured mappings have highest priority - const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); - if (configuredMime) { - return [configuredMime, MIME_TEXT]; - } + // 1.) User configured mappings have highest priority + const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); + if (configuredMime) { + return [configuredMime, MIME_TEXT]; } // 2.) Registered mappings have middle priority @@ -168,7 +166,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText // Longest extension match if (association.extension) { if (!extensionMatch || association.extension.length > extensionMatch.extension!.length) { - if (strings.endsWith(filename, association.extensionLowercase!)) { + if (endsWith(filename, association.extensionLowercase!)) { extensionMatch = association; } } @@ -194,12 +192,15 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText } function guessMimeTypeByFirstline(firstLine: string): string | null { - if (strings.startsWithUTF8BOM(firstLine)) { + if (startsWithUTF8BOM(firstLine)) { firstLine = firstLine.substr(1); } if (firstLine.length > 0) { - for (let i = 0; i < registeredAssociations.length; ++i) { + + // We want to prioritize associations based on the order they are registered so that the last registered + // association wins over all other. This is for https://github.com/Microsoft/vscode/issues/20074 + for (let i = registeredAssociations.length - 1; i >= 0; i--) { const association = registeredAssociations[i]; if (!association.firstline) { continue; @@ -233,12 +234,13 @@ export function isUnspecific(mime: string[] | string): boolean { * 2. Otherwise, if there are other extensions, suggest the first one. * 3. Otherwise, suggest the prefix. */ -export function suggestFilename(langId: string, prefix: string): string { +export function suggestFilename(mode: string | undefined, prefix: string): string { const extensions = registeredAssociations - .filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === langId) + .filter(assoc => !assoc.userConfigured && assoc.extension && assoc.id === mode) .map(assoc => assoc.extension); - const extensionsWithDotFirst = arrays.coalesce(extensions) - .filter(assoc => strings.startsWith(assoc, '.')); + + const extensionsWithDotFirst = coalesce(extensions) + .filter(assoc => startsWith(assoc, '.')); if (extensionsWithDotFirst.length > 0) { return prefix + extensionsWithDotFirst[0]; @@ -302,6 +304,6 @@ const mapExtToMediaMimes: MapExtToMediaMimes = { }; export function getMediaMime(path: string): string | undefined { - const ext = paths.extname(path); + const ext = extname(path); return mapExtToMediaMimes[ext.toLowerCase()]; } diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 1915b8fb6f3..a7466e641af 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -42,4 +42,8 @@ export namespace Schemas { export const untitled: string = 'untitled'; export const data: string = 'data'; + + export const command: string = 'command'; + + export const vscodeRemote: string = 'vscode-remote'; } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index fe461fe6b7b..97c8f9196c0 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -14,7 +14,7 @@ export function deepClone(obj: T): T { return obj as any; } const result: any = Array.isArray(obj) ? [] : {}; - Object.keys(obj).forEach((key: string) => { + Object.keys(obj as any).forEach((key: string) => { if (obj[key] && typeof obj[key] === 'object') { result[key] = deepClone(obj[key]); } else { @@ -30,11 +30,11 @@ export function deepFreeze(obj: T): T { } const stack: any[] = [obj]; while (stack.length > 0) { - let obj = stack.shift(); + const obj = stack.shift(); Object.freeze(obj); for (const key in obj) { if (_hasOwnProperty.call(obj, key)) { - let prop = obj[key]; + const prop = obj[key]; if (typeof prop === 'object' && !Object.isFrozen(prop)) { stack.push(prop); } @@ -47,10 +47,10 @@ export function deepFreeze(obj: T): T { const _hasOwnProperty = Object.prototype.hasOwnProperty; export function cloneAndChange(obj: any, changer: (orig: any) => any): any { - return _cloneAndChange(obj, changer, []); + return _cloneAndChange(obj, changer, new Set()); } -function _cloneAndChange(obj: any, changer: (orig: any) => any, encounteredObjects: any[]): any { +function _cloneAndChange(obj: any, changer: (orig: any) => any, seen: Set): any { if (isUndefinedOrNull(obj)) { return obj; } @@ -62,24 +62,24 @@ function _cloneAndChange(obj: any, changer: (orig: any) => any, encounteredObjec if (isArray(obj)) { const r1: any[] = []; - for (let i1 = 0; i1 < obj.length; i1++) { - r1.push(_cloneAndChange(obj[i1], changer, encounteredObjects)); + for (const e of obj) { + r1.push(_cloneAndChange(e, changer, seen)); } return r1; } if (isObject(obj)) { - if (encounteredObjects.indexOf(obj) >= 0) { + if (seen.has(obj)) { throw new Error('Cannot clone recursive data-structure'); } - encounteredObjects.push(obj); + seen.add(obj); const r2 = {}; for (let i2 in obj) { if (_hasOwnProperty.call(obj, i2)) { - (r2 as any)[i2] = _cloneAndChange(obj[i2], changer, encounteredObjects); + (r2 as any)[i2] = _cloneAndChange(obj[i2], changer, seen); } } - encounteredObjects.pop(); + seen.delete(obj); return r2; } @@ -113,6 +113,10 @@ export function mixin(destination: any, source: any, overwrite: boolean = true): return destination; } +export function assign(destination: T): T; +export function assign(destination: T, u: U): T & U; +export function assign(destination: T, u: U, v: V): T & U & V; +export function assign(destination: T, u: U, v: V, w: W): T & U & V & W; export function assign(destination: any, ...sources: any[]): any { sources.forEach(source => Object.keys(source).forEach(key => destination[key] = source[key])); return destination; @@ -171,34 +175,6 @@ export function equals(one: any, other: any): boolean { return true; } -export function arrayToHash(array: any[]) { - const result: any = {}; - for (let i = 0; i < array.length; ++i) { - result[array[i]] = true; - } - return result; -} - -/** - * Given an array of strings, returns a function which, given a string - * returns true or false whether the string is in that array. - */ -export function createKeywordMatcher(arr: string[], caseInsensitive: boolean = false): (str: string) => boolean { - if (caseInsensitive) { - arr = arr.map(function (x) { return x.toLowerCase(); }); - } - const hash = arrayToHash(arr); - if (caseInsensitive) { - return function (word) { - return hash[word.toLowerCase()] !== undefined && hash.hasOwnProperty(word.toLowerCase()); - }; - } else { - return function (word) { - return hash[word] !== undefined && hash.hasOwnProperty(word); - }; - } -} - /** * Calls JSON.Stringify with a replacer to break apart any circular references. * This prevents JSON.stringify from throwing the exception @@ -218,12 +194,7 @@ export function safeStringify(obj: any): string { }); } -export function getOrDefault(obj: T, fn: (obj: T) => R, defaultValue: R | null = null): R | null { - const result = fn(obj); - return typeof result === 'undefined' ? defaultValue : result; -} - -export function getOrDefault2(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R { +export function getOrDefault(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R { const result = fn(obj); return typeof result === 'undefined' ? defaultValue : result; } diff --git a/src/vs/base/common/octicon.ts b/src/vs/base/common/octicon.ts index 580ab40cf02..48da86f6cc1 100644 --- a/src/vs/base/common/octicon.ts +++ b/src/vs/base/common/octicon.ts @@ -30,7 +30,7 @@ function doParseOcticons(text: string, firstOcticonIndex: number): IParsedOctico if (chars) { textWithoutOcticons += chars; - for (let i = 0; i < chars.length; i++) { + for (const _ of chars) { octiconOffsets.push(octiconsOffset); // make sure to fill in octicon offsets } } @@ -115,10 +115,10 @@ export function matchesFuzzyOcticonAware(query: string, target: IParsedOcticons, // Map matches back to offsets with octicons and trimming if (matches) { - for (let i = 0; i < matches.length; i++) { - const octiconOffset = octiconOffsets[matches[i].start + leadingWhitespaceOffset] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */; - matches[i].start += octiconOffset; - matches[i].end += octiconOffset; + for (const match of matches) { + const octiconOffset = octiconOffsets[match.start + leadingWhitespaceOffset] /* octicon offsets at index */ + leadingWhitespaceOffset /* overall leading whitespace offset */; + match.start += octiconOffset; + match.end += octiconOffset; } } diff --git a/src/vs/base/common/paging.ts b/src/vs/base/common/paging.ts index a6518831a93..cc808e645b7 100644 --- a/src/vs/base/common/paging.ts +++ b/src/vs/base/common/paging.ts @@ -15,12 +15,12 @@ export interface IPager { firstPage: T[]; total: number; pageSize: number; - getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable; + getPage(pageIndex: number, cancellationToken: CancellationToken): Promise; } interface IPage { isResolved: boolean; - promise: Thenable | null; + promise: Promise | null; cts: CancellationTokenSource | null; promiseIndexes: Set; elements: T[]; @@ -43,7 +43,7 @@ export interface IPagedModel { length: number; isResolved(index: number): boolean; get(index: number): T; - resolve(index: number, cancellationToken: CancellationToken): Thenable; + resolve(index: number, cancellationToken: CancellationToken): Promise; } export function singlePagePager(elements: T[]): IPager { @@ -51,7 +51,7 @@ export function singlePagePager(elements: T[]): IPager { firstPage: elements, total: elements.length, pageSize: elements.length, - getPage: (pageIndex: number, cancellationToken: CancellationToken): Thenable => { + getPage: (pageIndex: number, cancellationToken: CancellationToken): Promise => { return Promise.resolve(elements); } }; @@ -90,7 +90,7 @@ export class PagedModel implements IPagedModel { return page.elements[indexInPage]; } - resolve(index: number, cancellationToken: CancellationToken): Thenable { + resolve(index: number, cancellationToken: CancellationToken): Promise { if (cancellationToken.isCancellationRequested) { return Promise.reject(canceled()); } @@ -151,7 +151,7 @@ export class DelayedPagedModel implements IPagedModel { return this.model.get(index); } - resolve(index: number, cancellationToken: CancellationToken): Thenable { + resolve(index: number, cancellationToken: CancellationToken): Promise { return new Promise((c, e) => { if (cancellationToken.isCancellationRequested) { return e(canceled()); @@ -196,7 +196,7 @@ export function mergePagers(one: IPager, other: IPager): IPager { firstPage: [...one.firstPage, ...other.firstPage], total: one.total + other.total, pageSize: one.pageSize + other.pageSize, - getPage(pageIndex: number, token): Thenable { + getPage(pageIndex: number, token): Promise { return Promise.all([one.getPage(pageIndex, token), other.getPage(pageIndex, token)]) .then(([onePage, otherPage]) => [...onePage, ...otherPage]); } diff --git a/src/vs/base/common/parsers.ts b/src/vs/base/common/parsers.ts index da9010662fa..748abd4fd47 100644 --- a/src/vs/base/common/parsers.ts +++ b/src/vs/base/common/parsers.ts @@ -79,10 +79,10 @@ export abstract class Parser { this._problemReporter.fatal(message); } - protected static merge(destination: T, source: T, overwrite: boolean): void { + protected static merge(destination: T, source: T, overwrite: boolean): void { Object.keys(source).forEach((key: string) => { - let destValue = destination[key]; - let sourceValue = source[key]; + const destValue = destination[key]; + const sourceValue = source[key]; if (Types.isUndefined(sourceValue)) { return; } diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts new file mode 100644 index 00000000000..029bfc01632 --- /dev/null +++ b/src/vs/base/common/path.ts @@ -0,0 +1,1683 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace +// Copied from: https://github.com/nodejs/node/tree/43dd49c9782848c25e5b03448c8a0f923f13c158 + +/** + * Copyright Joyent, Inc. and other Node 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: + * + * 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. + */ + +import * as process from 'vs/base/common/process'; + +const CHAR_UPPERCASE_A = 65;/* A */ +const CHAR_LOWERCASE_A = 97; /* a */ +const CHAR_UPPERCASE_Z = 90; /* Z */ +const CHAR_LOWERCASE_Z = 122; /* z */ +const CHAR_DOT = 46; /* . */ +const CHAR_FORWARD_SLASH = 47; /* / */ +const CHAR_BACKWARD_SLASH = 92; /* \ */ +const CHAR_COLON = 58; /* : */ +const CHAR_QUESTION_MARK = 63; /* ? */ + +class ErrorInvalidArgType extends Error { + code: 'ERR_INVALID_ARG_TYPE'; + constructor(name: string, expected: string, actual: any) { + // determiner: 'must be' or 'must not be' + let determiner; + if (typeof expected === 'string' && expected.indexOf('not ') === 0) { + determiner = 'must not be'; + expected = expected.replace(/^not /, ''); + } else { + determiner = 'must be'; + } + + const type = name.indexOf('.') !== -1 ? 'property' : 'argument'; + let msg = `The "${name}" ${type} ${determiner} of type ${expected}`; + + msg += `. Received type ${typeof actual}`; + super(msg); + } +} + +function validateString(value: string, name: string) { + if (typeof value !== 'string') { + throw new ErrorInvalidArgType(name, 'string', value); + } +} + +function isPathSeparator(code: number) { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; +} + +function isPosixPathSeparator(code: number) { + return code === CHAR_FORWARD_SLASH; +} + +function isWindowsDeviceRoot(code: number) { + return code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z || + code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z; +} + +// Resolves . and .. elements in a path with directory names +function normalizeString(path: string, allowAboveRoot: boolean, separator: string, isPathSeparator: (code?: number) => boolean) { + let res = ''; + let lastSegmentLength = 0; + let lastSlash = -1; + let dots = 0; + let code; + for (let i = 0; i <= path.length; ++i) { + if (i < path.length) { + code = path.charCodeAt(i); + } + else if (isPathSeparator(code)) { + break; + } + else { + code = CHAR_FORWARD_SLASH; + } + + if (isPathSeparator(code)) { + if (lastSlash === i - 1 || dots === 1) { + // NOOP + } else if (lastSlash !== i - 1 && dots === 2) { + if (res.length < 2 || lastSegmentLength !== 2 || + res.charCodeAt(res.length - 1) !== CHAR_DOT || + res.charCodeAt(res.length - 2) !== CHAR_DOT) { + if (res.length > 2) { + const lastSlashIndex = res.lastIndexOf(separator); + if (lastSlashIndex === -1) { + res = ''; + lastSegmentLength = 0; + } else { + res = res.slice(0, lastSlashIndex); + lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); + } + lastSlash = i; + dots = 0; + continue; + } else if (res.length === 2 || res.length === 1) { + res = ''; + lastSegmentLength = 0; + lastSlash = i; + dots = 0; + continue; + } + } + if (allowAboveRoot) { + if (res.length > 0) { + res += `${separator}..`; + } + else { + res = '..'; + } + lastSegmentLength = 2; + } + } else { + if (res.length > 0) { + res += separator + path.slice(lastSlash + 1, i); + } + else { + res = path.slice(lastSlash + 1, i); + } + lastSegmentLength = i - lastSlash - 1; + } + lastSlash = i; + dots = 0; + } else if (code === CHAR_DOT && dots !== -1) { + ++dots; + } else { + dots = -1; + } + } + return res; +} + +function _format(sep: string, pathObject: ParsedPath) { + const dir = pathObject.dir || pathObject.root; + const base = pathObject.base || + ((pathObject.name || '') + (pathObject.ext || '')); + if (!dir) { + return base; + } + if (dir === pathObject.root) { + return dir + base; + } + return dir + sep + base; +} + +interface ParsedPath { + root: string; + dir: string; + base: string; + ext: string; + name: string; +} + +interface IPath { + normalize(path: string): string; + isAbsolute(path: string): boolean; + join(...paths: string[]): string; + resolve(...pathSegments: string[]): string; + relative(from: string, to: string): string; + dirname(path: string): string; + basename(path: string, ext?: string): string; + extname(path: string): string; + format(pathObject: ParsedPath): string; + parse(path: string): ParsedPath; + toNamespacedPath(path: string): string; + sep: '\\' | '/'; + delimiter: string; + win32: IPath | null; + posix: IPath | null; +} + +export const win32: IPath = { + // path.resolve([from ...], to) + resolve(...pathSegments: string[]): string { + let resolvedDevice = ''; + let resolvedTail = ''; + let resolvedAbsolute = false; + + for (let i = pathSegments.length - 1; i >= -1; i--) { + let path; + if (i >= 0) { + path = pathSegments[i]; + } else if (!resolvedDevice) { + path = process.cwd(); + } else { + // Windows has the concept of drive-specific current working + // directories. If we've resolved a drive letter but not yet an + // absolute path, get cwd for that drive, or the process cwd if + // the drive cwd is not available. We're sure the device is not + // a UNC path at this points, because UNC paths are always absolute. + path = process.env['=' + resolvedDevice] || process.cwd(); + + // Verify that a cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + if (path === undefined || + path.slice(0, 3).toLowerCase() !== + resolvedDevice.toLowerCase() + '\\') { + path = resolvedDevice + '\\'; + } + } + + validateString(path, 'path'); + + // Skip empty entries + if (path.length === 0) { + continue; + } + + const len = path.length; + let rootEnd = 0; + let device = ''; + let isAbsolute = false; + const code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + // If we started with a separator, we know we at least have an + // absolute path of some kind (UNC or otherwise) + isAbsolute = true; + + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + const firstPart = path.slice(last, j); + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j === len) { + // We matched a UNC root only + + device = '\\\\' + firstPart + '\\' + path.slice(last); + rootEnd = j; + } else if (j !== last) { + // We matched a UNC root with leftovers + + device = '\\\\' + firstPart + '\\' + path.slice(last, j); + rootEnd = j; + } + } + } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + device = path.slice(0, 2); + rootEnd = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) { + // Treat separator following drive name as an absolute path + // indicator + isAbsolute = true; + rootEnd = 3; + } + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator + rootEnd = 1; + isAbsolute = true; + } + + if (device.length > 0 && + resolvedDevice.length > 0 && + device.toLowerCase() !== resolvedDevice.toLowerCase()) { + // This path points to another device so it is not applicable + continue; + } + + if (resolvedDevice.length === 0 && device.length > 0) { + resolvedDevice = device; + } + if (!resolvedAbsolute) { + resolvedTail = path.slice(rootEnd) + '\\' + resolvedTail; + resolvedAbsolute = isAbsolute; + } + + if (resolvedDevice.length > 0 && resolvedAbsolute) { + break; + } + } + + // At this point the path should be resolved to a full absolute path, + // but handle relative paths to be safe (might happen when process.cwd() + // fails) + + // Normalize the tail path + resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', + isPathSeparator); + + return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || + '.'; + }, + + normalize(path: string): string { + validateString(path, 'path'); + const len = path.length; + if (len === 0) { + return '.'; + } + let rootEnd = 0; + let device; + let isAbsolute = false; + const code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + // If we started with a separator, we know we at least have an absolute + // path of some kind (UNC or otherwise) + isAbsolute = true; + + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + const firstPart = path.slice(last, j); + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j === len) { + // We matched a UNC root only + // Return the normalized version of the UNC root since there + // is nothing left to process + + return '\\\\' + firstPart + '\\' + path.slice(last) + '\\'; + } else if (j !== last) { + // We matched a UNC root with leftovers + + device = '\\\\' + firstPart + '\\' + path.slice(last, j); + rootEnd = j; + } + } + } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + device = path.slice(0, 2); + rootEnd = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) { + // Treat separator following drive name as an absolute path + // indicator + isAbsolute = true; + rootEnd = 3; + } + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator, exit early to avoid unnecessary + // work + return '\\'; + } + + let tail; + if (rootEnd < len) { + tail = normalizeString(path.slice(rootEnd), !isAbsolute, '\\', + isPathSeparator); + } else { + tail = ''; + } + if (tail.length === 0 && !isAbsolute) { + tail = '.'; + } + if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) { + tail += '\\'; + } + if (device === undefined) { + if (isAbsolute) { + if (tail.length > 0) { + return '\\' + tail; + } + else { + return '\\'; + } + } else if (tail.length > 0) { + return tail; + } else { + return ''; + } + } else if (isAbsolute) { + if (tail.length > 0) { + return device + '\\' + tail; + } + else { + return device + '\\'; + } + } else if (tail.length > 0) { + return device + tail; + } else { + return device; + } + }, + + isAbsolute(path: string): boolean { + validateString(path, 'path'); + const len = path.length; + if (len === 0) { + return false; + } + + const code = path.charCodeAt(0); + if (isPathSeparator(code)) { + return true; + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (len > 2 && path.charCodeAt(1) === CHAR_COLON) { + if (isPathSeparator(path.charCodeAt(2))) { + return true; + } + } + } + return false; + }, + + join(...paths: string[]): string { + if (paths.length === 0) { + return '.'; + } + + let joined; + let firstPart: string | undefined; + for (let i = 0; i < paths.length; ++i) { + const arg = paths[i]; + validateString(arg, 'path'); + if (arg.length > 0) { + if (joined === undefined) { + joined = firstPart = arg; + } + else { + joined += '\\' + arg; + } + } + } + + if (joined === undefined) { + return '.'; + } + + // Make sure that the joined path doesn't start with two slashes, because + // normalize() will mistake it for an UNC path then. + // + // This step is skipped when it is very clear that the user actually + // intended to point at an UNC path. This is assumed when the first + // non-empty string arguments starts with exactly two slashes followed by + // at least one more non-slash character. + // + // Note that for normalize() to treat a path as an UNC path it needs to + // have at least 2 components, so we don't filter for that here. + // This means that the user can use join to construct UNC paths from + // a server name and a share name; for example: + // path.join('//server', 'share') -> '\\\\server\\share\\') + let needsReplace = true; + let slashCount = 0; + if (typeof firstPart === 'string' && isPathSeparator(firstPart.charCodeAt(0))) { + ++slashCount; + const firstLen = firstPart.length; + if (firstLen > 1) { + if (isPathSeparator(firstPart.charCodeAt(1))) { + ++slashCount; + if (firstLen > 2) { + if (isPathSeparator(firstPart.charCodeAt(2))) { + ++slashCount; + } + else { + // We matched a UNC path in the first part + needsReplace = false; + } + } + } + } + } + if (needsReplace) { + // Find any more consecutive slashes we need to replace + for (; slashCount < joined.length; ++slashCount) { + if (!isPathSeparator(joined.charCodeAt(slashCount))) { + break; + } + } + + // Replace the slashes if needed + if (slashCount >= 2) { + joined = '\\' + joined.slice(slashCount); + } + } + + return win32.normalize(joined); + }, + + + // It will solve the relative path from `from` to `to`, for instance: + // from = 'C:\\orandea\\test\\aaa' + // to = 'C:\\orandea\\impl\\bbb' + // The output of the function should be: '..\\..\\impl\\bbb' + relative(from: string, to: string): string { + validateString(from, 'from'); + validateString(to, 'to'); + + if (from === to) { + return ''; + } + + const fromOrig = win32.resolve(from); + const toOrig = win32.resolve(to); + + if (fromOrig === toOrig) { + return ''; + } + + from = fromOrig.toLowerCase(); + to = toOrig.toLowerCase(); + + if (from === to) { + return ''; + } + + // Trim any leading backslashes + let fromStart = 0; + for (; fromStart < from.length; ++fromStart) { + if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) { + break; + } + } + // Trim trailing backslashes (applicable to UNC paths only) + let fromEnd = from.length; + for (; fromEnd - 1 > fromStart; --fromEnd) { + if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) { + break; + } + } + const fromLen = (fromEnd - fromStart); + + // Trim any leading backslashes + let toStart = 0; + for (; toStart < to.length; ++toStart) { + if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) { + break; + } + } + // Trim trailing backslashes (applicable to UNC paths only) + let toEnd = to.length; + for (; toEnd - 1 > toStart; --toEnd) { + if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) { + break; + } + } + const toLen = (toEnd - toStart); + + // Compare paths to find the longest common path from root + const length = (fromLen < toLen ? fromLen : toLen); + let lastCommonSep = -1; + let i = 0; + for (; i <= length; ++i) { + if (i === length) { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' + return toOrig.slice(toStart + i + 1); + } else if (i === 2) { + // We get here if `from` is the device root. + // For example: from='C:\\'; to='C:\\foo' + return toOrig.slice(toStart + i); + } + } + if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='C:\\foo\\bar'; to='C:\\foo' + lastCommonSep = i; + } else if (i === 2) { + // We get here if `to` is the device root. + // For example: from='C:\\foo\\bar'; to='C:\\' + lastCommonSep = 3; + } + } + break; + } + const fromCode = from.charCodeAt(fromStart + i); + const toCode = to.charCodeAt(toStart + i); + if (fromCode !== toCode) { + break; + } + else if (fromCode === CHAR_BACKWARD_SLASH) { + lastCommonSep = i; + } + } + + // We found a mismatch before the first common path separator was seen, so + // return the original `to`. + if (i !== length && lastCommonSep === -1) { + return toOrig; + } + + let out = ''; + if (lastCommonSep === -1) { + lastCommonSep = 0; + } + // Generate the relative path based on the path difference between `to` and + // `from` + for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { + if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { + if (out.length === 0) { + out += '..'; + } + else { + out += '\\..'; + } + } + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts + if (out.length > 0) { + return out + toOrig.slice(toStart + lastCommonSep, toEnd); + } + else { + toStart += lastCommonSep; + if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { + ++toStart; + } + return toOrig.slice(toStart, toEnd); + } + }, + + toNamespacedPath(path: string): string { + // Note: this will *probably* throw somewhere. + if (typeof path !== 'string') { + return path; + } + + if (path.length === 0) { + return ''; + } + + const resolvedPath = win32.resolve(path); + + if (resolvedPath.length >= 3) { + if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { + // Possible UNC root + + if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { + const code = resolvedPath.charCodeAt(2); + if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { + // Matched non-long UNC root, convert the path to a long UNC path + return '\\\\?\\UNC\\' + resolvedPath.slice(2); + } + } + } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) { + // Possible device root + + if (resolvedPath.charCodeAt(1) === CHAR_COLON && + resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH) { + // Matched device root, convert the path to a long UNC path + return '\\\\?\\' + resolvedPath; + } + } + } + + return path; + }, + + dirname(path: string): string { + validateString(path, 'path'); + const len = path.length; + if (len === 0) { + return '.'; + } + let rootEnd = -1; + let end = -1; + let matchedSlash = true; + let offset = 0; + const code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + rootEnd = offset = 1; + + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j === len) { + // We matched a UNC root only + return path; + } + if (j !== last) { + // We matched a UNC root with leftovers + + // Offset by 1 to include the separator after the UNC root to + // treat it as a "normal root" on top of a (UNC) root + rootEnd = offset = j + 1; + } + } + } + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + rootEnd = offset = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) { + rootEnd = offset = 3; + } + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator, exit early to avoid + // unnecessary work + return path; + } + + for (let i = len - 1; i >= offset; --i) { + if (isPathSeparator(path.charCodeAt(i))) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } + + if (end === -1) { + if (rootEnd === -1) { + return '.'; + } + else { + end = rootEnd; + } + } + return path.slice(0, end); + }, + + basename(path: string, ext?: string): string { + if (ext !== undefined) { + validateString(ext, 'ext'); + } + validateString(path, 'path'); + let start = 0; + let end = -1; + let matchedSlash = true; + let i; + + // Check for a drive letter prefix so as not to mistake the following + // path separator as an extra separator at the end of the path that can be + // disregarded + if (path.length >= 2) { + const drive = path.charCodeAt(0); + if (isWindowsDeviceRoot(drive)) { + if (path.charCodeAt(1) === CHAR_COLON) { + start = 2; + } + } + } + + if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { + if (ext.length === path.length && ext === path) { + return ''; + } + let extIdx = ext.length - 1; + let firstNonSlashEnd = -1; + for (i = path.length - 1; i >= start; --i) { + const code = path.charCodeAt(i); + if (isPathSeparator(code)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd === -1) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx >= 0) { + // Try to match the explicit extension + if (code === ext.charCodeAt(extIdx)) { + if (--extIdx === -1) { + // We matched the extension, so mark this as the end of our path + // component + end = i; + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = -1; + end = firstNonSlashEnd; + } + } + } + } + + if (start === end) { + end = firstNonSlashEnd; + } + else if (end === -1) { + end = path.length; + } + return path.slice(start, end); + } else { + for (i = path.length - 1; i >= start; --i) { + if (isPathSeparator(path.charCodeAt(i))) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + if (end === -1) { + return ''; + } + return path.slice(start, end); + } + }, + + extname(path: string): string { + validateString(path, 'path'); + let start = 0; + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + + // Check for a drive letter prefix so as not to mistake the following + // path separator as an extra separator at the end of the path that can be + // disregarded + + if (path.length >= 2 && + path.charCodeAt(1) === CHAR_COLON && + isWindowsDeviceRoot(path.charCodeAt(0))) { + start = startPart = 2; + } + + for (let i = path.length - 1; i >= start; --i) { + const code = path.charCodeAt(i); + if (isPathSeparator(code)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) { + startDot = i; + } + else if (preDotState !== 1) { + preDotState = 1; + } + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if (startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { + return ''; + } + return path.slice(startDot, end); + }, + + format(pathObject): string { + if (pathObject === null || typeof pathObject !== 'object') { + throw new ErrorInvalidArgType('pathObject', 'Object', pathObject); + } + + return _format('\\', pathObject); + }, + + + parse(path) { + validateString(path, 'path'); + + const ret = { root: '', dir: '', base: '', ext: '', name: '' }; + if (path.length === 0) { + return ret; + } + + const len = path.length; + let rootEnd = 0; + let code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + rootEnd = 1; + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) { + break; + } + } + if (j === len) { + // We matched a UNC root only + + rootEnd = j; + } else if (j !== last) { + // We matched a UNC root with leftovers + + rootEnd = j + 1; + } + } + } + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + rootEnd = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) { + if (len === 3) { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + rootEnd = 3; + } + } else { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + + if (rootEnd > 0) { + ret.root = path.slice(0, rootEnd); + } + + let startDot = -1; + let startPart = rootEnd; + let end = -1; + let matchedSlash = true; + let i = path.length - 1; + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + + // Get non-dir info + for (; i >= rootEnd; --i) { + code = path.charCodeAt(i); + if (isPathSeparator(code)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) { + startDot = i; + } + else if (preDotState !== 1) { + preDotState = 1; + } + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if (startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { + if (end !== -1) { + ret.base = ret.name = path.slice(startPart, end); + } + } else { + ret.name = path.slice(startPart, startDot); + ret.base = path.slice(startPart, end); + ret.ext = path.slice(startDot, end); + } + + // If the directory is the root, use the entire root as the `dir` including + // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the + // trailing slash (`C:\abc\def` -> `C:\abc`). + if (startPart > 0 && startPart !== rootEnd) { + ret.dir = path.slice(0, startPart - 1); + } + else { + ret.dir = ret.root; + } + + return ret; + }, + + sep: '\\', + delimiter: ';', + win32: null, + posix: null +}; + +export const posix: IPath = { + // path.resolve([from ...], to) + resolve(...pathSegments: string[]): string { + let resolvedPath = ''; + let resolvedAbsolute = false; + + for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + let path; + if (i >= 0) { + path = pathSegments[i]; + } + else { + path = process.cwd(); + } + + validateString(path, 'path'); + + // Skip empty entries + if (path.length === 0) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', + isPosixPathSeparator); + + if (resolvedAbsolute) { + if (resolvedPath.length > 0) { + return '/' + resolvedPath; + } + else { + return '/'; + } + } else if (resolvedPath.length > 0) { + return resolvedPath; + } else { + return '.'; + } + }, + + normalize(path: string): string { + validateString(path, 'path'); + + if (path.length === 0) { + return '.'; + } + + const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + const trailingSeparator = + path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH; + + // Normalize the path + path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator); + + if (path.length === 0 && !isAbsolute) { + path = '.'; + } + if (path.length > 0 && trailingSeparator) { + path += '/'; + } + + if (isAbsolute) { + return '/' + path; + } + return path; + }, + + isAbsolute(path: string): boolean { + validateString(path, 'path'); + return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; + }, + + join(...paths: string[]): string { + if (paths.length === 0) { + return '.'; + } + let joined; + for (let i = 0; i < paths.length; ++i) { + const arg = arguments[i]; + validateString(arg, 'path'); + if (arg.length > 0) { + if (joined === undefined) { + joined = arg; + } + else { + joined += '/' + arg; + } + } + } + if (joined === undefined) { + return '.'; + } + return posix.normalize(joined); + }, + + relative(from: string, to: string): string { + validateString(from, 'from'); + validateString(to, 'to'); + + if (from === to) { + return ''; + } + + from = posix.resolve(from); + to = posix.resolve(to); + + if (from === to) { + return ''; + } + + // Trim any leading backslashes + let fromStart = 1; + for (; fromStart < from.length; ++fromStart) { + if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) { + break; + } + } + const fromEnd = from.length; + const fromLen = (fromEnd - fromStart); + + // Trim any leading backslashes + let toStart = 1; + for (; toStart < to.length; ++toStart) { + if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) { + break; + } + } + const toEnd = to.length; + const toLen = (toEnd - toStart); + + // Compare paths to find the longest common path from root + const length = (fromLen < toLen ? fromLen : toLen); + let lastCommonSep = -1; + let i = 0; + for (; i <= length; ++i) { + if (i === length) { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='/foo/bar'; to='/foo/bar/baz' + return to.slice(toStart + i + 1); + } else if (i === 0) { + // We get here if `from` is the root + // For example: from='/'; to='/foo' + return to.slice(toStart + i); + } + } else if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='/foo/bar/baz'; to='/foo/bar' + lastCommonSep = i; + } else if (i === 0) { + // We get here if `to` is the root. + // For example: from='/foo'; to='/' + lastCommonSep = 0; + } + } + break; + } + const fromCode = from.charCodeAt(fromStart + i); + const toCode = to.charCodeAt(toStart + i); + if (fromCode !== toCode) { + break; + } + else if (fromCode === CHAR_FORWARD_SLASH) { + lastCommonSep = i; + } + } + + let out = ''; + // Generate the relative path based on the path difference between `to` + // and `from` + for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { + if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { + if (out.length === 0) { + out += '..'; + } + else { + out += '/..'; + } + } + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts + if (out.length > 0) { + return out + to.slice(toStart + lastCommonSep); + } + else { + toStart += lastCommonSep; + if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) { + ++toStart; + } + return to.slice(toStart); + } + }, + + toNamespacedPath(path: string): string { + // Non-op on posix systems + return path; + }, + + dirname(path: string): string { + validateString(path, 'path'); + if (path.length === 0) { + return '.'; + } + const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + let end = -1; + let matchedSlash = true; + for (let i = path.length - 1; i >= 1; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } + + if (end === -1) { + return hasRoot ? '/' : '.'; + } + if (hasRoot && end === 1) { + return '//'; + } + return path.slice(0, end); + }, + + basename(path: string, ext?: string): string { + if (ext !== undefined) { + validateString(ext, 'ext'); + } + validateString(path, 'path'); + + let start = 0; + let end = -1; + let matchedSlash = true; + let i; + + if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { + if (ext.length === path.length && ext === path) { + return ''; + } + let extIdx = ext.length - 1; + let firstNonSlashEnd = -1; + for (i = path.length - 1; i >= 0; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd === -1) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx >= 0) { + // Try to match the explicit extension + if (code === ext.charCodeAt(extIdx)) { + if (--extIdx === -1) { + // We matched the extension, so mark this as the end of our path + // component + end = i; + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = -1; + end = firstNonSlashEnd; + } + } + } + } + + if (start === end) { + end = firstNonSlashEnd; + } + else if (end === -1) { + end = path.length; + } + return path.slice(start, end); + } else { + for (i = path.length - 1; i >= 0; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + if (end === -1) { + return ''; + } + return path.slice(start, end); + } + }, + + extname(path: string): string { + validateString(path, 'path'); + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + for (let i = path.length - 1; i >= 0; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) { + startDot = i; + } + else if (preDotState !== 1) { + preDotState = 1; + } + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if (startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { + return ''; + } + return path.slice(startDot, end); + }, + + format(pathObject): string { + if (pathObject === null || typeof pathObject !== 'object') { + throw new ErrorInvalidArgType('pathObject', 'Object', pathObject); + } + + return _format('/', pathObject); + }, + + parse(path: string): ParsedPath { + validateString(path, 'path'); + + const ret = { root: '', dir: '', base: '', ext: '', name: '' }; + if (path.length === 0) { + return ret; + } + const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + let start; + if (isAbsolute) { + ret.root = '/'; + start = 1; + } else { + start = 0; + } + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + let i = path.length - 1; + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + + // Get non-dir info + for (; i >= start; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) { + startDot = i; + } + else if (preDotState !== 1) { + preDotState = 1; + } + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if (startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { + if (end !== -1) { + if (startPart === 0 && isAbsolute) { + ret.base = ret.name = path.slice(1, end); + } + else { + ret.base = ret.name = path.slice(startPart, end); + } + } + } else { + if (startPart === 0 && isAbsolute) { + ret.name = path.slice(1, startDot); + ret.base = path.slice(1, end); + } else { + ret.name = path.slice(startPart, startDot); + ret.base = path.slice(startPart, end); + } + ret.ext = path.slice(startDot, end); + } + + if (startPart > 0) { + ret.dir = path.slice(0, startPart - 1); + } + else if (isAbsolute) { + ret.dir = '/'; + } + + return ret; + }, + + sep: '/', + delimiter: ':', + win32: null, + posix: null +}; + +posix.win32 = win32.win32 = win32; +posix.posix = win32.posix = posix; + +export const normalize = (process.platform === 'win32' ? win32.normalize : posix.normalize); +export const isAbsolute = (process.platform === 'win32' ? win32.isAbsolute : posix.isAbsolute); +export const join = (process.platform === 'win32' ? win32.join : posix.join); +export const resolve = (process.platform === 'win32' ? win32.resolve : posix.resolve); +export const relative = (process.platform === 'win32' ? win32.relative : posix.relative); +export const dirname = (process.platform === 'win32' ? win32.dirname : posix.dirname); +export const basename = (process.platform === 'win32' ? win32.basename : posix.basename); +export const extname = (process.platform === 'win32' ? win32.extname : posix.extname); +export const format = (process.platform === 'win32' ? win32.format : posix.format); +export const parse = (process.platform === 'win32' ? win32.parse : posix.parse); +export const toNamespacedPath = (process.platform === 'win32' ? win32.toNamespacedPath : posix.toNamespacedPath); +export const sep = (process.platform === 'win32' ? win32.sep : posix.sep); +export const delimiter = (process.platform === 'win32' ? win32.delimiter : posix.delimiter); diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts deleted file mode 100644 index 1255aed38cf..00000000000 --- a/src/vs/base/common/paths.ts +++ /dev/null @@ -1,405 +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 { isWindows } from 'vs/base/common/platform'; -import { startsWithIgnoreCase, equalsIgnoreCase } from 'vs/base/common/strings'; -import { CharCode } from 'vs/base/common/charCode'; - -/** - * The forward slash path separator. - */ -export const sep = '/'; - -/** - * The native path separator depending on the OS. - */ -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, separator = nativeSep): string { - const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); - if (idx === 0) { - return '.'; - } else if (~idx === 0) { - return path[0]; - } else if (~idx === path.length - 1) { - return dirname(path.substring(0, path.length - 1)); - } else { - let res = path.substring(0, ~idx); - if (isWindows && res[res.length - 1] === ':') { - res += separator; // make sure drive letters end with backslash - } - return res; - } -} - -/** - * @returns the base name of a path. - */ -export function basename(path: string): string { - const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); - if (idx === 0) { - return path; - } else if (~idx === path.length - 1) { - return basename(path.substring(0, path.length - 1)); - } else { - return path.substr(~idx + 1); - } -} - -/** - * @returns `.far` from `boo.far` or the empty string. - */ -export function extname(path: string): string { - path = basename(path); - const idx = ~path.lastIndexOf('.'); - return idx ? path.substring(~idx) : ''; -} - -const _posixBadPath = /(\/\.\.?\/)|(\/\.\.?)$|^(\.\.?\/)|(\/\/+)|(\\)/; -const _winBadPath = /(\\\.\.?\\)|(\\\.\.?)$|^(\.\.?\\)|(\\\\+)|(\/)/; - -function _isNormal(path: string, win: boolean): boolean { - return win - ? !_winBadPath.test(path) - : !_posixBadPath.test(path); -} - -export function normalize(path: undefined, toOSPath?: boolean): undefined; -export function normalize(path: null, toOSPath?: boolean): null; -export function normalize(path: string, toOSPath?: boolean): string; -export function normalize(path: string | null | undefined, toOSPath?: boolean): string | null | undefined { - - if (path === null || path === void 0) { - return path; - } - - const len = path.length; - if (len === 0) { - return '.'; - } - - const wantsBackslash = !!(isWindows && toOSPath); - if (_isNormal(path, wantsBackslash)) { - return path; - } - - const sep = wantsBackslash ? '\\' : '/'; - const root = getRoot(path, sep); - - // skip the root-portion of the path - let start = root.length; - let skip = false; - let res = ''; - - for (let end = root.length; end <= len; end++) { - - // either at the end or at a path-separator character - if (end === len || path.charCodeAt(end) === CharCode.Slash || path.charCodeAt(end) === CharCode.Backslash) { - - if (streql(path, start, end, '..')) { - // skip current and remove parent (if there is already something) - let prev_start = res.lastIndexOf(sep); - let prev_part = res.slice(prev_start + 1); - if ((root || prev_part.length > 0) && prev_part !== '..') { - res = prev_start === -1 ? '' : res.slice(0, prev_start); - skip = true; - } - } else if (streql(path, start, end, '.') && (root || res || end < len - 1)) { - // skip current (if there is already something or if there is more to come) - skip = true; - } - - if (!skip) { - let part = path.slice(start, end); - if (res !== '' && res[res.length - 1] !== sep) { - res += sep; - } - res += part; - } - start = end + 1; - skip = false; - } - } - - return root + res; -} - -function streql(value: string, start: number, end: number, other: string): boolean { - return start + other.length === end && value.indexOf(other, start) === start; -} - -/** - * Computes the _root_ this path, like `getRoot('c:\files') === c:\`, - * `getRoot('files:///files/path') === files:///`, - * or `getRoot('\\server\shares\path') === \\server\shares\` - */ -export function getRoot(path: string, sep: string = '/'): string { - - if (!path) { - return ''; - } - - let len = path.length; - let code = path.charCodeAt(0); - if (code === CharCode.Slash || code === CharCode.Backslash) { - - code = path.charCodeAt(1); - if (code === CharCode.Slash || code === CharCode.Backslash) { - // UNC candidate \\localhost\shares\ddd - // ^^^^^^^^^^^^^^^^^^^ - code = path.charCodeAt(2); - if (code !== CharCode.Slash && code !== CharCode.Backslash) { - let pos = 3; - let start = pos; - for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { - break; - } - } - code = path.charCodeAt(pos + 1); - if (start !== pos && code !== CharCode.Slash && code !== CharCode.Backslash) { - pos += 1; - for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { - return path.slice(0, pos + 1) // consume this separator - .replace(/[\\/]/g, sep); - } - } - } - } - } - - // /user/far - // ^ - return sep; - - } else if ((code >= CharCode.A && code <= CharCode.Z) || (code >= CharCode.a && code <= CharCode.z)) { - // check for windows drive letter c:\ or c: - - if (path.charCodeAt(1) === CharCode.Colon) { - code = path.charCodeAt(2); - if (code === CharCode.Slash || code === CharCode.Backslash) { - // C:\fff - // ^^^ - return path.slice(0, 2) + sep; - } else { - // C: - // ^^ - return path.slice(0, 2); - } - } - } - - // check for URI - // scheme://authority/path - // ^^^^^^^^^^^^^^^^^^^ - let pos = path.indexOf('://'); - if (pos !== -1) { - pos += 3; // 3 -> "://".length - for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { - return path.slice(0, pos + 1); // consume this separator - } - } - } - - return ''; -} - -export const join: (...parts: string[]) => string = function () { - // Not using a function with var-args because of how TS compiles - // them to JS - it would result in 2*n runtime cost instead - // of 1*n, where n is parts.length. - - let value = ''; - for (let i = 0; i < arguments.length; i++) { - let part = arguments[i]; - if (i > 0) { - // add the separater between two parts unless - // there already is one - let last = value.charCodeAt(value.length - 1); - if (last !== CharCode.Slash && last !== CharCode.Backslash) { - let next = part.charCodeAt(0); - if (next !== CharCode.Slash && next !== CharCode.Backslash) { - - value += sep; - } - } - } - value += part; - } - - return normalize(value); -}; - - -/** - * Check if the path follows this pattern: `\\hostname\sharename`. - * - * @see https://msdn.microsoft.com/en-us/library/gg465305.aspx - * @return A boolean indication if the path is a UNC path, on none-windows - * always false. - */ -export function isUNC(path: string): boolean { - if (!isWindows) { - // UNC is a windows concept - return false; - } - - if (!path || path.length < 5) { - // at least \\a\b - return false; - } - - let code = path.charCodeAt(0); - if (code !== CharCode.Backslash) { - return false; - } - code = path.charCodeAt(1); - if (code !== CharCode.Backslash) { - return false; - } - let pos = 2; - let start = pos; - for (; pos < path.length; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Backslash) { - break; - } - } - if (start === pos) { - return false; - } - code = path.charCodeAt(pos + 1); - if (isNaN(code) || code === CharCode.Backslash) { - return false; - } - return true; -} - -// Reference: https://en.wikipedia.org/wiki/Filename -const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g; -const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; -export function isValidBasename(name: string | null | undefined): boolean { - if (!name || name.length === 0 || /^\s+$/.test(name)) { - return false; // require a name that is not just whitespace - } - - INVALID_FILE_CHARS.lastIndex = 0; // the holy grail of software development - if (INVALID_FILE_CHARS.test(name)) { - return false; // check for certain invalid file characters - } - - if (isWindows && WINDOWS_FORBIDDEN_NAMES.test(name)) { - return false; // check for certain invalid file names - } - - if (name === '.' || name === '..') { - return false; // check for reserved values - } - - if (isWindows && name[name.length - 1] === '.') { - return false; // Windows: file cannot end with a "." - } - - if (isWindows && name.length !== name.trim().length) { - return false; // Windows: file cannot end with a whitespace - } - - return true; -} - -export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boolean { - const identityEquals = (pathA === pathB); - if (!ignoreCase || identityEquals) { - return identityEquals; - } - - if (!pathA || !pathB) { - return false; - } - - return equalsIgnoreCase(pathA, pathB); -} - -export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean, separator = nativeSep): boolean { - if (path === candidate) { - return true; - } - - if (!path || !candidate) { - return false; - } - - if (candidate.length > path.length) { - return false; - } - - if (ignoreCase) { - const beginsWith = startsWithIgnoreCase(path, candidate); - if (!beginsWith) { - return false; - } - - if (candidate.length === path.length) { - return true; // same path, different casing - } - - let sepOffset = candidate.length; - 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) === separator; - } - - if (candidate.charAt(candidate.length - 1) !== separator) { - candidate += separator; - } - - return path.indexOf(candidate) === 0; -} - -/** - * Adapted from Node's path.isAbsolute functions - */ -export function isAbsolute(path: string): boolean { - return isWindows ? - isAbsolute_win32(path) : - isAbsolute_posix(path); -} - -export function isAbsolute_win32(path: string): boolean { - if (!path) { - return false; - } - - const char0 = path.charCodeAt(0); - if (char0 === CharCode.Slash || char0 === CharCode.Backslash) { - return true; - } else if ((char0 >= CharCode.A && char0 <= CharCode.Z) || (char0 >= CharCode.a && char0 <= CharCode.z)) { - if (path.length > 2 && path.charCodeAt(1) === CharCode.Colon) { - const char2 = path.charCodeAt(2); - if (char2 === CharCode.Slash || char2 === CharCode.Backslash) { - return true; - } - } - } - - return false; -} - -export function isAbsolute_posix(path: string): boolean { - return !!(path && path.charCodeAt(0) === CharCode.Slash); -} diff --git a/src/vs/base/common/performance.d.ts b/src/vs/base/common/performance.d.ts index dfdb1251598..657f250d8b3 100644 --- a/src/vs/base/common/performance.d.ts +++ b/src/vs/base/common/performance.d.ts @@ -4,22 +4,18 @@ *--------------------------------------------------------------------------------------------*/ export interface PerformanceEntry { - readonly type: 'mark' | 'measure'; readonly name: string; - readonly startTime: number; - readonly duration: number; + readonly timestamp: number; } export function mark(name: string): void; -export function measure(name: string, from?: string, to?: string): PerformanceEntry; - /** * All entries filtered by type and sorted by `startTime`. */ -export function getEntries(type: 'mark' | 'measure'): PerformanceEntry[]; +export function getEntries(): PerformanceEntry[]; -export function getEntry(type: 'mark' | 'measure', name: string): PerformanceEntry; +export function getEntry(name: string): PerformanceEntry; export function getDuration(from: string, to: string): number; diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index 5590211c6d5..2bc77108620 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -5,137 +5,73 @@ 'use strict'; -/*global define*/ +//@ts-check -// This module can be loaded in an amd and commonjs-context. -// Because we want both instances to use the same perf-data -// we store them globally -// stores data as 'type','name','startTime','duration' +function _factory(sharedObj) { -if (typeof define !== "function" && typeof module === "object" && typeof module.exports === "object") { - // this is commonjs, fake amd - global.define = function (dep, callback) { - module.exports = callback(); - global.define = undefined; - }; -} + sharedObj._performanceEntries = sharedObj._performanceEntries || []; -define([], function () { - - var _global = this; - if (typeof global !== 'undefined') { - _global = global; - } - _global._performanceEntries = _global._performanceEntries || []; - - // const _now = global.performance && performance.now ? performance.now : Date.now - const _now = Date.now; + const _dataLen = 2; + const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; function importEntries(entries) { - global._performanceEntries.splice(0, 0, ...entries); + sharedObj._performanceEntries.splice(0, 0, ...entries); } function exportEntries() { - return global._performanceEntries.slice(0); + return sharedObj._performanceEntries.slice(0); } - function getEntries(type, name) { + function getEntries() { const result = []; - const entries = global._performanceEntries; - 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], - }); - } + const entries = sharedObj._performanceEntries; + for (let i = 0; i < entries.length; i += _dataLen) { + result.push({ + name: entries[i], + timestamp: entries[i + 1], + }); } - - return result.sort((a, b) => { - return a.startTime - b.startTime || a.seq - b.seq; - }); + return result; } - function getEntry(type, name) { - const entries = global._performanceEntries; - for (let i = 0; i < entries.length; i += 5) { - if (entries[i] === type && entries[i + 1] === name) { + function getEntry(name) { + const entries = sharedObj._performanceEntries; + for (let i = 0; i < entries.length; i += _dataLen) { + if (entries[i] === name) { return { - type: entries[i], - name: entries[i + 1], - startTime: entries[i + 2], - duration: entries[i + 3], - seq: entries[i + 4], + name: entries[i], + timestamp: entries[i + 1], }; } } } function getDuration(from, to) { - const entries = global._performanceEntries; + const entries = sharedObj._performanceEntries; let target = to; - let endTime = 0; - for (let i = entries.length - 1; i >= 0; i -= 5) { - if (entries[i - 3] === target) { + let endIndex = 0; + for (let i = entries.length - _dataLen; i >= 0; i -= _dataLen) { + if (entries[i] === target) { if (target === to) { // found `to` (end of interval) - endTime = entries[i - 2]; + endIndex = i; target = from; } else { - return endTime - entries[i - 2]; + // found `from` (start of interval) + return entries[endIndex + 1] - entries[i + 1]; } } } return 0; } - let seq = 0; - function mark(name) { - global._performanceEntries.push('mark', name, _now(), 0, seq++); - if (typeof console.timeStamp === 'function') { - console.timeStamp(name); - } + sharedObj._performanceEntries.push(name, Date.now()); + _timeStamp(name); } - function measure(name, from, to) { - - let startTime; - let duration; - let now = _now(); - - if (!from) { - startTime = now; - } else { - startTime = _getLastStartTime(from); - } - - if (!to) { - duration = now - startTime; - } else { - duration = _getLastStartTime(to) - startTime; - } - - global._performanceEntries.push('measure', name, startTime, duration); - } - - function _getLastStartTime(name) { - const entries = global._performanceEntries; - for (let i = entries.length - 1; i >= 0; i -= 5) { - if (entries[i - 3] === name) { - return entries[i - 2]; - } - } - - throw new Error(name + ' not found'); - } - - var exports = { + const exports = { mark: mark, - measure: measure, getEntries: getEntries, getEntry: getEntry, getDuration: getDuration, @@ -144,4 +80,29 @@ define([], function () { }; return exports; -}); +} + +// This module can be loaded in an amd and commonjs-context. +// Because we want both instances to use the same perf-data +// we store them globally + +let sharedObj; +if (typeof global === 'object') { + // nodejs + sharedObj = global; +} else if (typeof self === 'object') { + // browser + sharedObj = self; +} else { + sharedObj = {}; +} + +if (typeof define === 'function') { + // amd + define([], function () { return _factory(sharedObj); }); +} else if (typeof module === "object" && typeof module.exports === "object") { + // commonjs + module.exports = _factory(sharedObj); +} else { + // invalid context... +} diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 864c6bdc620..4cba839fe52 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -3,13 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +const LANGUAGE_DEFAULT = 'en'; + let _isWindows = false; let _isMacintosh = false; let _isLinux = false; let _isNative = false; let _isWeb = false; let _locale: string | undefined = undefined; -let _language: string | undefined = undefined; +let _language: string = LANGUAGE_DEFAULT; let _translationsConfigFile: string | undefined = undefined; interface NLSConfig { @@ -32,17 +34,15 @@ interface INodeProcess { }; type?: string; } -declare let process: INodeProcess; -declare let global: any; +declare const process: INodeProcess; +declare const global: any; interface INavigator { userAgent: string; language: string; } -declare let navigator: INavigator; -declare let self: any; - -export const LANGUAGE_DEFAULT = 'en'; +declare const navigator: INavigator; +declare const self: any; const isElectronRenderer = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.electron !== 'undefined' && process.type === 'renderer'); @@ -120,6 +120,27 @@ export function isRootUser(): boolean { */ export const language = _language; +export namespace Language { + + export function value(): string { + return language; + } + + export function isDefaultVariant(): boolean { + if (language.length === 2) { + return language === 'en'; + } else if (language.length >= 3) { + return language[0] === 'e' && language[1] === 'n' && language[2] === '-'; + } else { + return false; + } + } + + export function isDefault(): boolean { + return language === 'en'; + } +} + /** * The OS locale or the locale specified by --locale. The format of * the string is all lower case (e.g. zh-tw for Traditional @@ -155,14 +176,3 @@ export const enum OperatingSystem { Linux = 3 } export const OS = (_isMacintosh ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux)); - -export const enum AccessibilitySupport { - /** - * This should be the browser case where it is not known if a screen reader is attached or no. - */ - Unknown = 0, - - Disabled = 1, - - Enabled = 2 -} diff --git a/src/vs/base/common/process.ts b/src/vs/base/common/process.ts new file mode 100644 index 00000000000..a8447d58eea --- /dev/null +++ b/src/vs/base/common/process.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { isWindows, isMacintosh, setImmediate } from 'vs/base/common/platform'; + +interface IProcess { + platform: string; + env: object; + + cwd(): string; + nextTick(callback: (...args: any[]) => void): number; +} + +declare const process: IProcess; +const safeProcess: IProcess = (typeof process === 'undefined') ? { + cwd(): string { return '/'; }, + env: Object.create(null), + get platform(): string { return isWindows ? 'win32' : isMacintosh ? 'darwin' : 'linux'; }, + nextTick(callback: (...args: any[]) => void): number { return setImmediate(callback); } +} : process; + +export const cwd = safeProcess.cwd; +export const env = safeProcess.env; +export const platform = safeProcess.platform; +export const nextTick = safeProcess.nextTick; diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts index 7eef74d894f..c52f7b3774f 100644 --- a/src/vs/base/common/processes.ts +++ b/src/vs/base/common/processes.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IProcessEnvironment } from 'vs/base/common/platform'; + /** * Options to be passed to the external program or shell. */ @@ -84,3 +86,41 @@ export const enum TerminateResponseCode { AccessDenied = 2, ProcessNotFound = 3, } + +export interface ProcessItem { + name: string; + cmd: string; + pid: number; + ppid: number; + load: number; + mem: number; + + children?: ProcessItem[]; +} + +/** + * Sanitizes a VS Code process environment by removing all Electron/VS Code-related values. + */ +export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve: string[]): void { + const set = preserve.reduce((set, key) => { + set[key] = true; + return set; + }, {} as Record); + const keysToRemove = [ + /^ELECTRON_.+$/, + /^GOOGLE_API_KEY$/, + /^VSCODE_.+$/, + /^SNAP(|_.*)$/ + ]; + const envKeys = Object.keys(env); + envKeys + .filter(key => !set[key]) + .forEach(envKey => { + for (let i = 0; i < keysToRemove.length; i++) { + if (envKey.search(keysToRemove[i]) !== -1) { + delete env[envKey]; + break; + } + } + }); +} diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 05be7f7037a..521a02a4b57 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -3,12 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as paths from 'vs/base/common/paths'; +import * as extpath from 'vs/base/common/extpath'; +import * as paths from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { Schemas } from 'vs/base/common/network'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { CharCode } from 'vs/base/common/charCode'; +import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; +import { TernarySearchTree } from 'vs/base/common/map'; export function getComparisonKey(resource: URI): string { return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString(); @@ -32,22 +35,24 @@ export function basenameOrAuthority(resource: URI): string { 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); + return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), ignoreCase); } - if (isEqualAuthority(base.authority, parentCandidate.authority, ignoreCase)) { - return paths.isEqualOrParent(base.path, parentCandidate.path, ignoreCase, '/'); + if (isEqualAuthority(base.authority, parentCandidate.authority)) { + return extpath.isEqualOrParent(base.path, parentCandidate.path, ignoreCase, '/'); } } return false; } -function isEqualAuthority(a1: string, a2: string, ignoreCase?: boolean) { - return a1 === a2 || ignoreCase && a1 && a2 && equalsIgnoreCase(a1, a2); +/** + * Tests wheter the two authorities are the same + */ +export function isEqualAuthority(a1: string, a2: string) { + return a1 === a2 || equalsIgnoreCase(a1, a2); } export function isEqual(first: URI | undefined, second: URI | undefined, ignoreCase = hasToIgnoreCase(first)): boolean { - const identityEquals = (first === second); - if (identityEquals) { + if (first === second) { return true; } @@ -55,15 +60,20 @@ export function isEqual(first: URI | undefined, second: URI | undefined, ignoreC return false; } - if (ignoreCase) { - return equalsIgnoreCase(first.toString(), second.toString()); + if (first.scheme !== second.scheme || !isEqualAuthority(first.authority, second.authority)) { + return false; } - return first.toString() === second.toString(); + const p1 = first.path || '/', p2 = second.path || '/'; + return p1 === p2 || ignoreCase && equalsIgnoreCase(p1 || '/', p2 || '/'); } export function basename(resource: URI): string { - return paths.basename(resource.path); + return paths.posix.basename(resource.path); +} + +export function extname(resource: URI): string { + return paths.posix.extname(resource.path); } /** @@ -72,13 +82,17 @@ export function basename(resource: URI): string { * @param resource The input URI. * @returns The URI representing the directory of the input URI. */ -export function dirname(resource: URI): URI | null { - if (resource.scheme === Schemas.file) { - return URI.file(paths.dirname(fsPath(resource))); +export function dirname(resource: URI): URI { + if (resource.path.length === 0) { + return resource; } - let dirname = paths.dirname(resource.path, '/'); + if (resource.scheme === Schemas.file) { + return URI.file(paths.dirname(originalFSPath(resource))); + } + let dirname = paths.posix.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 + console.error(`dirname("${resource.toString})) resulted in a relative path`); + dirname = '/'; // 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 @@ -86,18 +100,18 @@ export function dirname(resource: URI): URI | null { } /** - * Join a URI path with a path fragment and normalizes the resulting path. + * Join a URI path with path fragments 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 { +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; + joinedPath = URI.file(paths.join(originalFSPath(resource), ...pathFragment)).path; } else { - joinedPath = paths.join(resource.path, pathFragment); + joinedPath = paths.posix.join(resource.path || '/', ...pathFragment); } return resource.with({ path: joinedPath @@ -111,11 +125,14 @@ export function joinPath(resource: URI, pathFragment: string): URI { * @returns The URI with the normalized path. */ export function normalizePath(resource: URI): URI { + if (!resource.path.length) { + return resource; + } let normalizedPath: string; if (resource.scheme === Schemas.file) { - normalizedPath = URI.file(paths.normalize(fsPath(resource))).path; + normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path; } else { - normalizedPath = paths.normalize(resource.path); + normalizedPath = paths.posix.normalize(resource.path); } return resource.with({ path: normalizedPath @@ -126,21 +143,22 @@ export function normalizePath(resource: URI): URI { * Returns the fsPath of an URI where the drive letter is not normalized. * See #56403. */ -export function fsPath(uri: URI): string { +export function originalFSPath(uri: URI): string { let value: string; - if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { + const uriPath = uri.path; + if (uri.authority && uriPath.length > 1 && uri.scheme === Schemas.file) { // unc path: file://shares/c$/far/boo - value = `//${uri.authority}${uri.path}`; + value = `//${uri.authority}${uriPath}`; } 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 + && uriPath.charCodeAt(0) === CharCode.Slash + && extpath.isWindowsDriveLetter(uriPath.charCodeAt(1)) + && uriPath.charCodeAt(2) === CharCode.Colon ) { - value = uri.path.substr(1); + value = uriPath.substr(1); } else { // other path - value = uri.path; + value = uriPath; } if (isWindows) { value = value.replace(/\//g, '\\'); @@ -152,7 +170,82 @@ export function fsPath(uri: URI): string { * Returns true if the URI path is absolute. */ export function isAbsolutePath(resource: URI): boolean { - return paths.isAbsolute(resource.path); + return !!resource.path && resource.path[0] === '/'; +} + +/** + * Returns true if the URI path has a trailing path separator + */ +export function hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean { + if (resource.scheme === Schemas.file) { + const fsp = originalFSPath(resource); + return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep; + } else { + const p = resource.path; + return p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; // ignore the slash at offset 0 + } +} + +/** + * Removes a trailing path separator, if there's one. + * Important: Doesn't remove the first slash, it would make the URI invalid + */ +export function removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + if (hasTrailingPathSeparator(resource, sep)) { + return resource.with({ path: resource.path.substr(0, resource.path.length - 1) }); + } + return resource; +} + +/** + * Adds a trailing path separator to the URI if there isn't one already. + * For example, c:\ would be unchanged, but c:\users would become c:\users\ + */ +export function addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI { + let isRootSep: boolean = false; + if (resource.scheme === Schemas.file) { + const fsp = originalFSPath(resource); + isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep)); + } else { + sep = '/'; + const p = resource.path; + isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash; + } + if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) { + return resource.with({ path: resource.path + '/' }); + } + return resource; +} + +/** + * Returns a relative path between two URIs. If the URIs don't have the same schema or authority, `undefined` is returned. + * The returned relative path always uses forward slashes. + */ +export function relativePath(from: URI, to: URI): string | undefined { + if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { + return undefined; + } + if (from.scheme === Schemas.file) { + const relativePath = paths.relative(from.path, to.path); + return isWindows ? extpath.toSlashes(relativePath) : relativePath; + } + return paths.posix.relative(from.path || '/', to.path || '/'); +} + +/** + * Resolves a absolute or relative path against a base URI. + */ +export function resolvePath(base: URI, path: string): URI { + if (base.scheme === Schemas.file) { + const newURI = URI.file(paths.resolve(originalFSPath(base), path)); + return base.with({ + authority: newURI.authority, + path: newURI.path + }); + } + return base.with({ + path: paths.posix.resolve(base.path, path) + }); } export function distinctParents(items: T[], resourceAccessor: (item: T) => URI): T[] { @@ -175,21 +268,6 @@ export function distinctParents(items: T[], resourceAccessor: (item: T) => UR return distinctParents; } -/** - * Tests whether the given URL is a file URI created by `URI.parse` instead of `URI.file`. - * Such URI have no scheme or scheme that consist of a single letter (windows drive letter) - * @param candidate The URI to test - * @returns A corrected, real file URI if the input seems to be malformed. - * 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; -} - - /** * Data URI related helpers. */ @@ -223,3 +301,43 @@ export namespace DataUri { return metadata; } } + +export class ResourceGlobMatcher { + + private readonly globalExpression: ParsedExpression; + private readonly expressionsByRoot: TernarySearchTree<{ root: URI, expression: ParsedExpression }> = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>(); + + constructor( + globalExpression: IExpression, + rootExpressions: { root: URI, expression: IExpression }[] + ) { + this.globalExpression = parse(globalExpression); + for (const expression of rootExpressions) { + this.expressionsByRoot.set(expression.root.toString(), { root: expression.root, expression: parse(expression.expression) }); + } + } + + matches(resource: URI): boolean { + const rootExpression = this.expressionsByRoot.findSubstr(resource.toString()); + if (rootExpression) { + const path = relativePath(rootExpression.root, resource); + if (path && !!rootExpression.expression(path)) { + return true; + } + } + return !!this.globalExpression(resource.path); + } +} + +export function toLocalResource(resource: URI, authority: string | undefined): URI { + if (authority) { + let path = resource.path; + if (path && path[0] !== paths.posix.sep) { + path = paths.posix.sep + path; + } + + return resource.with({ scheme: Schemas.vscodeRemote, authority, path }); + } + + return resource.with({ scheme: Schemas.file }); +} \ No newline at end of file diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index f33897a9f3b..faee10bed56 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -117,13 +117,13 @@ export class ScrollState implements IScrollDimensions, IScrollPosition { } public createScrollEvent(previous: ScrollState): ScrollEvent { - let widthChanged = (this.width !== previous.width); - let scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth); - let scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft); + const widthChanged = (this.width !== previous.width); + const scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth); + const scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft); - let heightChanged = (this.height !== previous.height); - let scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight); - let scrollTopChanged = (this.scrollTop !== previous.scrollTop); + const heightChanged = (this.height !== previous.height); + const scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight); + const scrollTopChanged = (this.scrollTop !== previous.scrollTop); return { width: this.width, @@ -377,8 +377,8 @@ export class SmoothScrollingOperation { private readonly _startTime: number; public animationFrameDisposable: IDisposable | null; - private scrollLeft: IAnimation; - private scrollTop: IAnimation; + private scrollLeft!: IAnimation; + private scrollTop!: IAnimation; protected constructor(from: ISmoothScrollPosition, to: ISmoothScrollPosition, startTime: number, duration: number) { this.from = from; diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index e3206199e6b..d4397d3279d 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -21,8 +21,8 @@ export function isFalsyOrWhitespace(str: string | undefined): boolean { * @returns the provided number with the given number of preceding zeros. */ export function pad(n: number, l: number, char: string = '0'): string { - let str = '' + n; - let r = [str]; + const str = '' + n; + const r = [str]; for (let i = str.length; i < l; i++) { r.push(char); @@ -44,7 +44,7 @@ export function format(value: string, ...args: any[]): string { return value; } return value.replace(_formatRegexp, function (match, group) { - let idx = parseInt(group, 10); + const idx = parseInt(group, 10); return isNaN(idx) || idx < 0 || idx >= args.length ? match : args[idx]; @@ -56,7 +56,7 @@ export function format(value: string, ...args: any[]): string { * being used e.g. in HTMLElement.innerHTML. */ export function escape(html: string): string { - return html.replace(/[<|>|&]/g, function (match) { + return html.replace(/[<>&]/g, function (match) { switch (match) { case '<': return '<'; case '>': return '>'; @@ -79,7 +79,7 @@ export function escapeRegExpCharacters(value: string): string { * @param needle the thing to trim (default is a blank) */ export function trim(haystack: string, needle: string = ' '): string { - let trimmed = ltrim(haystack, needle); + const trimmed = ltrim(haystack, needle); return rtrim(trimmed, needle); } @@ -93,7 +93,7 @@ export function ltrim(haystack: string, needle: string): string { return haystack; } - let needleLen = needle.length; + const needleLen = needle.length; if (needleLen === 0 || haystack.length === 0) { return haystack; } @@ -116,7 +116,7 @@ export function rtrim(haystack: string, needle: string): string { return haystack; } - let needleLen = needle.length, + const needleLen = needle.length, haystackLen = haystack.length; if (needleLen === 0 || haystackLen === 0) { @@ -173,7 +173,7 @@ export function startsWith(haystack: string, needle: string): boolean { * Determines if haystack ends with needle. */ export function endsWith(haystack: string, needle: string): boolean { - let diff = haystack.length - needle.length; + const diff = haystack.length - needle.length; if (diff > 0) { return haystack.indexOf(needle, diff) === diff; } else if (diff === 0) { @@ -188,6 +188,7 @@ export interface RegExpOptions { wholeWord?: boolean; multiline?: boolean; global?: boolean; + unicode?: boolean; } export function createRegExp(searchString: string, isRegex: boolean, options: RegExpOptions = {}): RegExp { @@ -215,6 +216,9 @@ export function createRegExp(searchString: string, isRegex: boolean, options: Re if (options.multiline) { modifiers += 'm'; } + if (options.unicode) { + modifiers += 'u'; + } return new RegExp(searchString, modifiers); } @@ -228,21 +232,28 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean { // We check against an empty string. If the regular expression doesn't advance // (e.g. ends in an endless loop) it will match an empty string. - let match = regexp.exec(''); - return !!(match && regexp.lastIndex === 0); + const match = regexp.exec(''); + return !!(match && regexp.lastIndex === 0); } export function regExpContainsBackreference(regexpValue: string): boolean { return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/); } +export function regExpFlags(regexp: RegExp): string { + return (regexp.global ? 'g' : '') + + (regexp.ignoreCase ? 'i' : '') + + (regexp.multiline ? 'm' : '') + + ((regexp as any).unicode ? 'u' : ''); +} + /** * Returns first index of the string that is not whitespace. * If string is empty or contains only whitespaces, returns -1 */ export function firstNonWhitespaceIndex(str: string): number { for (let i = 0, len = str.length; i < len; i++) { - let chCode = str.charCodeAt(i); + const chCode = str.charCodeAt(i); if (chCode !== CharCode.Space && chCode !== CharCode.Tab) { return i; } @@ -256,7 +267,7 @@ export function firstNonWhitespaceIndex(str: string): number { */ export function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string { for (let i = start; i < end; i++) { - let chCode = str.charCodeAt(i); + const chCode = str.charCodeAt(i); if (chCode !== CharCode.Space && chCode !== CharCode.Tab) { return str.substring(start, i); } @@ -270,7 +281,7 @@ export function getLeadingWhitespace(str: string, start: number = 0, end: number */ export function lastNonWhitespaceIndex(str: string, startIndex: number = str.length - 1): number { for (let i = startIndex; i >= 0; i--) { - let chCode = str.charCodeAt(i); + const chCode = str.charCodeAt(i); if (chCode !== CharCode.Space && chCode !== CharCode.Tab) { return i; } @@ -369,7 +380,7 @@ function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean { // a-z A-Z if (isAsciiLetter(codeA) && isAsciiLetter(codeB)) { - let diff = Math.abs(codeA - codeB); + const diff = Math.abs(codeA - codeB); if (diff !== 0 && diff !== 32) { return false; } @@ -420,8 +431,8 @@ export function commonSuffixLength(a: string, b: string): number { let i: number, len = Math.min(a.length, b.length); - let aLastIndex = a.length - 1; - let bLastIndex = b.length - 1; + const aLastIndex = a.length - 1; + const bLastIndex = b.length - 1; for (i = 0; i < len; i++) { if (a.charCodeAt(aLastIndex - i) !== b.charCodeAt(bLastIndex - i)) { @@ -448,7 +459,7 @@ function substrEquals(a: string, aStart: number, aEnd: number, b: string, bStart * For instance `overlap("foobar", "arr, I'm a pirate") === 2`. */ export function overlap(a: string, b: string): number { - let aEnd = a.length; + const aEnd = a.length; let bEnd = b.length; let aStart = aEnd - bEnd; @@ -475,9 +486,9 @@ export function overlap(a: string, b: string): number { // Code points U+0000 to U+D7FF and U+E000 to U+FFFF are represented on a single character // Code points U+10000 to U+10FFFF are represented on two consecutive characters //export function getUnicodePoint(str:string, index:number, len:number):number { -// let chrCode = str.charCodeAt(index); +// const chrCode = str.charCodeAt(index); // if (0xD800 <= chrCode && chrCode <= 0xDBFF && index + 1 < len) { -// let nextChrCode = str.charCodeAt(index + 1); +// const nextChrCode = str.charCodeAt(index + 1); // if (0xDC00 <= nextChrCode && nextChrCode <= 0xDFFF) { // return (chrCode - 0xD800) << 10 + (nextChrCode - 0xDC00) + 0x10000; // } @@ -616,6 +627,21 @@ export function removeAnsiEscapeCodes(str: string): string { return str; } +export const removeAccents: (str: string) => string = (function () { + if (typeof (String.prototype as any).normalize !== 'function') { + // ☹️ no ES6 features... + return function (str: string) { return str; }; + } else { + // transform into NFD form and remove accents + // see: https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript/37511463#37511463 + const regex = /[\u0300-\u036f]/g; + return function (str: string) { + return (str as any).normalize('NFD').replace(regex, empty); + }; + } +})(); + + // -- UTF-8 BOM export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM); @@ -659,7 +685,7 @@ export function fuzzyContains(target: string, query: string): boolean { let index = 0; let lastIndexOf = -1; while (index < queryLen) { - let indexOf = targetLower.indexOf(query[index], lastIndexOf + 1); + const indexOf = targetLower.indexOf(query[index], lastIndexOf + 1); if (indexOf < 0) { return false; } diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 49fc60dd7f0..c3e24cc0a45 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -49,7 +49,7 @@ export function isStringArray(value: any): value is string[] { * @returns whether the provided parameter is of type `object` but **not** * `null`, an `array`, a `regexp`, nor a `date`. */ -export function isObject(obj: any): boolean { +export function isObject(obj: any): obj is Object { // The method can't do a type cast since there are type (like strings) which // are subclasses of any put not positvely matched by the function. Hence type // narrowing results in wrong results. @@ -93,7 +93,6 @@ export function isUndefinedOrNull(obj: any): obj is undefined | null { return isUndefined(obj) || obj === null; } - const hasOwnProperty = Object.prototype.hasOwnProperty; /** @@ -124,12 +123,12 @@ export function isFunction(obj: any): obj is Function { * @returns whether the provided parameters is are JavaScript Function or not. */ export function areFunctions(...objects: any[]): boolean { - return objects && objects.length > 0 && objects.every(isFunction); + return objects.length > 0 && objects.every(isFunction); } export type TypeConstraint = string | Function; -export function validateConstraints(args: any[], constraints: (TypeConstraint | undefined)[]): void { +export function validateConstraints(args: any[], constraints: Array): void { const len = Math.min(args.length, constraints.length); for (let i = 0; i < len; i++) { validateConstraint(args[i], constraints[i]); @@ -143,8 +142,12 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi throw new Error(`argument does not match constraint: typeof ${constraint}`); } } else if (isFunction(constraint)) { - if (arg instanceof constraint) { - return; + try { + if (arg instanceof constraint) { + return; + } + } catch{ + // ignore } if (!isUndefinedOrNull(arg) && arg.constructor === constraint) { return; @@ -156,13 +159,26 @@ export function validateConstraint(arg: any, constraint: TypeConstraint | undefi } } -/** - * Creates a new object of the provided class and will call the constructor with - * any additional argument supplied. - */ -export function create(ctor: Function, ...args: any[]): any { - let obj = Object.create(ctor.prototype); - ctor.apply(obj, args); - - return obj; +export function getAllPropertyNames(obj: object): string[] { + let res: string[] = []; + let proto = Object.getPrototypeOf(obj); + while (Object.prototype !== proto) { + res = res.concat(Object.getOwnPropertyNames(proto)); + proto = Object.getPrototypeOf(proto); + } + return res; +} + +/** + * Converts null to undefined, passes all other values through. + */ +export function withNullAsUndefined(x: T | null): T | undefined { + return x === null ? undefined : x; +} + +/** + * Converts undefined to null, passes all other values through. + */ +export function withUndefinedAsNull(x: T | undefined): T | null { + return typeof x === 'undefined' ? null : x; } diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 4424cc055ae..ec6497ef0b6 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -56,6 +56,21 @@ function _validateUri(ret: URI, _strict?: boolean): void { } } +// for a while we allowed uris *without* schemes and this is the migration +// for them, e.g. an uri without scheme and without strict-mode warns and falls +// back to the file-scheme. that should cause the least carnage and still be a +// clear warning +function _schemeFix(scheme: string, _strict: boolean): string { + if (_strict || _throwOnMissingSchema) { + return scheme || _empty; + } + if (!scheme) { + console.trace('BAD uri lacks scheme, falling back to file-scheme.'); + scheme = 'file'; + } + return scheme; +} + // implements a bit of https://tools.ietf.org/html/rfc3986#section-5 function _referenceResolution(scheme: string, path: string): string { @@ -108,7 +123,10 @@ export class URI implements UriComponents { && typeof (thing).fragment === 'string' && typeof (thing).path === 'string' && typeof (thing).query === 'string' - && typeof (thing).scheme === 'string'; + && typeof (thing).scheme === 'string' + && typeof (thing).fsPath === 'function' + && typeof (thing).with === 'function' + && typeof (thing).toString === 'function'; } /** @@ -151,7 +169,7 @@ export class URI implements UriComponents { /** * @internal */ - protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean) { + protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict: boolean = false) { if (typeof schemeOrData === 'object') { this.scheme = schemeOrData.scheme || _empty; @@ -163,7 +181,7 @@ export class URI implements UriComponents { // that creates uri components. // _validateUri(this); } else { - this.scheme = schemeOrData || _empty; + this.scheme = _schemeFix(schemeOrData, _strict); this.authority = authority || _empty; this.path = _referenceResolution(this.scheme, path || _empty); this.query = query || _empty; @@ -208,34 +226,34 @@ export class URI implements UriComponents { // ---- modify to new ------------------------- - public with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI { + with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI { if (!change) { return this; } let { scheme, authority, path, query, fragment } = change; - if (scheme === void 0) { + if (scheme === undefined) { scheme = this.scheme; } else if (scheme === null) { scheme = _empty; } - if (authority === void 0) { + if (authority === undefined) { authority = this.authority; } else if (authority === null) { authority = _empty; } - if (path === void 0) { + if (path === undefined) { path = this.path; } else if (path === null) { path = _empty; } - if (query === void 0) { + if (query === undefined) { query = this.query; } else if (query === null) { query = _empty; } - if (fragment === void 0) { + if (fragment === undefined) { fragment = this.fragment; } else if (fragment === null) { fragment = _empty; @@ -261,7 +279,7 @@ export class URI implements UriComponents { * * @param value A string which represents an URI (see `URI#toString`). */ - public static parse(value: string, _strict: boolean = false): URI { + static parse(value: string, _strict: boolean = false): URI { const match = _regexp.exec(value); if (!match) { return new _URI(_empty, _empty, _empty, _empty, _empty); @@ -297,7 +315,7 @@ export class URI implements UriComponents { * * @param path A file system path (see `URI#fsPath`) */ - public static file(path: string): URI { + static file(path: string): URI { let authority = _empty; @@ -311,7 +329,7 @@ export class URI implements UriComponents { // check for authority as used in UNC shares // or use the path as given if (path[0] === _slash && path[1] === _slash) { - let idx = path.indexOf(_slash, 2); + const idx = path.indexOf(_slash, 2); if (idx === -1) { authority = path.substring(2); path = _slash; @@ -324,7 +342,7 @@ export 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 { + static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { return new _URI( components.scheme, components.authority, @@ -337,7 +355,7 @@ export class URI implements UriComponents { // ---- printing/externalize --------------------------- /** - * Creates a string presentation for this URI. It's guaranteed that calling + * Creates a string representation for this URI. It's guaranteed that calling * `URI.parse` with the result of this function creates an URI which is equal * to this URI. * @@ -347,21 +365,25 @@ export class URI implements UriComponents { * * @param skipEncoding Do not encode the result, default is `false` */ - public toString(skipEncoding: boolean = false): string { + toString(skipEncoding: boolean = false): string { return _asFormatted(this, skipEncoding); } - public toJSON(): object { + toJSON(): UriComponents { return this; } - static revive(data: UriComponents | any): URI { + static revive(data: UriComponents | URI): URI; + static revive(data: UriComponents | URI | undefined): URI | undefined; + static revive(data: UriComponents | URI | null): URI | null; + static revive(data: UriComponents | URI | undefined | null): URI | undefined | null; + static revive(data: UriComponents | URI | undefined | null): URI | undefined | null { if (!data) { return data; } else if (data instanceof URI) { return data; } else { - let result = new _URI(data); + const result = new _URI(data); result._fsPath = (data).fsPath; result._formatted = (data).external; return result; @@ -397,7 +419,7 @@ class _URI extends URI { return this._fsPath; } - public toString(skipEncoding: boolean = false): string { + toString(skipEncoding: boolean = false): string { if (!skipEncoding) { if (!this._formatted) { this._formatted = _asFormatted(this, false); @@ -409,7 +431,7 @@ class _URI extends URI { } } - toJSON(): object { + toJSON(): UriComponents { const res = { $mid: 1 }; @@ -470,7 +492,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri let nativeEncodePos = -1; for (let pos = 0; pos < uriComponent.length; pos++) { - let code = uriComponent.charCodeAt(pos); + const code = uriComponent.charCodeAt(pos); // unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3 if ( @@ -500,7 +522,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri } // check with default table first - let escaped = encodeTable[code]; + const escaped = encodeTable[code]; if (escaped !== undefined) { // check if we are delaying native encode @@ -529,7 +551,7 @@ function encodeURIComponentFast(uriComponent: string, allowSlash: boolean): stri function encodeURIComponentMinimal(path: string): string { let res: string | undefined = undefined; for (let pos = 0; pos < path.length; pos++) { - let code = path.charCodeAt(pos); + const code = path.charCodeAt(pos); if (code === CharCode.Hash || code === CharCode.QuestionMark) { if (res === undefined) { res = path.substr(0, pos); @@ -546,7 +568,6 @@ function encodeURIComponentMinimal(path: string): string { /** * Compute `fsPath` for the given uri - * @param uri */ function _makeFsPath(uri: URI): string { @@ -620,12 +641,12 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string { if (path) { // lower-case windows drive letters in /C:/fff or C:/fff if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) { - let code = path.charCodeAt(1); + const code = path.charCodeAt(1); if (code >= CharCode.A && code <= CharCode.Z) { path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3 } } else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) { - let code = path.charCodeAt(0); + const code = path.charCodeAt(0); if (code >= CharCode.A && code <= CharCode.Z) { path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3 } diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 374ffd99055..4cee99c11f7 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; +import { MarshalledObject } from 'vs/base/common/marshalling'; export interface IURITransformer { transformIncoming(uri: UriComponents): UriComponents; - transformOutgoing(uri: URI): URI; transformOutgoing(uri: UriComponents): UriComponents; + transformOutgoingURI(uri: URI): URI; } export const DefaultURITransformer: IURITransformer = new class { @@ -16,9 +17,90 @@ export const DefaultURITransformer: IURITransformer = new class { return uri; } - transformOutgoing(uri: URI): URI; - transformOutgoing(uri: UriComponents): UriComponents; - transformOutgoing(uri: URI | UriComponents): URI | UriComponents { + transformOutgoing(uri: UriComponents): UriComponents { return uri; } -}; \ No newline at end of file + + transformOutgoingURI(uri: URI): URI { + return uri; + } +}; + +function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any { + + if (!obj || depth > 200) { + return null; + } + + if (typeof obj === 'object') { + if (obj instanceof URI) { + return transformer.transformOutgoing(obj); + } + + // walk object (or array) + for (let key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + const r = _transformOutgoingURIs(obj[key], transformer, depth + 1); + if (r !== null) { + obj[key] = r; + } + } + } + } + + return null; +} + +export function transformOutgoingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformOutgoingURIs(obj, transformer, 0); + if (result === null) { + // no change + return obj; + } + return result; +} + + +function _transformIncomingURIs(obj: any, transformer: IURITransformer, revive: boolean, depth: number): any { + + if (!obj || depth > 200) { + return null; + } + + if (typeof obj === 'object') { + + if ((obj).$mid === 1) { + return revive ? URI.revive(transformer.transformIncoming(obj)) : transformer.transformIncoming(obj); + } + + // walk object (or array) + for (let key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + const r = _transformIncomingURIs(obj[key], transformer, revive, depth + 1); + if (r !== null) { + obj[key] = r; + } + } + } + } + + return null; +} + +export function transformIncomingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformIncomingURIs(obj, transformer, false, 0); + if (result === null) { + // no change + return obj; + } + return result; +} + +export function transformAndReviveIncomingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformIncomingURIs(obj, transformer, true, 0); + if (result === null) { + // no change + return obj; + } + return result; +} \ No newline at end of file diff --git a/src/vs/base/common/winjs.base.d.ts b/src/vs/base/common/winjs.base.d.ts deleted file mode 100644 index efac30a26b0..00000000000 --- a/src/vs/base/common/winjs.base.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/// Interfaces for WinJS - -export type ErrorCallback = (error: any) => void; - -export class Promise { - constructor(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason: any) => void) => void); - - public then( - onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | null): Promise; - - - public static as(value: null): Promise; - public static as(value: undefined): Promise; - public static as(value: PromiseLike): PromiseLike; - public static as>(value: SomePromise): SomePromise; - public static as(value: T): Promise; - - public static join(promises: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; - public static join(promises: (T | PromiseLike)[]): Promise; - - public static wrap(value: T | PromiseLike): Promise; - - public static wrapError(error: Error): Promise; - - /** - * @internal - */ - public static addEventListener(event: 'error', promiseErrorHandler: (e: IPromiseError) => void): void; -} - -export type TValueCallback = (value: T | PromiseLike) => void; - -export { - Promise as TPromise, - TValueCallback as ValueCallback -}; - -export interface IPromiseErrorDetail { - parent: Promise; - error: any; - id: number; - handler: Function; - exception: Error; -} - -export interface IPromiseError { - detail: IPromiseErrorDetail; -} diff --git a/src/vs/base/common/winjs.base.js b/src/vs/base/common/winjs.base.js deleted file mode 100644 index d8de83ddd3e..00000000000 --- a/src/vs/base/common/winjs.base.js +++ /dev/null @@ -1,2096 +0,0 @@ -/** - * Extracted from https://github.com/winjs/winjs - * Version: 4.4.0(ec3258a9f3a36805a187848984e3bb938044178d) - * Copyright (c) Microsoft Corporation. - * All Rights Reserved. - * Licensed under the MIT License. - */ -var __winjs_exports; - -(function() { - -var _modules = Object.create(null);//{}; -_modules["WinJS/Core/_WinJS"] = {}; - -var _winjs = function(moduleId, deps, factory) { - var exports = {}; - var exportsPassedIn = false; - - var depsValues = deps.map(function(dep) { - if (dep === 'exports') { - exportsPassedIn = true; - return exports; - } - return _modules[dep]; - }); - - var result = factory.apply({}, depsValues); - - _modules[moduleId] = exportsPassedIn ? exports : result; -}; - - -_winjs("WinJS/Core/_Global", [], function () { - "use strict"; - - // Appease jshint - /* global window, self, global */ - - var globalObject = - typeof window !== 'undefined' ? window : - typeof self !== 'undefined' ? self : - typeof global !== 'undefined' ? global : - {}; - return globalObject; -}); - -_winjs("WinJS/Core/_BaseCoreUtils", ["WinJS/Core/_Global"], function baseCoreUtilsInit(_Global) { - "use strict"; - - var hasWinRT = !!_Global.Windows; - - function markSupportedForProcessing(func) { - /// - /// - /// Marks a function as being compatible with declarative processing, such as WinJS.UI.processAll - /// or WinJS.Binding.processAll. - /// - /// - /// The function to be marked as compatible with declarative processing. - /// - /// - /// The input function. - /// - /// - func.supportedForProcessing = true; - return func; - } - - var actualSetImmediate = null; - - return { - hasWinRT: hasWinRT, - markSupportedForProcessing: markSupportedForProcessing, - _setImmediate: function (callback) { - // BEGIN monaco change - if (actualSetImmediate === null) { - if (_Global.setImmediate) { - actualSetImmediate = _Global.setImmediate.bind(_Global); - } else if (typeof process !== 'undefined' && typeof process.nextTick === 'function') { - actualSetImmediate = process.nextTick.bind(process); - } else { - actualSetImmediate = _Global.setTimeout.bind(_Global); - } - } - actualSetImmediate(callback); - // END monaco change - } - }; -}); -_winjs("WinJS/Core/_WriteProfilerMark", ["WinJS/Core/_Global"], function profilerInit(_Global) { - "use strict"; - - return _Global.msWriteProfilerMark || function () { }; -}); -_winjs("WinJS/Core/_Base", ["WinJS/Core/_WinJS","WinJS/Core/_Global","WinJS/Core/_BaseCoreUtils","WinJS/Core/_WriteProfilerMark"], function baseInit(_WinJS, _Global, _BaseCoreUtils, _WriteProfilerMark) { - "use strict"; - - function initializeProperties(target, members, prefix) { - var keys = Object.keys(members); - var isArray = Array.isArray(target); - var properties; - var i, len; - for (i = 0, len = keys.length; i < len; i++) { - var key = keys[i]; - var enumerable = key.charCodeAt(0) !== /*_*/95; - var member = members[key]; - if (member && typeof member === 'object') { - if (member.value !== undefined || typeof member.get === 'function' || typeof member.set === 'function') { - if (member.enumerable === undefined) { - member.enumerable = enumerable; - } - if (prefix && member.setName && typeof member.setName === 'function') { - member.setName(prefix + "." + key); - } - properties = properties || {}; - properties[key] = member; - continue; - } - } - if (!enumerable) { - properties = properties || {}; - properties[key] = { value: member, enumerable: enumerable, configurable: true, writable: true }; - continue; - } - if (isArray) { - target.forEach(function (target) { - target[key] = member; - }); - } else { - target[key] = member; - } - } - if (properties) { - if (isArray) { - target.forEach(function (target) { - Object.defineProperties(target, properties); - }); - } else { - Object.defineProperties(target, properties); - } - } - } - - (function () { - - var _rootNamespace = _WinJS; - if (!_rootNamespace.Namespace) { - _rootNamespace.Namespace = Object.create(Object.prototype); - } - - function createNamespace(parentNamespace, name) { - var currentNamespace = parentNamespace || {}; - if (name) { - var namespaceFragments = name.split("."); - if (currentNamespace === _Global && namespaceFragments[0] === "WinJS") { - currentNamespace = _WinJS; - namespaceFragments.splice(0, 1); - } - for (var i = 0, len = namespaceFragments.length; i < len; i++) { - var namespaceName = namespaceFragments[i]; - if (!currentNamespace[namespaceName]) { - Object.defineProperty(currentNamespace, namespaceName, - { value: {}, writable: false, enumerable: true, configurable: true } - ); - } - currentNamespace = currentNamespace[namespaceName]; - } - } - return currentNamespace; - } - - function defineWithParent(parentNamespace, name, members) { - /// - /// - /// Defines a new namespace with the specified name under the specified parent namespace. - /// - /// - /// The parent namespace. - /// - /// - /// The name of the new namespace. - /// - /// - /// The members of the new namespace. - /// - /// - /// The newly-defined namespace. - /// - /// - var currentNamespace = createNamespace(parentNamespace, name); - - if (members) { - initializeProperties(currentNamespace, members, name || ""); - } - - return currentNamespace; - } - - function define(name, members) { - /// - /// - /// Defines a new namespace with the specified name. - /// - /// - /// The name of the namespace. This could be a dot-separated name for nested namespaces. - /// - /// - /// The members of the new namespace. - /// - /// - /// The newly-defined namespace. - /// - /// - return defineWithParent(_Global, name, members); - } - - var LazyStates = { - uninitialized: 1, - working: 2, - initialized: 3, - }; - - function lazy(f) { - var name; - var state = LazyStates.uninitialized; - var result; - return { - setName: function (value) { - name = value; - }, - get: function () { - switch (state) { - case LazyStates.initialized: - return result; - - case LazyStates.uninitialized: - state = LazyStates.working; - try { - _WriteProfilerMark("WinJS.Namespace._lazy:" + name + ",StartTM"); - result = f(); - } finally { - _WriteProfilerMark("WinJS.Namespace._lazy:" + name + ",StopTM"); - state = LazyStates.uninitialized; - } - f = null; - state = LazyStates.initialized; - return result; - - case LazyStates.working: - throw "Illegal: reentrancy on initialization"; - - default: - throw "Illegal"; - } - }, - set: function (value) { - switch (state) { - case LazyStates.working: - throw "Illegal: reentrancy on initialization"; - - default: - state = LazyStates.initialized; - result = value; - break; - } - }, - enumerable: true, - configurable: true, - }; - } - - // helper for defining AMD module members - function moduleDefine(exports, name, members) { - var target = [exports]; - var publicNS = null; - if (name) { - publicNS = createNamespace(_Global, name); - target.push(publicNS); - } - initializeProperties(target, members, name || ""); - return publicNS; - } - - // Establish members of the "WinJS.Namespace" namespace - Object.defineProperties(_rootNamespace.Namespace, { - - defineWithParent: { value: defineWithParent, writable: true, enumerable: true, configurable: true }, - - define: { value: define, writable: true, enumerable: true, configurable: true }, - - _lazy: { value: lazy, writable: true, enumerable: true, configurable: true }, - - _moduleDefine: { value: moduleDefine, writable: true, enumerable: true, configurable: true } - - }); - - })(); - - (function () { - - function define(constructor, instanceMembers, staticMembers) { - /// - /// - /// Defines a class using the given constructor and the specified instance members. - /// - /// - /// A constructor function that is used to instantiate this class. - /// - /// - /// The set of instance fields, properties, and methods made available on the class. - /// - /// - /// The set of static fields, properties, and methods made available on the class. - /// - /// - /// The newly-defined class. - /// - /// - constructor = constructor || function () { }; - _BaseCoreUtils.markSupportedForProcessing(constructor); - if (instanceMembers) { - initializeProperties(constructor.prototype, instanceMembers); - } - if (staticMembers) { - initializeProperties(constructor, staticMembers); - } - return constructor; - } - - function derive(baseClass, constructor, instanceMembers, staticMembers) { - /// - /// - /// Creates a sub-class based on the supplied baseClass parameter, using prototypal inheritance. - /// - /// - /// The class to inherit from. - /// - /// - /// A constructor function that is used to instantiate this class. - /// - /// - /// The set of instance fields, properties, and methods to be made available on the class. - /// - /// - /// The set of static fields, properties, and methods to be made available on the class. - /// - /// - /// The newly-defined class. - /// - /// - if (baseClass) { - constructor = constructor || function () { }; - var basePrototype = baseClass.prototype; - constructor.prototype = Object.create(basePrototype); - _BaseCoreUtils.markSupportedForProcessing(constructor); - Object.defineProperty(constructor.prototype, "constructor", { value: constructor, writable: true, configurable: true, enumerable: true }); - if (instanceMembers) { - initializeProperties(constructor.prototype, instanceMembers); - } - if (staticMembers) { - initializeProperties(constructor, staticMembers); - } - return constructor; - } else { - return define(constructor, instanceMembers, staticMembers); - } - } - - function mix(constructor) { - /// - /// - /// Defines a class using the given constructor and the union of the set of instance members - /// specified by all the mixin objects. The mixin parameter list is of variable length. - /// - /// - /// A constructor function that is used to instantiate this class. - /// - /// - /// The newly-defined class. - /// - /// - constructor = constructor || function () { }; - var i, len; - for (i = 1, len = arguments.length; i < len; i++) { - initializeProperties(constructor.prototype, arguments[i]); - } - return constructor; - } - - // Establish members of "WinJS.Class" namespace - _WinJS.Namespace.define("WinJS.Class", { - define: define, - derive: derive, - mix: mix - }); - - })(); - - return { - Namespace: _WinJS.Namespace, - Class: _WinJS.Class - }; - -}); -_winjs("WinJS/Core/_ErrorFromName", ["WinJS/Core/_Base"], function errorsInit(_Base) { - "use strict"; - - var ErrorFromName = _Base.Class.derive(Error, function (name, message) { - /// - /// - /// Creates an Error object with the specified name and message properties. - /// - /// The name of this error. The name is meant to be consumed programmatically and should not be localized. - /// The message for this error. The message is meant to be consumed by humans and should be localized. - /// Error instance with .name and .message properties populated - /// - this.name = name; - this.message = message || name; - }, { - /* empty */ - }, { - supportedForProcessing: false, - }); - - _Base.Namespace.define("WinJS", { - // ErrorFromName establishes a simple pattern for returning error codes. - // - ErrorFromName: ErrorFromName - }); - - return ErrorFromName; - -}); - - -_winjs("WinJS/Core/_Events", ["exports","WinJS/Core/_Base"], function eventsInit(exports, _Base) { - "use strict"; - - - function createEventProperty(name) { - var eventPropStateName = "_on" + name + "state"; - - return { - get: function () { - var state = this[eventPropStateName]; - return state && state.userHandler; - }, - set: function (handler) { - var state = this[eventPropStateName]; - if (handler) { - if (!state) { - state = { wrapper: function (evt) { return state.userHandler(evt); }, userHandler: handler }; - Object.defineProperty(this, eventPropStateName, { value: state, enumerable: false, writable:true, configurable: true }); - this.addEventListener(name, state.wrapper, false); - } - state.userHandler = handler; - } else if (state) { - this.removeEventListener(name, state.wrapper, false); - this[eventPropStateName] = null; - } - }, - enumerable: true - }; - } - - function createEventProperties() { - /// - /// - /// Creates an object that has one property for each name passed to the function. - /// - /// - /// A variable list of property names. - /// - /// - /// The object with the specified properties. The names of the properties are prefixed with 'on'. - /// - /// - var props = {}; - for (var i = 0, len = arguments.length; i < len; i++) { - var name = arguments[i]; - props["on" + name] = createEventProperty(name); - } - return props; - } - - var EventMixinEvent = _Base.Class.define( - function EventMixinEvent_ctor(type, detail, target) { - this.detail = detail; - this.target = target; - this.timeStamp = Date.now(); - this.type = type; - }, - { - bubbles: { value: false, writable: false }, - cancelable: { value: false, writable: false }, - currentTarget: { - get: function () { return this.target; } - }, - defaultPrevented: { - get: function () { return this._preventDefaultCalled; } - }, - trusted: { value: false, writable: false }, - eventPhase: { value: 0, writable: false }, - target: null, - timeStamp: null, - type: null, - - preventDefault: function () { - this._preventDefaultCalled = true; - }, - stopImmediatePropagation: function () { - this._stopImmediatePropagationCalled = true; - }, - stopPropagation: function () { - } - }, { - supportedForProcessing: false, - } - ); - - var eventMixin = { - _listeners: null, - - addEventListener: function (type, listener, useCapture) { - /// - /// - /// Adds an event listener to the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to invoke when the event is raised. - /// - /// - /// if true initiates capture, otherwise false. - /// - /// - useCapture = useCapture || false; - this._listeners = this._listeners || {}; - var eventListeners = (this._listeners[type] = this._listeners[type] || []); - for (var i = 0, len = eventListeners.length; i < len; i++) { - var l = eventListeners[i]; - if (l.useCapture === useCapture && l.listener === listener) { - return; - } - } - eventListeners.push({ listener: listener, useCapture: useCapture }); - }, - dispatchEvent: function (type, details) { - /// - /// - /// Raises an event of the specified type and with the specified additional properties. - /// - /// - /// The type (name) of the event. - /// - /// - /// The set of additional properties to be attached to the event object when the event is raised. - /// - /// - /// true if preventDefault was called on the event. - /// - /// - var listeners = this._listeners && this._listeners[type]; - if (listeners) { - var eventValue = new EventMixinEvent(type, details, this); - // Need to copy the array to protect against people unregistering while we are dispatching - listeners = listeners.slice(0, listeners.length); - for (var i = 0, len = listeners.length; i < len && !eventValue._stopImmediatePropagationCalled; i++) { - listeners[i].listener(eventValue); - } - return eventValue.defaultPrevented || false; - } - return false; - }, - removeEventListener: function (type, listener, useCapture) { - /// - /// - /// Removes an event listener from the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to remove. - /// - /// - /// Specifies whether to initiate capture. - /// - /// - useCapture = useCapture || false; - var listeners = this._listeners && this._listeners[type]; - if (listeners) { - for (var i = 0, len = listeners.length; i < len; i++) { - var l = listeners[i]; - if (l.listener === listener && l.useCapture === useCapture) { - listeners.splice(i, 1); - if (listeners.length === 0) { - delete this._listeners[type]; - } - // Only want to remove one element for each call to removeEventListener - break; - } - } - } - } - }; - - _Base.Namespace._moduleDefine(exports, "WinJS.Utilities", { - _createEventProperty: createEventProperty, - createEventProperties: createEventProperties, - eventMixin: eventMixin - }); - -}); - - -_winjs("WinJS/Core/_Trace", ["WinJS/Core/_Global"], function traceInit(_Global) { - "use strict"; - - function nop(v) { - return v; - } - - return { - _traceAsyncOperationStarting: (_Global.Debug && _Global.Debug.msTraceAsyncOperationStarting && _Global.Debug.msTraceAsyncOperationStarting.bind(_Global.Debug)) || nop, - _traceAsyncOperationCompleted: (_Global.Debug && _Global.Debug.msTraceAsyncOperationCompleted && _Global.Debug.msTraceAsyncOperationCompleted.bind(_Global.Debug)) || nop, - _traceAsyncCallbackStarting: (_Global.Debug && _Global.Debug.msTraceAsyncCallbackStarting && _Global.Debug.msTraceAsyncCallbackStarting.bind(_Global.Debug)) || nop, - _traceAsyncCallbackCompleted: (_Global.Debug && _Global.Debug.msTraceAsyncCallbackCompleted && _Global.Debug.msTraceAsyncCallbackCompleted.bind(_Global.Debug)) || nop - }; -}); -_winjs("WinJS/Promise/_StateMachine", ["WinJS/Core/_Global","WinJS/Core/_BaseCoreUtils","WinJS/Core/_Base","WinJS/Core/_ErrorFromName","WinJS/Core/_Events","WinJS/Core/_Trace"], function promiseStateMachineInit(_Global, _BaseCoreUtils, _Base, _ErrorFromName, _Events, _Trace) { - "use strict"; - - _Global.Debug && (_Global.Debug.setNonUserCodeExceptions = true); - - var ListenerType = _Base.Class.mix(_Base.Class.define(null, { /*empty*/ }, { supportedForProcessing: false }), _Events.eventMixin); - var promiseEventListeners = new ListenerType(); - // make sure there is a listeners collection so that we can do a more trivial check below - promiseEventListeners._listeners = {}; - var errorET = "error"; - var canceledName = "Canceled"; - var tagWithStack = false; - var tag = { - promise: 0x01, - thenPromise: 0x02, - errorPromise: 0x04, - exceptionPromise: 0x08, - completePromise: 0x10, - }; - tag.all = tag.promise | tag.thenPromise | tag.errorPromise | tag.exceptionPromise | tag.completePromise; - - // - // Global error counter, for each error which enters the system we increment this once and then - // the error number travels with the error as it traverses the tree of potential handlers. - // - // When someone has registered to be told about errors (WinJS.Promise.callonerror) promises - // which are in error will get tagged with a ._errorId field. This tagged field is the - // contract by which nested promises with errors will be identified as chaining for the - // purposes of the callonerror semantics. If a nested promise in error is encountered without - // a ._errorId it will be assumed to be foreign and treated as an interop boundary and - // a new error id will be minted. - // - var error_number = 1; - - // - // The state machine has a interesting hiccup in it with regards to notification, in order - // to flatten out notification and avoid recursion for synchronous completion we have an - // explicit set of *_notify states which are responsible for notifying their entire tree - // of children. They can do this because they know that immediate children are always - // ThenPromise instances and we can therefore reach into their state to access the - // _listeners collection. - // - // So, what happens is that a Promise will be fulfilled through the _completed or _error - // messages at which point it will enter a *_notify state and be responsible for to move - // its children into an (as appropriate) success or error state and also notify that child's - // listeners of the state transition, until leaf notes are reached. - // - - var state_created, // -> working - state_working, // -> error | error_notify | success | success_notify | canceled | waiting - state_waiting, // -> error | error_notify | success | success_notify | waiting_canceled - state_waiting_canceled, // -> error | error_notify | success | success_notify | canceling - state_canceled, // -> error | error_notify | success | success_notify | canceling - state_canceling, // -> error_notify - state_success_notify, // -> success - state_success, // -> . - state_error_notify, // -> error - state_error; // -> . - - // Noop function, used in the various states to indicate that they don't support a given - // message. Named with the somewhat cute name '_' because it reads really well in the states. - - function _() { } - - // Initial state - // - state_created = { - name: "created", - enter: function (promise) { - promise._setState(state_working); - }, - cancel: _, - done: _, - then: _, - _completed: _, - _error: _, - _notify: _, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Ready state, waiting for a message (completed/error/progress), able to be canceled - // - state_working = { - name: "working", - enter: _, - cancel: function (promise) { - promise._setState(state_canceled); - }, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Waiting state, if a promise is completed with a value which is itself a promise - // (has a then() method) it signs up to be informed when that child promise is - // fulfilled at which point it will be fulfilled with that value. - // - state_waiting = { - name: "waiting", - enter: function (promise) { - var waitedUpon = promise._value; - // We can special case our own intermediate promises which are not in a - // terminal state by just pushing this promise as a listener without - // having to create new indirection functions - if (waitedUpon instanceof ThenPromise && - waitedUpon._state !== state_error && - waitedUpon._state !== state_success) { - pushListener(waitedUpon, { promise: promise }); - } else { - var error = function (value) { - if (waitedUpon._errorId) { - promise._chainedError(value, waitedUpon); - } else { - // Because this is an interop boundary we want to indicate that this - // error has been handled by the promise infrastructure before we - // begin a new handling chain. - // - callonerror(promise, value, detailsForHandledError, waitedUpon, error); - promise._error(value); - } - }; - error.handlesOnError = true; - waitedUpon.then( - promise._completed.bind(promise), - error, - promise._progress.bind(promise) - ); - } - }, - cancel: function (promise) { - promise._setState(state_waiting_canceled); - }, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Waiting canceled state, when a promise has been in a waiting state and receives a - // request to cancel its pending work it will forward that request to the child promise - // and then waits to be informed of the result. This promise moves itself into the - // canceling state but understands that the child promise may instead push it to a - // different state. - // - state_waiting_canceled = { - name: "waiting_canceled", - enter: function (promise) { - // Initiate a transition to canceling. Triggering a cancel on the promise - // that we are waiting upon may result in a different state transition - // before the state machine pump runs again. - promise._setState(state_canceling); - var waitedUpon = promise._value; - if (waitedUpon.cancel) { - waitedUpon.cancel(); - } - }, - cancel: _, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Canceled state, moves to the canceling state and then tells the promise to do - // whatever it might need to do on cancelation. - // - state_canceled = { - name: "canceled", - enter: function (promise) { - // Initiate a transition to canceling. The _cancelAction may change the state - // before the state machine pump runs again. - promise._setState(state_canceling); - promise._cancelAction(); - }, - cancel: _, - done: done, - then: then, - _completed: completed, - _error: error, - _notify: _, - _progress: progress, - _setCompleteValue: setCompleteValue, - _setErrorValue: setErrorValue - }; - - // Canceling state, commits to the promise moving to an error state with an error - // object whose 'name' and 'message' properties contain the string "Canceled" - // - state_canceling = { - name: "canceling", - enter: function (promise) { - var error = new Error(canceledName); - error.name = error.message; - promise._value = error; - promise._setState(state_error_notify); - }, - cancel: _, - done: _, - then: _, - _completed: _, - _error: _, - _notify: _, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Success notify state, moves a promise to the success state and notifies all children - // - state_success_notify = { - name: "complete_notify", - enter: function (promise) { - promise.done = CompletePromise.prototype.done; - promise.then = CompletePromise.prototype.then; - if (promise._listeners) { - var queue = [promise]; - var p; - while (queue.length) { - p = queue.shift(); - p._state._notify(p, queue); - } - } - promise._setState(state_success); - }, - cancel: _, - done: null, /*error to get here */ - then: null, /*error to get here */ - _completed: _, - _error: _, - _notify: notifySuccess, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Success state, moves a promise to the success state and does NOT notify any children. - // Some upstream promise is owning the notification pass. - // - state_success = { - name: "success", - enter: function (promise) { - promise.done = CompletePromise.prototype.done; - promise.then = CompletePromise.prototype.then; - promise._cleanupAction(); - }, - cancel: _, - done: null, /*error to get here */ - then: null, /*error to get here */ - _completed: _, - _error: _, - _notify: notifySuccess, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Error notify state, moves a promise to the error state and notifies all children - // - state_error_notify = { - name: "error_notify", - enter: function (promise) { - promise.done = ErrorPromise.prototype.done; - promise.then = ErrorPromise.prototype.then; - if (promise._listeners) { - var queue = [promise]; - var p; - while (queue.length) { - p = queue.shift(); - p._state._notify(p, queue); - } - } - promise._setState(state_error); - }, - cancel: _, - done: null, /*error to get here*/ - then: null, /*error to get here*/ - _completed: _, - _error: _, - _notify: notifyError, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // Error state, moves a promise to the error state and does NOT notify any children. - // Some upstream promise is owning the notification pass. - // - state_error = { - name: "error", - enter: function (promise) { - promise.done = ErrorPromise.prototype.done; - promise.then = ErrorPromise.prototype.then; - promise._cleanupAction(); - }, - cancel: _, - done: null, /*error to get here*/ - then: null, /*error to get here*/ - _completed: _, - _error: _, - _notify: notifyError, - _progress: _, - _setCompleteValue: _, - _setErrorValue: _ - }; - - // - // The statemachine implementation follows a very particular pattern, the states are specified - // as static stateless bags of functions which are then indirected through the state machine - // instance (a Promise). As such all of the functions on each state have the promise instance - // passed to them explicitly as a parameter and the Promise instance members do a little - // dance where they indirect through the state and insert themselves in the argument list. - // - // We could instead call directly through the promise states however then every caller - // would have to remember to do things like pumping the state machine to catch state transitions. - // - - var PromiseStateMachine = _Base.Class.define(null, { - _listeners: null, - _nextState: null, - _state: null, - _value: null, - - cancel: function () { - /// - /// - /// Attempts to cancel the fulfillment of a promised value. If the promise hasn't - /// already been fulfilled and cancellation is supported, the promise enters - /// the error state with a value of Error("Canceled"). - /// - /// - this._state.cancel(this); - this._run(); - }, - done: function Promise_done(onComplete, onError, onProgress) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// After the handlers have finished executing, this function throws any error that would have been returned - /// from then() as a promise in the error state. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The fulfilled value is passed as the single argument. If the value is null, - /// the fulfilled value is returned. The value returned - /// from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while executing the function, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function is the fulfilled value of the promise returned by then(). - /// - /// - /// the function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - this._state.done(this, onComplete, onError, onProgress); - }, - then: function Promise_then(onComplete, onError, onProgress) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The value is passed as the single argument. If the value is null, the value is returned. - /// The value returned from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while this function is being executed, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function becomes the fulfilled value of the promise returned by then(). - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// The promise whose value is the result of executing the complete or - /// error function. - /// - /// - // BEGIN monaco change - if (this.then !== Promise_then) { - this.then(onComplete, onError, onProgress); - return; - } - // END monaco change - return this._state.then(this, onComplete, onError, onProgress); - }, - - _chainedError: function (value, context) { - var result = this._state._error(this, value, detailsForChainedError, context); - this._run(); - return result; - }, - _completed: function (value) { - var result = this._state._completed(this, value); - this._run(); - return result; - }, - _error: function (value) { - var result = this._state._error(this, value, detailsForError); - this._run(); - return result; - }, - _progress: function (value) { - this._state._progress(this, value); - }, - _setState: function (state) { - this._nextState = state; - }, - _setCompleteValue: function (value) { - this._state._setCompleteValue(this, value); - this._run(); - }, - _setChainedErrorValue: function (value, context) { - var result = this._state._setErrorValue(this, value, detailsForChainedError, context); - this._run(); - return result; - }, - _setExceptionValue: function (value) { - var result = this._state._setErrorValue(this, value, detailsForException); - this._run(); - return result; - }, - _run: function () { - while (this._nextState) { - this._state = this._nextState; - this._nextState = null; - this._state.enter(this); - } - } - }, { - supportedForProcessing: false - }); - - // - // Implementations of shared state machine code. - // - - function completed(promise, value) { - var targetState; - if (value && typeof value === "object" && typeof value.then === "function") { - targetState = state_waiting; - } else { - targetState = state_success_notify; - } - promise._value = value; - promise._setState(targetState); - } - function createErrorDetails(exception, error, promise, id, parent, handler) { - return { - exception: exception, - error: error, - promise: promise, - handler: handler, - id: id, - parent: parent - }; - } - function detailsForHandledError(promise, errorValue, context, handler) { - var exception = context._isException; - var errorId = context._errorId; - return createErrorDetails( - exception ? errorValue : null, - exception ? null : errorValue, - promise, - errorId, - context, - handler - ); - } - function detailsForChainedError(promise, errorValue, context) { - var exception = context._isException; - var errorId = context._errorId; - setErrorInfo(promise, errorId, exception); - return createErrorDetails( - exception ? errorValue : null, - exception ? null : errorValue, - promise, - errorId, - context - ); - } - function detailsForError(promise, errorValue) { - var errorId = ++error_number; - setErrorInfo(promise, errorId); - return createErrorDetails( - null, - errorValue, - promise, - errorId - ); - } - function detailsForException(promise, exceptionValue) { - var errorId = ++error_number; - setErrorInfo(promise, errorId, true); - return createErrorDetails( - exceptionValue, - null, - promise, - errorId - ); - } - function done(promise, onComplete, onError, onProgress) { - var asyncOpID = _Trace._traceAsyncOperationStarting("WinJS.Promise.done"); - pushListener(promise, { c: onComplete, e: onError, p: onProgress, asyncOpID: asyncOpID }); - } - function error(promise, value, onerrorDetails, context) { - promise._value = value; - callonerror(promise, value, onerrorDetails, context); - promise._setState(state_error_notify); - } - function notifySuccess(promise, queue) { - var value = promise._value; - var listeners = promise._listeners; - if (!listeners) { - return; - } - promise._listeners = null; - var i, len; - for (i = 0, len = Array.isArray(listeners) ? listeners.length : 1; i < len; i++) { - var listener = len === 1 ? listeners : listeners[i]; - var onComplete = listener.c; - var target = listener.promise; - - _Trace._traceAsyncOperationCompleted(listener.asyncOpID, _Global.Debug && _Global.Debug.MS_ASYNC_OP_STATUS_SUCCESS); - - if (target) { - _Trace._traceAsyncCallbackStarting(listener.asyncOpID); - try { - target._setCompleteValue(onComplete ? onComplete(value) : value); - } catch (ex) { - target._setExceptionValue(ex); - } finally { - _Trace._traceAsyncCallbackCompleted(); - } - if (target._state !== state_waiting && target._listeners) { - queue.push(target); - } - } else { - CompletePromise.prototype.done.call(promise, onComplete); - } - } - } - function notifyError(promise, queue) { - var value = promise._value; - var listeners = promise._listeners; - if (!listeners) { - return; - } - promise._listeners = null; - var i, len; - for (i = 0, len = Array.isArray(listeners) ? listeners.length : 1; i < len; i++) { - var listener = len === 1 ? listeners : listeners[i]; - var onError = listener.e; - var target = listener.promise; - - var errorID = _Global.Debug && (value && value.name === canceledName ? _Global.Debug.MS_ASYNC_OP_STATUS_CANCELED : _Global.Debug.MS_ASYNC_OP_STATUS_ERROR); - _Trace._traceAsyncOperationCompleted(listener.asyncOpID, errorID); - - if (target) { - var asyncCallbackStarted = false; - try { - if (onError) { - _Trace._traceAsyncCallbackStarting(listener.asyncOpID); - asyncCallbackStarted = true; - if (!onError.handlesOnError) { - callonerror(target, value, detailsForHandledError, promise, onError); - } - target._setCompleteValue(onError(value)); - } else { - target._setChainedErrorValue(value, promise); - } - } catch (ex) { - target._setExceptionValue(ex); - } finally { - if (asyncCallbackStarted) { - _Trace._traceAsyncCallbackCompleted(); - } - } - if (target._state !== state_waiting && target._listeners) { - queue.push(target); - } - } else { - ErrorPromise.prototype.done.call(promise, null, onError); - } - } - } - function callonerror(promise, value, onerrorDetailsGenerator, context, handler) { - if (promiseEventListeners._listeners[errorET]) { - if (value instanceof Error && value.message === canceledName) { - return; - } - promiseEventListeners.dispatchEvent(errorET, onerrorDetailsGenerator(promise, value, context, handler)); - } - } - function progress(promise, value) { - var listeners = promise._listeners; - if (listeners) { - var i, len; - for (i = 0, len = Array.isArray(listeners) ? listeners.length : 1; i < len; i++) { - var listener = len === 1 ? listeners : listeners[i]; - var onProgress = listener.p; - if (onProgress) { - try { onProgress(value); } catch (ex) { } - } - if (!(listener.c || listener.e) && listener.promise) { - listener.promise._progress(value); - } - } - } - } - function pushListener(promise, listener) { - var listeners = promise._listeners; - if (listeners) { - // We may have either a single listener (which will never be wrapped in an array) - // or 2+ listeners (which will be wrapped). Since we are now adding one more listener - // we may have to wrap the single listener before adding the second. - listeners = Array.isArray(listeners) ? listeners : [listeners]; - listeners.push(listener); - } else { - listeners = listener; - } - promise._listeners = listeners; - } - // The difference beween setCompleteValue()/setErrorValue() and complete()/error() is that setXXXValue() moves - // a promise directly to the success/error state without starting another notification pass (because one - // is already ongoing). - function setErrorInfo(promise, errorId, isException) { - promise._isException = isException || false; - promise._errorId = errorId; - } - function setErrorValue(promise, value, onerrorDetails, context) { - promise._value = value; - callonerror(promise, value, onerrorDetails, context); - promise._setState(state_error); - } - function setCompleteValue(promise, value) { - var targetState; - if (value && typeof value === "object" && typeof value.then === "function") { - targetState = state_waiting; - } else { - targetState = state_success; - } - promise._value = value; - promise._setState(targetState); - } - function then(promise, onComplete, onError, onProgress) { - var result = new ThenPromise(promise); - var asyncOpID = _Trace._traceAsyncOperationStarting("WinJS.Promise.then"); - pushListener(promise, { promise: result, c: onComplete, e: onError, p: onProgress, asyncOpID: asyncOpID }); - return result; - } - - // - // Internal implementation detail promise, ThenPromise is created when a promise needs - // to be returned from a then() method. - // - var ThenPromise = _Base.Class.derive(PromiseStateMachine, - function (creator) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.thenPromise))) { - this._stack = Promise._getStack(); - } - - this._creator = creator; - this._setState(state_created); - this._run(); - }, { - _creator: null, - - _cancelAction: function () { if (this._creator) { this._creator.cancel(); } }, - _cleanupAction: function () { this._creator = null; } - }, { - supportedForProcessing: false - } - ); - - // - // Slim promise implementations for already completed promises, these are created - // under the hood on synchronous completion paths as well as by WinJS.Promise.wrap - // and WinJS.Promise.wrapError. - // - - var ErrorPromise = _Base.Class.define( - function ErrorPromise_ctor(value) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.errorPromise))) { - this._stack = Promise._getStack(); - } - - this._value = value; - callonerror(this, value, detailsForError); - }, { - cancel: function () { - /// - /// - /// Attempts to cancel the fulfillment of a promised value. If the promise hasn't - /// already been fulfilled and cancellation is supported, the promise enters - /// the error state with a value of Error("Canceled"). - /// - /// - }, - done: function ErrorPromise_done(unused, onError) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// After the handlers have finished executing, this function throws any error that would have been returned - /// from then() as a promise in the error state. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The fulfilled value is passed as the single argument. If the value is null, - /// the fulfilled value is returned. The value returned - /// from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while executing the function, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function is the fulfilled value of the promise returned by then(). - /// - /// - /// the function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - var value = this._value; - if (onError) { - try { - if (!onError.handlesOnError) { - callonerror(null, value, detailsForHandledError, this, onError); - } - var result = onError(value); - if (result && typeof result === "object" && typeof result.done === "function") { - // If a promise is returned we need to wait on it. - result.done(); - } - return; - } catch (ex) { - value = ex; - } - } - if (value instanceof Error && value.message === canceledName) { - // suppress cancel - return; - } - // force the exception to be thrown asyncronously to avoid any try/catch blocks - // - Promise._doneHandler(value); - }, - then: function ErrorPromise_then(unused, onError) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The value is passed as the single argument. If the value is null, the value is returned. - /// The value returned from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while this function is being executed, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function becomes the fulfilled value of the promise returned by then(). - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// The promise whose value is the result of executing the complete or - /// error function. - /// - /// - - // If the promise is already in a error state and no error handler is provided - // we optimize by simply returning the promise instead of creating a new one. - // - if (!onError) { return this; } - var result; - var value = this._value; - try { - if (!onError.handlesOnError) { - callonerror(null, value, detailsForHandledError, this, onError); - } - result = new CompletePromise(onError(value)); - } catch (ex) { - // If the value throw from the error handler is the same as the value - // provided to the error handler then there is no need for a new promise. - // - if (ex === value) { - result = this; - } else { - result = new ExceptionPromise(ex); - } - } - return result; - } - }, { - supportedForProcessing: false - } - ); - - var ExceptionPromise = _Base.Class.derive(ErrorPromise, - function ExceptionPromise_ctor(value) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.exceptionPromise))) { - this._stack = Promise._getStack(); - } - - this._value = value; - callonerror(this, value, detailsForException); - }, { - /* empty */ - }, { - supportedForProcessing: false - } - ); - - var CompletePromise = _Base.Class.define( - function CompletePromise_ctor(value) { - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.completePromise))) { - this._stack = Promise._getStack(); - } - - if (value && typeof value === "object" && typeof value.then === "function") { - var result = new ThenPromise(null); - result._setCompleteValue(value); - return result; - } - this._value = value; - }, { - cancel: function () { - /// - /// - /// Attempts to cancel the fulfillment of a promised value. If the promise hasn't - /// already been fulfilled and cancellation is supported, the promise enters - /// the error state with a value of Error("Canceled"). - /// - /// - }, - done: function CompletePromise_done(onComplete) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// After the handlers have finished executing, this function throws any error that would have been returned - /// from then() as a promise in the error state. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The fulfilled value is passed as the single argument. If the value is null, - /// the fulfilled value is returned. The value returned - /// from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while executing the function, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function is the fulfilled value of the promise returned by then(). - /// - /// - /// the function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - if (!onComplete) { return; } - try { - var result = onComplete(this._value); - if (result && typeof result === "object" && typeof result.done === "function") { - result.done(); - } - } catch (ex) { - // force the exception to be thrown asynchronously to avoid any try/catch blocks - Promise._doneHandler(ex); - } - }, - then: function CompletePromise_then(onComplete) { - /// - /// - /// Allows you to specify the work to be done on the fulfillment of the promised value, - /// the error handling to be performed if the promise fails to fulfill - /// a value, and the handling of progress notifications along the way. - /// - /// - /// The function to be called if the promise is fulfilled successfully with a value. - /// The value is passed as the single argument. If the value is null, the value is returned. - /// The value returned from the function becomes the fulfilled value of the promise returned by - /// then(). If an exception is thrown while this function is being executed, the promise returned - /// by then() moves into the error state. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. If it is null, the error is forwarded. - /// The value returned from the function becomes the fulfilled value of the promise returned by then(). - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// The promise whose value is the result of executing the complete or - /// error function. - /// - /// - try { - // If the value returned from the completion handler is the same as the value - // provided to the completion handler then there is no need for a new promise. - // - var newValue = onComplete ? onComplete(this._value) : this._value; - return newValue === this._value ? this : new CompletePromise(newValue); - } catch (ex) { - return new ExceptionPromise(ex); - } - } - }, { - supportedForProcessing: false - } - ); - - // - // Promise is the user-creatable WinJS.Promise object. - // - - function timeout(timeoutMS) { - var id; - return new Promise( - function (c) { - if (timeoutMS) { - id = _Global.setTimeout(c, timeoutMS); - } else { - _BaseCoreUtils._setImmediate(c); - } - }, - function () { - if (id) { - _Global.clearTimeout(id); - } - } - ); - } - - function timeoutWithPromise(timeout, promise) { - var cancelPromise = function () { promise.cancel(); }; - var cancelTimeout = function () { timeout.cancel(); }; - timeout.then(cancelPromise); - promise.then(cancelTimeout, cancelTimeout); - return promise; - } - - var staticCanceledPromise; - - var Promise = _Base.Class.derive(PromiseStateMachine, - function Promise_ctor(init, oncancel) { - /// - /// - /// A promise provides a mechanism to schedule work to be done on a value that - /// has not yet been computed. It is a convenient abstraction for managing - /// interactions with asynchronous APIs. - /// - /// - /// The function that is called during construction of the promise. The function - /// is given three arguments (complete, error, progress). Inside this function - /// you should add event listeners for the notifications supported by this value. - /// - /// - /// The function to call if a consumer of this promise wants - /// to cancel its undone work. Promises are not required to - /// support cancellation. - /// - /// - - if (tagWithStack && (tagWithStack === true || (tagWithStack & tag.promise))) { - this._stack = Promise._getStack(); - } - - this._oncancel = oncancel; - this._setState(state_created); - this._run(); - - try { - var complete = this._completed.bind(this); - var error = this._error.bind(this); - var progress = this._progress.bind(this); - init(complete, error, progress); - } catch (ex) { - this._setExceptionValue(ex); - } - }, { - _oncancel: null, - - _cancelAction: function () { - // BEGIN monaco change - try { - if (this._oncancel) { - this._oncancel(); - } else { - throw new Error('Promise did not implement oncancel'); - } - } catch (ex) { - // Access fields to get them created - var msg = ex.message; - var stack = ex.stack; - promiseEventListeners.dispatchEvent('error', ex); - } - // END monaco change - }, - _cleanupAction: function () { this._oncancel = null; } - }, { - - addEventListener: function Promise_addEventListener(eventType, listener, capture) { - /// - /// - /// Adds an event listener to the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to invoke when the event is raised. - /// - /// - /// Specifies whether or not to initiate capture. - /// - /// - promiseEventListeners.addEventListener(eventType, listener, capture); - }, - any: function Promise_any(values) { - /// - /// - /// Returns a promise that is fulfilled when one of the input promises - /// has been fulfilled. - /// - /// - /// An array that contains promise objects or objects whose property - /// values include promise objects. - /// - /// - /// A promise that on fulfillment yields the value of the input (complete or error). - /// - /// - return new Promise( - function (complete, error) { - var keys = Object.keys(values); - if (keys.length === 0) { - complete(); - } - var canceled = 0; - keys.forEach(function (key) { - Promise.as(values[key]).then( - function () { complete({ key: key, value: values[key] }); }, - function (e) { - if (e instanceof Error && e.name === canceledName) { - if ((++canceled) === keys.length) { - complete(Promise.cancel); - } - return; - } - error({ key: key, value: values[key] }); - } - ); - }); - }, - function () { - var keys = Object.keys(values); - keys.forEach(function (key) { - var promise = Promise.as(values[key]); - if (typeof promise.cancel === "function") { - promise.cancel(); - } - }); - } - ); - }, - as: function Promise_as(value) { - /// - /// - /// Returns a promise. If the object is already a promise it is returned; - /// otherwise the object is wrapped in a promise. - /// - /// - /// The value to be treated as a promise. - /// - /// - /// A promise. - /// - /// - if (value && typeof value === "object" && typeof value.then === "function") { - return value; - } - return new CompletePromise(value); - }, - /// - /// Canceled promise value, can be returned from a promise completion handler - /// to indicate cancelation of the promise chain. - /// - cancel: { - get: function () { - return (staticCanceledPromise = staticCanceledPromise || new ErrorPromise(new _ErrorFromName(canceledName))); - } - }, - dispatchEvent: function Promise_dispatchEvent(eventType, details) { - /// - /// - /// Raises an event of the specified type and properties. - /// - /// - /// The type (name) of the event. - /// - /// - /// The set of additional properties to be attached to the event object. - /// - /// - /// Specifies whether preventDefault was called on the event. - /// - /// - return promiseEventListeners.dispatchEvent(eventType, details); - }, - is: function Promise_is(value) { - /// - /// - /// Determines whether a value fulfills the promise contract. - /// - /// - /// A value that may be a promise. - /// - /// - /// true if the specified value is a promise, otherwise false. - /// - /// - return value && typeof value === "object" && typeof value.then === "function"; - }, - join: function Promise_join(values) { - /// - /// - /// Creates a promise that is fulfilled when all the values are fulfilled. - /// - /// - /// An object whose fields contain values, some of which may be promises. - /// - /// - /// A promise whose value is an object with the same field names as those of the object in the values parameter, where - /// each field value is the fulfilled value of a promise. - /// - /// - return new Promise( - function (complete, error, progress) { - var keys = Object.keys(values); - var errors = Array.isArray(values) ? [] : {}; - var results = Array.isArray(values) ? [] : {}; - var undefineds = 0; - var pending = keys.length; - var argDone = function (key) { - if ((--pending) === 0) { - var errorCount = Object.keys(errors).length; - if (errorCount === 0) { - complete(results); - } else { - var canceledCount = 0; - keys.forEach(function (key) { - var e = errors[key]; - if (e instanceof Error && e.name === canceledName) { - canceledCount++; - } - }); - if (canceledCount === errorCount) { - complete(Promise.cancel); - } else { - error(errors); - } - } - } else { - progress({ Key: key, Done: true }); - } - }; - keys.forEach(function (key) { - var value = values[key]; - if (value === undefined) { - undefineds++; - } else { - Promise.then(value, - function (value) { results[key] = value; argDone(key); }, - function (value) { errors[key] = value; argDone(key); } - ); - } - }); - pending -= undefineds; - if (pending === 0) { - complete(results); - return; - } - }, - function () { - Object.keys(values).forEach(function (key) { - var promise = Promise.as(values[key]); - if (typeof promise.cancel === "function") { - promise.cancel(); - } - }); - } - ); - }, - removeEventListener: function Promise_removeEventListener(eventType, listener, capture) { - /// - /// - /// Removes an event listener from the control. - /// - /// - /// The type (name) of the event. - /// - /// - /// The listener to remove. - /// - /// - /// Specifies whether or not to initiate capture. - /// - /// - promiseEventListeners.removeEventListener(eventType, listener, capture); - }, - supportedForProcessing: false, - then: function Promise_then(value, onComplete, onError, onProgress) { - /// - /// - /// A static version of the promise instance method then(). - /// - /// - /// the value to be treated as a promise. - /// - /// - /// The function to be called if the promise is fulfilled with a value. - /// If it is null, the promise simply - /// returns the value. The value is passed as the single argument. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// A promise whose value is the result of executing the provided complete function. - /// - /// - return Promise.as(value).then(onComplete, onError, onProgress); - }, - thenEach: function Promise_thenEach(values, onComplete, onError, onProgress) { - /// - /// - /// Performs an operation on all the input promises and returns a promise - /// that has the shape of the input and contains the result of the operation - /// that has been performed on each input. - /// - /// - /// A set of values (which could be either an array or an object) of which some or all are promises. - /// - /// - /// The function to be called if the promise is fulfilled with a value. - /// If the value is null, the promise returns the value. - /// The value is passed as the single argument. - /// - /// - /// The function to be called if the promise is fulfilled with an error. The error - /// is passed as the single argument. - /// - /// - /// The function to be called if the promise reports progress. Data about the progress - /// is passed as the single argument. Promises are not required to support - /// progress. - /// - /// - /// A promise that is the result of calling Promise.join on the values parameter. - /// - /// - var result = Array.isArray(values) ? [] : {}; - Object.keys(values).forEach(function (key) { - result[key] = Promise.as(values[key]).then(onComplete, onError, onProgress); - }); - return Promise.join(result); - }, - timeout: function Promise_timeout(time, promise) { - /// - /// - /// Creates a promise that is fulfilled after a timeout. - /// - /// - /// The timeout period in milliseconds. If this value is zero or not specified - /// setImmediate is called, otherwise setTimeout is called. - /// - /// - /// A promise that will be canceled if it doesn't complete before the - /// timeout has expired. - /// - /// - /// A promise that is completed asynchronously after the specified timeout. - /// - /// - var to = timeout(time); - return promise ? timeoutWithPromise(to, promise) : to; - }, - wrap: function Promise_wrap(value) { - /// - /// - /// Wraps a non-promise value in a promise. You can use this function if you need - /// to pass a value to a function that requires a promise. - /// - /// - /// Some non-promise value to be wrapped in a promise. - /// - /// - /// A promise that is successfully fulfilled with the specified value - /// - /// - return new CompletePromise(value); - }, - wrapError: function Promise_wrapError(error) { - /// - /// - /// Wraps a non-promise error value in a promise. You can use this function if you need - /// to pass an error to a function that requires a promise. - /// - /// - /// A non-promise error value to be wrapped in a promise. - /// - /// - /// A promise that is in an error state with the specified value. - /// - /// - return new ErrorPromise(error); - }, - - _veryExpensiveTagWithStack: { - get: function () { return tagWithStack; }, - set: function (value) { tagWithStack = value; } - }, - _veryExpensiveTagWithStack_tag: tag, - _getStack: function () { - if (_Global.Debug && _Global.Debug.debuggerEnabled) { - try { throw new Error(); } catch (e) { return e.stack; } - } - }, - - _cancelBlocker: function Promise__cancelBlocker(input, oncancel) { - // - // Returns a promise which on cancelation will still result in downstream cancelation while - // protecting the promise 'input' from being canceled which has the effect of allowing - // 'input' to be shared amoung various consumers. - // - if (!Promise.is(input)) { - return Promise.wrap(input); - } - var complete; - var error; - var output = new Promise( - function (c, e) { - complete = c; - error = e; - }, - function () { - complete = null; - error = null; - oncancel && oncancel(); - } - ); - input.then( - function (v) { complete && complete(v); }, - function (e) { error && error(e); } - ); - return output; - }, - - } - ); - Object.defineProperties(Promise, _Events.createEventProperties(errorET)); - - Promise._doneHandler = function (value) { - _BaseCoreUtils._setImmediate(function Promise_done_rethrow() { - throw value; - }); - }; - - return { - PromiseStateMachine: PromiseStateMachine, - Promise: Promise, - state_created: state_created - }; -}); - -_winjs("WinJS/Promise", ["WinJS/Core/_Base","WinJS/Promise/_StateMachine"], function promiseInit( _Base, _StateMachine) { - "use strict"; - - _Base.Namespace.define("WinJS", { - Promise: _StateMachine.Promise - }); - - return _StateMachine.Promise; -}); - -__winjs_exports = _modules["WinJS/Core/_WinJS"]; -__winjs_exports.TPromise = __winjs_exports.Promise; -__winjs_exports.PPromise = __winjs_exports.Promise; - -// ESM-comment-begin -if (typeof exports === 'undefined' && typeof define === 'function' && define.amd) { - define([], __winjs_exports); -} else { - module.exports = __winjs_exports; -} -// ESM-comment-end - -})(); - -// ESM-uncomment-begin -// export var Promise = __winjs_exports.Promise; -// export var TPromise = __winjs_exports.TPromise; -// export var PPromise = __winjs_exports.PPromise; -// ESM-uncomment-end diff --git a/src/vs/base/common/winjs.polyfill.promise.ts b/src/vs/base/common/winjs.polyfill.promise.ts deleted file mode 100644 index 3b24d0dbf8e..00000000000 --- a/src/vs/base/common/winjs.polyfill.promise.ts +++ /dev/null @@ -1,133 +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 { Promise as WinJSPromise } from './winjs.base'; -import * as platform from 'vs/base/common/platform'; -import { isThenable } from 'vs/base/common/async'; - -function isWinJSPromise(candidate: any): candidate is WinJSPromise { - return isThenable(candidate) && typeof (candidate as any).done === 'function'; -} - -declare class WinJSPromiseRemovals { - any(promises: (T | PromiseLike)[]): WinJSPromise<{ key: string; value: WinJSPromise; }>; -} - -/** - * A polyfill for the native promises. The implementation is based on - * WinJS promises but tries to gap differences between winjs promises - * and native promises. - */ -export class PolyfillPromise implements Promise { - - static all(thenables: Thenable[]): PolyfillPromise { - return new PolyfillPromise(WinJSPromise.join(thenables).then(null, values => { - // WinJSPromise returns a sparse array whereas - // native promises return the *first* error - for (var key in values) { - if (values.hasOwnProperty(key)) { - return values[key]; - } - } - })); - } - - static race(thenables: Thenable[]): PolyfillPromise { - // WinJSPromise returns `{ key: , value: }` - // from the `any` call and Promise.race just wants the value - return new PolyfillPromise((WinJSPromise as any as WinJSPromiseRemovals).any(thenables).then(entry => entry.value, err => err.value)); - } - - static resolve(value): PolyfillPromise { - return new PolyfillPromise(WinJSPromise.wrap(value)); - } - - static reject(value): PolyfillPromise { - return new PolyfillPromise(WinJSPromise.wrapError(value)); - } - - private _winjsPromise: WinJSPromise; - - constructor(winjsPromise: WinJSPromise); - constructor(callback: (resolve: (value?: T) => void, reject: (err?: any) => void) => any); - constructor(initOrPromise: WinJSPromise | ((resolve: (value?: T) => void, reject: (err?: any) => void) => any)) { - - if (isWinJSPromise(initOrPromise)) { - this._winjsPromise = initOrPromise; - } else { - this._winjsPromise = new WinJSPromise((resolve, reject) => { - let initializing = true; - initOrPromise(function (value) { - if (!initializing) { - resolve(value); - } else { - platform.setImmediate(() => resolve(value)); - } - }, function (err) { - if (!initializing) { - reject(err); - } else { - platform.setImmediate(() => reject(err)); - } - }); - initializing = false; - }); - } - } - - then(onFulfilled?: any, onRejected?: any): PolyfillPromise { - let sync = true; - // To support chaining, we need to return the value of the - // onFulfilled and onRejected callback. - // WinJSPromise supports a flat-map style #then, ie. the callbacks - // passed to WinJSPromise#then can return a Promise. - let promise = new PolyfillPromise(this._winjsPromise.then( - onFulfilled && function (value) { - if (!sync) { - return onFulfilled(value); - } else { - return new WinJSPromise((resolve, reject) => { - platform.setImmediate(() => { - let result; - try { - result = onFulfilled(value); - } - catch (err2) { - reject(err2); - return; - } - resolve(result); - }); - }); - } - }, - onRejected && function (err) { - if (!sync) { - return onRejected(err); - } else { - return new WinJSPromise((resolve, reject) => { - platform.setImmediate(() => { - let result; - try { - result = onRejected(err); - } - catch (err2) { - reject(err2); - return; - } - resolve(result); - }); - }); - } - } - )); - sync = false; - return promise; - } - - catch(onRejected?: any): PolyfillPromise { - return this.then(null, onRejected); - } -} diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 48fb1527d34..fa19fff26e9 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -6,15 +6,7 @@ import { transformErrorForSerialization } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; -import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; - -var global: any = self; - -// When missing, polyfill the native promise -// with our winjs-based polyfill -if (typeof global.Promise === 'undefined') { - global.Promise = PolyfillPromise; -} +import { getAllPropertyNames } from 'vs/base/common/types'; const INITIALIZE = '$initialize'; @@ -243,8 +235,8 @@ export class SimpleWorkerClient extends Disposable { lazyProxyReject = reject; this._onModuleLoaded.then((availableMethods: string[]) => { let proxy = {}; - for (let i = 0; i < availableMethods.length; i++) { - (proxy as any)[availableMethods[i]] = createProxyMethod(availableMethods[i], proxyMethodRequest); + for (const methodName of availableMethods) { + (proxy as any)[methodName] = createProxyMethod(methodName, proxyMethodRequest); } resolve(proxy); }, (e) => { @@ -254,11 +246,11 @@ export class SimpleWorkerClient extends Disposable { }); // Create proxy to loaded code - let proxyMethodRequest = (method: string, args: any[]): Promise => { + const proxyMethodRequest = (method: string, args: any[]): Promise => { return this._request(method, args); }; - let createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise): Function => { + const createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise): () => Promise => { return function () { let args = Array.prototype.slice.call(arguments, 0); return proxyMethodRequest(method, args); @@ -333,7 +325,7 @@ export class SimpleWorkerServer { if (this._requestHandler) { // static request handler let methods: string[] = []; - for (let prop in this._requestHandler) { + for (const prop of getAllPropertyNames(this._requestHandler)) { if (typeof this._requestHandler[prop] === 'function') { methods.push(prop); } @@ -369,7 +361,7 @@ export class SimpleWorkerServer { } let methods: string[] = []; - for (let prop in this._requestHandler) { + for (const prop of getAllPropertyNames(this._requestHandler)) { if (typeof this._requestHandler[prop] === 'function') { methods.push(prop); } diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts index a134a70cf79..0b5785377c4 100644 --- a/src/vs/base/node/config.ts +++ b/src/vs/base/node/config.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; -import { dirname, basename } from 'path'; +import { dirname } from 'vs/base/common/path'; import * as objects from 'vs/base/common/objects'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; -import * as extfs from 'vs/base/node/extfs'; -import { isWindows } from 'vs/base/common/platform'; +import { readlink, statLink } from 'vs/base/node/pfs'; +import { watchFolder, watchFile } from 'vs/base/node/watcher'; export interface IConfigurationChangeEvent { config: T; @@ -22,12 +22,11 @@ export interface IConfigWatcher { reload(callback: (config: T) => void): void; getConfig(): T; - getValue(key: string, fallback?: V): V; } export interface IConfigOptions { onError: (error: Error | string) => void; - defaultConfig?: T; + defaultConfig: T; changeBufferDelay?: number; parse?: (content: string, errors: any[]) => T; initCallback?: (config: T) => void; @@ -49,11 +48,9 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { private timeoutHandle: NodeJS.Timer | null; private disposables: IDisposable[]; private readonly _onDidUpdateConfiguration: Emitter>; - private configName: string; - constructor(private _path: string, private options: IConfigOptions = { changeBufferDelay: 0, defaultConfig: Object.create(null), onError: error => console.error(error) }) { + constructor(private _path: string, private options: IConfigOptions = { defaultConfig: Object.create(null), onError: error => console.error(error) }) { this.disposables = []; - this.configName = basename(this._path); this._onDidUpdateConfiguration = new Emitter>(); this.disposables.push(this._onDidUpdateConfiguration); @@ -62,15 +59,15 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { this.initAsync(); } - public get path(): string { + get path(): string { return this._path; } - public get hasParseErrors(): boolean { + get hasParseErrors(): boolean { return this.parseErrors && this.parseErrors.length > 0; } - public get onDidUpdateConfiguration(): Event> { + get onDidUpdateConfiguration(): Event> { return this._onDidUpdateConfiguration.event; } @@ -113,11 +110,11 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { try { this.parseErrors = []; res = this.options.parse ? this.options.parse(raw, this.parseErrors) : json.parse(raw, this.parseErrors); + return res || this.options.defaultConfig; } catch (error) { // Ignore parsing errors + return this.options.defaultConfig; } - - return res || this.options.defaultConfig; } private registerWatcher(): void { @@ -127,60 +124,41 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { this.watch(parentFolder, true); // Check if the path is a symlink and watch its target if so - fs.lstat(this._path, (err, stat) => { - if (err || stat.isDirectory()) { - return; // path is not a valid file - } - - // We found a symlink - if (stat.isSymbolicLink()) { - fs.readlink(this._path, (err, realPath) => { - if (err) { - return; // path is not a valid symlink - } - - this.watch(realPath, false); - }); - } - }); + this.handleSymbolicLink().then(undefined, error => { /* ignore error */ }); } - private watch(path: string, isParentFolder: boolean): void { + private async handleSymbolicLink(): Promise { + const { stat, isSymbolicLink } = await statLink(this._path); + if (isSymbolicLink && !stat.isDirectory()) { + const realPath = await readlink(this._path); + + this.watch(realPath, false); + } + } + + private watch(path: string, isFolder: boolean): void { if (this.disposed) { return; // avoid watchers that will never get disposed by checking for being disposed } - this.disposables.push(extfs.watch(path, - (type, file) => this.onConfigFileChange(type, file, isParentFolder), - (error: string) => this.options.onError(error) - )); + if (isFolder) { + this.disposables.push(watchFolder(path, (type, path) => path === this._path ? this.onConfigFileChange() : undefined, error => this.options.onError(error))); + } else { + this.disposables.push(watchFile(path, (type, path) => this.onConfigFileChange(), error => this.options.onError(error))); + } } - private onConfigFileChange(eventType: string, filename: string, isParentFolder: boolean): void { - if (isParentFolder) { - - // Windows: in some cases the filename contains artifacts from the absolute path - // see https://github.com/nodejs/node/issues/19170 - // As such, we have to ensure that the filename basename is used for comparison. - if (isWindows && filename && filename !== this.configName) { - filename = basename(filename); - } - - if (filename !== this.configName) { - return; // a change to a sibling file that is not our config file - } - } - + private onConfigFileChange(): void { if (this.timeoutHandle) { global.clearTimeout(this.timeoutHandle); this.timeoutHandle = null; } // we can get multiple change events for one change, so we buffer through a timeout - this.timeoutHandle = global.setTimeout(() => this.reload(), this.options.changeBufferDelay); + this.timeoutHandle = global.setTimeout(() => this.reload(), this.options.changeBufferDelay || 0); } - public reload(callback?: (config: T) => void): void { + reload(callback?: (config: T) => void): void { this.loadAsync(currentConfig => { if (!objects.equals(currentConfig, this.cache)) { this.updateCache(currentConfig); @@ -194,31 +172,19 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { }); } - public getConfig(): T { + getConfig(): T { this.ensureLoaded(); return this.cache; } - public getValue(key: string, fallback?: V): V { - this.ensureLoaded(); - - if (!key) { - return fallback; - } - - const value = this.cache ? (this.cache as any)[key] : void 0; - - return typeof value !== 'undefined' ? value : fallback; - } - private ensureLoaded(): void { if (!this.loaded) { this.updateCache(this.loadSync()); } } - public dispose(): void { + dispose(): void { this.disposed = true; this.disposables = dispose(this.disposables); } diff --git a/src/vs/base/node/cpuUsage.sh b/src/vs/base/node/cpuUsage.sh index 94d56de9899..3d42b36dc20 100755 --- a/src/vs/base/node/cpuUsage.sh +++ b/src/vs/base/node/cpuUsage.sh @@ -56,7 +56,7 @@ for PID in "$@"; do PROCESS_TIME_BEFORE=${PROCESS_BEFORE_TIMES[$ITER]} let PROCESS_DELTA=$PROCESS_TIME_AFTER-$PROCESS_TIME_BEFORE let TOTAL_DELTA=$TOTAL_TIME_AFTER-$TOTAL_TIME_BEFORE - CPU_USAGE=`echo "100*$PROCESS_DELTA/$TOTAL_DELTA" | bc -l` + CPU_USAGE=`echo "$((100*$PROCESS_DELTA/$TOTAL_DELTA))"` # Parent script reads from stdout, so echo result to be read echo $CPU_USAGE diff --git a/src/vs/base/node/crypto.ts b/src/vs/base/node/crypto.ts index 8d3dc0e63a2..f18503ab882 100644 --- a/src/vs/base/node/crypto.ts +++ b/src/vs/base/node/crypto.ts @@ -8,7 +8,7 @@ import * as crypto from 'crypto'; import * as stream from 'stream'; import { once } from 'vs/base/common/functional'; -export function checksum(path: string, sha1hash: string): Promise { +export function checksum(path: string, sha1hash: string | undefined): Promise { const promise = new Promise((c, e) => { const input = fs.createReadStream(path); const hash = crypto.createHash('sha1'); diff --git a/src/vs/base/node/decoder.ts b/src/vs/base/node/decoder.ts index 5ef4abf19f7..5146d0e24de 100644 --- a/src/vs/base/node/decoder.ts +++ b/src/vs/base/node/decoder.ts @@ -23,9 +23,9 @@ export class LineDecoder { this.remaining = null; } - public write(buffer: Buffer): string[] { - let result: string[] = []; - let value = this.remaining + write(buffer: Buffer): string[] { + const result: string[] = []; + const value = this.remaining ? this.remaining + this.stringDecoder.write(buffer) : this.stringDecoder.write(buffer); @@ -41,7 +41,7 @@ export class LineDecoder { result.push(value.substring(start, idx)); idx++; if (idx < value.length) { - let lastChar = ch; + const lastChar = ch; ch = value.charCodeAt(idx); if ((lastChar === CharCode.CarriageReturn && ch === CharCode.LineFeed) || (lastChar === CharCode.LineFeed && ch === CharCode.CarriageReturn)) { idx++; @@ -56,7 +56,7 @@ export class LineDecoder { return result; } - public end(): string | null { + end(): string | null { return this.remaining; } } \ No newline at end of file diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 516afbc93cc..9b04d9f0a96 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -3,122 +3,132 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as stream from 'vs/base/node/stream'; import * as iconv from 'iconv-lite'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { exec } from 'child_process'; -import { Readable, Writable, WritableOptions } from 'stream'; +import { Readable, Writable } from 'stream'; +import { VSBuffer } from 'vs/base/common/buffer'; export const UTF8 = 'utf8'; export const UTF8_with_bom = 'utf8bom'; export const UTF16be = 'utf16be'; export const UTF16le = 'utf16le'; +export const UTF16be_BOM = [0xFE, 0xFF]; +export const UTF16le_BOM = [0xFF, 0xFE]; +export const UTF8_BOM = [0xEF, 0xBB, 0xBF]; + +const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not +const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, small number of bytes are enough +const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing + export interface IDecodeStreamOptions { - guessEncoding?: boolean; + guessEncoding: boolean; minBytesRequiredForDetection?: number; - overwriteEncoding?(detectedEncoding: string | null): string; + + overwriteEncoding(detectedEncoding: string | null): string; } -export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise<{ detected: IDetectedEncodingResult, stream: NodeJS.ReadableStream }> { +export interface IDecodeStreamResult { + stream: NodeJS.ReadableStream; + detected: IDetectedEncodingResult; +} + +export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise { if (!options.minBytesRequiredForDetection) { options.minBytesRequiredForDetection = options.guessEncoding ? AUTO_GUESS_BUFFER_MAX_LEN : NO_GUESS_BUFFER_MAX_LEN; } - if (!options.overwriteEncoding) { - options.overwriteEncoding = detected => detected || UTF8; - } + return new Promise((resolve, reject) => { + const writer = new class extends Writable { + private decodeStream: NodeJS.ReadWriteStream; + private decodeStreamPromise: Promise; - return new Promise<{ detected: IDetectedEncodingResult, stream: NodeJS.ReadableStream }>((resolve, reject) => { + private bufferedChunks: Buffer[] = []; + private bytesBuffered = 0; - readable.on('error', reject); - - readable.pipe(new class extends Writable { - - private _decodeStream: NodeJS.ReadWriteStream; - private _decodeStreamConstruction: Thenable; - private _buffer: Buffer[] = []; - private _bytesBuffered = 0; - - constructor(opts?: WritableOptions) { - super(opts); - this.once('finish', () => this._finish()); - } - - _write(chunk: any, encoding: string, callback: Function): void { + _write(chunk: Buffer, encoding: string, callback: (error: Error | null) => void): void { if (!Buffer.isBuffer(chunk)) { - callback(new Error('data must be a buffer')); + return callback(new Error('toDecodeStream(): data must be a buffer')); } - if (this._decodeStream) { - // just a forwarder now - this._decodeStream.write(chunk, callback); + // if the decode stream is ready, we just write directly + if (this.decodeStream) { + this.decodeStream.write(chunk, callback); + return; } - this._buffer.push(chunk); - this._bytesBuffered += chunk.length; + // otherwise we need to buffer the data until the stream is ready + this.bufferedChunks.push(chunk); + this.bytesBuffered += chunk.byteLength; - if (this._decodeStreamConstruction) { - // waiting for the decoder to be ready - this._decodeStreamConstruction.then(_ => callback(), err => callback(err)); + // waiting for the decoder to be ready + if (this.decodeStreamPromise) { + this.decodeStreamPromise.then(() => callback(null), error => callback(error)); + } - } else if (typeof options.minBytesRequiredForDetection === 'number' && this._bytesBuffered >= options.minBytesRequiredForDetection) { - // buffered enough data, create stream and forward data + // buffered enough data for encoding detection, create stream and forward data + else if (typeof options.minBytesRequiredForDetection === 'number' && this.bytesBuffered >= options.minBytesRequiredForDetection) { this._startDecodeStream(callback); + } - } else { - // only buffering - callback(); + // only buffering until enough data for encoding detection is there + else { + callback(null); } } - _startDecodeStream(callback: Function): void { + _startDecodeStream(callback: (error: Error | null) => void): void { - this._decodeStreamConstruction = Promise.resolve(detectEncodingFromBuffer({ - buffer: Buffer.concat(this._buffer), bytesRead: this._bytesBuffered + // detect encoding from buffer + this.decodeStreamPromise = Promise.resolve(detectEncodingFromBuffer({ + buffer: Buffer.concat(this.bufferedChunks), + bytesRead: this.bytesBuffered }, options.guessEncoding)).then(detected => { - if (options.overwriteEncoding) { - detected.encoding = options.overwriteEncoding(detected.encoding); - } - this._decodeStream = decodeStream(detected.encoding); - for (const buffer of this._buffer) { - this._decodeStream.write(buffer); - } - callback(); - resolve({ detected, stream: this._decodeStream }); - }, err => { - this.emit('error', err); - callback(err); + // ensure to respect overwrite of encoding + detected.encoding = options.overwriteEncoding(detected.encoding); + + // decode and write buffer + this.decodeStream = decodeStream(detected.encoding); + this.decodeStream.write(Buffer.concat(this.bufferedChunks), callback); + this.bufferedChunks.length = 0; + + // signal to the outside our detected encoding + // and final decoder stream + resolve({ detected, stream: this.decodeStream }); + }, error => { + this.emit('error', error); + + callback(error); }); } - _finish(): void { - if (this._decodeStream) { - // normal finish - this._decodeStream.end(); - } else { - // we were still waiting for data... - this._startDecodeStream(() => this._decodeStream.end()); + _final(callback: (error: Error | null) => void) { + + // normal finish + if (this.decodeStream) { + this.decodeStream.end(callback); + } + + // we were still waiting for data to do the encoding + // detection. thus, wrap up starting the stream even + // without all the data to get things going + else { + this._startDecodeStream(() => this.decodeStream.end(callback)); } } - }); + }; + + // errors + readable.on('error', reject); + + // pipe through + readable.pipe(writer); }); } -export function bomLength(encoding: string): number { - switch (encoding) { - case UTF8: - return 3; - case UTF16be: - case UTF16le: - return 2; - } - - return 0; -} - export function decode(buffer: Buffer, encoding: string): string { return iconv.decode(buffer, toNodeEncoding(encoding)); } @@ -131,7 +141,7 @@ export function encodingExists(encoding: string): boolean { return iconv.encodingExists(toNodeEncoding(encoding)); } -export function decodeStream(encoding: string | null): NodeJS.ReadWriteStream { +function decodeStream(encoding: string | null): NodeJS.ReadWriteStream { return iconv.decodeStream(toNodeEncoding(encoding)); } @@ -147,8 +157,8 @@ function toNodeEncoding(enc: string | null): string { return enc; } -export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead: number): string | null { - if (!buffer || bytesRead < 2) { +export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): string | null { + if (!buffer || bytesRead < UTF16be_BOM.length) { return null; } @@ -156,62 +166,54 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead: const b1 = buffer.readUInt8(1); // UTF-16 BE - if (b0 === 0xFE && b1 === 0xFF) { + if (b0 === UTF16be_BOM[0] && b1 === UTF16be_BOM[1]) { return UTF16be; } // UTF-16 LE - if (b0 === 0xFF && b1 === 0xFE) { + if (b0 === UTF16le_BOM[0] && b1 === UTF16le_BOM[1]) { return UTF16le; } - if (bytesRead < 3) { + if (bytesRead < UTF8_BOM.length) { return null; } const b2 = buffer.readUInt8(2); // UTF-8 - if (b0 === 0xEF && b1 === 0xBB && b2 === 0xBF) { + if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) { return UTF8; } return null; } -/** - * Detects the Byte Order Mark in a given file. - * If no BOM is detected, null will be passed to callback. - */ -export function detectEncodingByBOM(file: string): Promise { - return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead)); -} - const MINIMUM_THRESHOLD = 0.2; const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; /** * Guesses the encoding from buffer. */ -export function guessEncodingByBuffer(buffer: Buffer): Promise { - return import('jschardet').then(jschardet => { - jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; +async function guessEncodingByBuffer(buffer: Buffer): Promise { + const jschardet = await import('jschardet'); - const guessed = jschardet.detect(buffer); - if (!guessed || !guessed.encoding) { - return null; - } + jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; - const enc = guessed.encoding.toLowerCase(); + const guessed = jschardet.detect(buffer); + if (!guessed || !guessed.encoding) { + return null; + } - // Ignore encodings that cannot guess correctly - // (http://chardet.readthedocs.io/en/latest/supported-encodings.html) - if (0 <= IGNORE_ENCODINGS.indexOf(enc)) { - return null; - } + const enc = guessed.encoding.toLowerCase(); - return toIconvLiteEncoding(guessed.encoding); - }); + // Ignore encodings that cannot guess correctly + // (http://chardet.readthedocs.io/en/latest/supported-encodings.html) + if (0 <= IGNORE_ENCODINGS.indexOf(enc)) { + return null; + } + + return toIconvLiteEncoding(guessed.encoding); } const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { @@ -263,18 +265,19 @@ export function toCanonicalName(enc: string): string { } } -const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not -const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, small number of bytes are enough -const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing - export interface IDetectedEncodingResult { encoding: string | null; seemsBinary: boolean; } -export function detectEncodingFromBuffer(readResult: stream.ReadResult, autoGuessEncoding?: false): IDetectedEncodingResult; -export function detectEncodingFromBuffer(readResult: stream.ReadResult, autoGuessEncoding?: boolean): Promise; -export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResult, autoGuessEncoding?: boolean): Promise | IDetectedEncodingResult { +export interface IReadResult { + buffer: Buffer | null; + bytesRead: number; +} + +export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: false): IDetectedEncodingResult; +export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: boolean): Promise; +export function detectEncodingFromBuffer({ buffer, bytesRead }: IReadResult, autoGuessEncoding?: boolean): Promise | IDetectedEncodingResult { // Always first check for BOM to find out about encoding let encoding = detectEncodingByBOMFromBuffer(buffer, bytesRead); @@ -359,7 +362,7 @@ const windowsTerminalEncodings = { '1252': 'cp1252' // West European Latin }; -export function resolveTerminalEncoding(verbose?: boolean): Promise { +export async function resolveTerminalEncoding(verbose?: boolean): Promise { let rawEncodingPromise: Promise; // Support a global environment variable to win over other mechanics @@ -393,37 +396,35 @@ export function resolveTerminalEncoding(verbose?: boolean): Promise { exec('chcp', (err, stdout, stderr) => { if (stdout) { const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings); - for (let i = 0; i < windowsTerminalEncodingKeys.length; i++) { - const key = windowsTerminalEncodingKeys[i]; + for (const key of windowsTerminalEncodingKeys) { if (stdout.indexOf(key) >= 0) { return resolve(windowsTerminalEncodings[key]); } } } - return resolve(void 0); + return resolve(undefined); }); }); } - return rawEncodingPromise.then(rawEncoding => { - if (verbose) { - console.log(`Detected raw terminal encoding: ${rawEncoding}`); - } - - if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) { - return UTF8; - } - - const iconvEncoding = toIconvLiteEncoding(rawEncoding); - if (iconv.encodingExists(iconvEncoding)) { - return iconvEncoding; - } - - if (verbose) { - console.log('Unsupported terminal encoding, falling back to UTF-8.'); - } + const rawEncoding = await rawEncodingPromise; + if (verbose) { + console.log(`Detected raw terminal encoding: ${rawEncoding}`); + } + if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) { return UTF8; - }); + } + + const iconvEncoding = toIconvLiteEncoding(rawEncoding); + if (iconv.encodingExists(iconvEncoding)) { + return iconvEncoding; + } + + if (verbose) { + console.log('Unsupported terminal encoding, falling back to UTF-8.'); + } + + return UTF8; } diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts deleted file mode 100644 index 3cc6f7cbefc..00000000000 --- a/src/vs/base/node/extfs.ts +++ /dev/null @@ -1,709 +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 fs from 'fs'; -import * as paths from 'path'; -import { nfcall } from 'vs/base/common/async'; -import { normalizeNFC } from 'vs/base/common/normalization'; -import * as platform from 'vs/base/common/platform'; -import * as strings from 'vs/base/common/strings'; -import * as uuid from 'vs/base/common/uuid'; -import { encode, encodeStream } from 'vs/base/node/encoding'; -import * as flow from 'vs/base/node/flow'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; - -const loop = flow.loop; - -export function readdirSync(path: string): string[] { - // Mac: uses NFD unicode form on disk, but we want NFC - // See also https://github.com/nodejs/node/issues/2165 - if (platform.isMacintosh) { - return fs.readdirSync(path).map(c => normalizeNFC(c)); - } - - return fs.readdirSync(path); -} - -export function readdir(path: string, callback: (error: Error | null, files: string[]) => void): void { - // Mac: uses NFD unicode form on disk, but we want NFC - // See also https://github.com/nodejs/node/issues/2165 - if (platform.isMacintosh) { - return fs.readdir(path, (error, children) => { - if (error) { - return callback(error, []); - } - - return callback(null, children.map(c => normalizeNFC(c))); - }); - } - - return fs.readdir(path, callback); -} - -export interface IStatAndLink { - stat: fs.Stats; - isSymbolicLink: boolean; -} - -export function statLink(path: string, callback: (error: Error | null, statAndIsLink: IStatAndLink | null) => void): void { - fs.lstat(path, (error, lstat) => { - if (error || lstat.isSymbolicLink()) { - fs.stat(path, (error, stat) => { - if (error) { - return callback(error, null); - } - - callback(null, { stat, isSymbolicLink: lstat && lstat.isSymbolicLink() }); - }); - } else { - callback(null, { stat: lstat, isSymbolicLink: false }); - } - }); -} - -export function copy(source: string, target: string, callback: (error: Error | null) => void, copiedSourcesIn?: { [path: string]: boolean }): void { - const copiedSources = copiedSourcesIn ? copiedSourcesIn : Object.create(null); - - fs.stat(source, (error, stat) => { - if (error) { - return callback(error); - } - - if (!stat.isDirectory()) { - return doCopyFile(source, target, stat.mode & 511, callback); - } - - if (copiedSources[source]) { - return callback(null); // escape when there are cycles (can happen with symlinks) - } - - copiedSources[source] = true; // remember as copied - - const proceed = function () { - readdir(source, (err, files) => { - loop(files, (file: string, clb: (error: Error | null, result: string[]) => void) => { - copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, []), copiedSources); - }, callback); - }); - }; - - mkdirp(target, stat.mode & 511).then(proceed, proceed); - }); -} - -function doCopyFile(source: string, target: string, mode: number, callback: (error: Error) => void): void { - const reader = fs.createReadStream(source); - const writer = fs.createWriteStream(target, { mode }); - - let finished = false; - const finish = (error?: Error) => { - if (!finished) { - finished = true; - - // in error cases, pass to callback - if (error) { - callback(error); - } - - // we need to explicitly chmod because of https://github.com/nodejs/node/issues/1104 - else { - fs.chmod(target, mode, callback); - } - } - }; - - // handle errors properly - reader.once('error', error => finish(error)); - writer.once('error', error => finish(error)); - - // we are done (underlying fd has been closed) - writer.once('close', () => finish()); - - // start piping - reader.pipe(writer); -} - -export function mkdirp(path: string, mode?: number, token?: CancellationToken): TPromise { - const mkdir = (): Promise => { - return nfcall(fs.mkdir, path, mode).then(null, (mkdirErr: NodeJS.ErrnoException) => { - - // ENOENT: a parent folder does not exist yet - if (mkdirErr.code === 'ENOENT') { - return TPromise.wrapError(mkdirErr); - } - - // Any other error: check if folder exists and - // return normally in that case if its a folder - return nfcall(fs.stat, path).then((stat: fs.Stats) => { - if (!stat.isDirectory()) { - return TPromise.wrapError(new Error(`'${path}' exists and is not a directory.`)); - } - - return null; - }, statErr => { - return TPromise.wrapError(mkdirErr); // bubble up original mkdir error - }); - }); - }; - - // stop at root - if (path === paths.dirname(path)) { - return TPromise.as(true); - } - - // 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') { - return mkdirp(paths.dirname(path), mode).then(mkdir); - } - - // Any other error - return TPromise.wrapError(err); - }); -} - -// Deletes the given path by first moving it out of the workspace. This has two benefits. For one, the operation can return fast because -// after the rename, the contents are out of the workspace although not yet deleted. The greater benefit however is that this operation -// will fail in case any file is used by another process. fs.unlink() in node will not bail if a file unlinked is used by another process. -// However, the consequences are bad as outlined in all the related bugs from https://github.com/joyent/node/issues/7164 -export function del(path: string, tmpFolder: string, callback: (error: Error | null) => void, done?: (error: Error | null) => void): void { - fs.exists(path, exists => { - if (!exists) { - return callback(null); - } - - fs.stat(path, (err, stat) => { - if (err || !stat) { - return callback(err); - } - - // Special windows workaround: A file or folder that ends with a "." cannot be moved to another place - // because it is not a valid file name. In this case, we really have to do the deletion without prior move. - if (path[path.length - 1] === '.' || strings.endsWith(path, './') || strings.endsWith(path, '.\\')) { - return rmRecursive(path, callback); - } - - const pathInTemp = paths.join(tmpFolder, uuid.generateUuid()); - fs.rename(path, pathInTemp, (error: Error | null) => { - if (error) { - return rmRecursive(path, callback); // if rename fails, delete without tmp dir - } - - // Return early since the move succeeded - callback(null); - - // do the heavy deletion outside the callers callback - rmRecursive(pathInTemp, error => { - if (error) { - console.error(error); - } - - if (done) { - done(error); - } - }); - }); - }); - }); -} - -function rmRecursive(path: string, callback: (error: Error | null) => void): void { - if (path === '\\' || path === '/') { - return callback(new Error('Will not delete root!')); - } - - fs.exists(path, exists => { - if (!exists) { - callback(null); - } else { - fs.lstat(path, (err, stat) => { - if (err || !stat) { - callback(err); - } else if (!stat.isDirectory() || stat.isSymbolicLink() /* !!! never recurse into links when deleting !!! */) { - const mode = stat.mode; - if (!(mode & 128)) { // 128 === 0200 - fs.chmod(path, mode | 128, (err: Error) => { // 128 === 0200 - if (err) { - callback(err); - } else { - fs.unlink(path, callback); - } - }); - } else { - fs.unlink(path, callback); - } - } else { - readdir(path, (err, children) => { - if (err || !children) { - callback(err); - } else if (children.length === 0) { - fs.rmdir(path, callback); - } else { - let firstError: Error | null = null; - let childrenLeft = children.length; - children.forEach(child => { - rmRecursive(paths.join(path, child), (err: Error) => { - childrenLeft--; - if (err) { - firstError = firstError || err; - } - - if (childrenLeft === 0) { - if (firstError) { - callback(firstError); - } else { - fs.rmdir(path, callback); - } - } - }); - }); - } - }); - } - }); - } - }); -} - -export function delSync(path: string): void { - try { - const stat = fs.lstatSync(path); - if (stat.isDirectory() && !stat.isSymbolicLink()) { - readdirSync(path).forEach(child => delSync(paths.join(path, child))); - fs.rmdirSync(path); - } else { - fs.unlinkSync(path); - } - } catch (err) { - if (err.code === 'ENOENT') { - return; // not found - } - - throw err; - } -} - -export function mv(source: string, target: string, callback: (error: Error | null) => void): void { - if (source === target) { - return callback(null); - } - - function updateMtime(err: Error | null): void { - if (err) { - return callback(err); - } - - fs.stat(target, (error, stat) => { - if (error) { - return callback(error); - } - - if (stat.isDirectory()) { - return callback(null); - } - - fs.open(target, 'a', null, (err: Error, fd: number) => { - if (err) { - return callback(err); - } - - fs.futimes(fd, stat.atime, new Date(), (err: Error) => { - if (err) { - return callback(err); - } - - fs.close(fd, callback); - }); - }); - }); - } - - // Try native rename() - fs.rename(source, target, (err: Error) => { - if (!err) { - return updateMtime(null); - } - - // In two cases we fallback to classic copy and delete: - // - // 1.) The EXDEV error indicates that source and target are on different devices - // In this case, fallback to using a copy() operation as there is no way to - // rename() between different devices. - // - // 2.) The user tries to rename a file/folder that ends with a dot. This is not - // really possible to move then, at least on UNC devices. - if (err && source.toLowerCase() !== target.toLowerCase() && ((err).code === 'EXDEV') || strings.endsWith(source, '.')) { - return copy(source, target, (err: Error) => { - if (err) { - return callback(err); - } - - rmRecursive(source, updateMtime); - }); - } - - return callback(err); - }); -} - -export interface IWriteFileOptions { - mode?: number; - flag?: string; - encoding?: { - charset: string; - addBOM: boolean; - }; -} - -let canFlush = true; -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)) { - doWriteFileAndFlush(path, data, options, callback); - } else { - doWriteFileStreamAndFlush(path, data, options, callback); - } -} - -function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IWriteFileOptions, callback: (error?: Error) => void): void { - - // finish only once - let finished = false; - const finish = (error?: Error) => { - if (!finished) { - finished = true; - - // in error cases we need to manually close streams - // if the write stream was successfully opened - if (error) { - if (isOpen) { - writer.once('close', () => callback(error)); - writer.close(); - } else { - callback(error); - } - } - - // otherwise just return without error - else { - callback(); - } - } - }; - - // create writer to target. we set autoClose: false because we want to use the streams - // file descriptor to call fs.fdatasync to ensure the data is flushed to disk - const writer = fs.createWriteStream(path, { mode: options.mode, flags: options.flag, autoClose: false }); - - // Event: 'open' - // Purpose: save the fd for later use and start piping - // Notes: will not be called when there is an error opening the file descriptor! - let fd: number; - let isOpen: boolean; - writer.once('open', descriptor => { - fd = descriptor; - isOpen = true; - - // if an encoding is provided, we need to pipe the stream through - // an encoder stream and forward the encoding related options - if (options.encoding) { - reader = reader.pipe(encodeStream(options.encoding.charset, { addBOM: options.encoding.addBOM })); - } - - // start data piping only when we got a successful open. this ensures that we do - // not consume the stream when an error happens and helps to fix this issue: - // https://github.com/Microsoft/vscode/issues/42542 - reader.pipe(writer); - }); - - // Event: 'error' - // Purpose: to return the error to the outside and to close the write stream (does not happen automatically) - reader.once('error', error => finish(error)); - writer.once('error', error => finish(error)); - - // Event: 'finish' - // Purpose: use fs.fdatasync to flush the contents to disk - // Notes: event is called when the writer has finished writing to the underlying resource. we must call writer.close() - // because we have created the WriteStream with autoClose: false - writer.once('finish', () => { - - // flush to disk - if (canFlush && isOpen) { - fs.fdatasync(fd, (syncError: Error) => { - - // In some exotic setups it is well possible that node fails to sync - // In that case we disable flushing and warn to the console - if (syncError) { - console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError); - canFlush = false; - } - - writer.close(); - }); - } else { - writer.close(); - } - }); - - // Event: 'close' - // Purpose: signal we are done to the outside - // Notes: event is called when the writer's filedescriptor is closed - writer.once('close', () => finish()); -} - -// Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk -// We do this in cases where we want to make sure the data is really on disk and -// not in some cache. -// -// See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194 -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 }); - } - - if (!canFlush) { - return fs.writeFile(path, data, { mode: options.mode, flag: options.flag }, callback); - } - - // Open the file with same flags and mode as fs.writeFile() - fs.open(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode, (openError, fd) => { - if (openError) { - return callback(openError); - } - - // It is valid to pass a fd handle to fs.writeFile() and this will keep the handle open! - fs.writeFile(fd, data, writeError => { - if (writeError) { - return fs.close(fd, () => callback(writeError)); // still need to close the handle on error! - } - - // Flush contents (not metadata) of the file to disk - fs.fdatasync(fd, (syncError: Error) => { - - // In some exotic setups it is well possible that node fails to sync - // In that case we disable flushing and warn to the console - if (syncError) { - console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError); - canFlush = false; - } - - return fs.close(fd, closeError => callback(closeError)); - }); - }); - }); -} - -export function writeFileAndFlushSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void { - options = ensureOptions(options); - - if (options.encoding) { - data = encode(data, options.encoding.charset, { addBOM: options.encoding.addBOM }); - } - - if (!canFlush) { - return fs.writeFileSync(path, data, { mode: options.mode, flag: options.flag }); - } - - // Open the file with same flags and mode as fs.writeFile() - const fd = fs.openSync(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode); - - try { - - // It is valid to pass a fd handle to fs.writeFile() and this will keep the handle open! - fs.writeFileSync(fd, data); - - // Flush contents (not metadata) of the file to disk - try { - fs.fdatasyncSync(fd); - } catch (syncError) { - console.warn('[node.js fs] fdatasyncSync is now disabled for this session because it failed: ', syncError); - canFlush = false; - } - } finally { - fs.closeSync(fd); - } -} - -function ensureOptions(options?: IWriteFileOptions): IWriteFileOptions { - if (!options) { - return { mode: 0o666, flag: 'w' }; - } - - const ensuredOptions: IWriteFileOptions = { mode: options.mode, flag: options.flag, encoding: options.encoding }; - - if (typeof ensuredOptions.mode !== 'number') { - ensuredOptions.mode = 0o666; - } - - if (typeof ensuredOptions.flag !== 'string') { - ensuredOptions.flag = 'w'; - } - - return ensuredOptions; -} - -/** - * Copied from: https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83 - * - * Given an absolute, normalized, and existing file path 'realcase' returns the exact path that the file has on disk. - * On a case insensitive file system, the returned path might differ from the original path by character casing. - * On a case sensitive file system, the returned path will always be identical to the original path. - * In case of errors, null is returned. But you cannot use this function to verify that a path exists. - * realcaseSync does not handle '..' or '.' path segments and it does not take the locale into account. - */ -export function realcaseSync(path: string): string | null { - const dir = paths.dirname(path); - if (path === dir) { // end recursion - return path; - } - - const name = (paths.basename(path) /* can be '' for windows drive letters */ || path).toLowerCase(); - try { - const entries = readdirSync(dir); - const found = entries.filter(e => e.toLowerCase() === name); // use a case insensitive search - if (found.length === 1) { - // on a case sensitive filesystem we cannot determine here, whether the file exists or not, hence we need the 'file exists' precondition - const prefix = realcaseSync(dir); // recurse - if (prefix) { - return paths.join(prefix, found[0]); - } - } else if (found.length > 1) { - // must be a case sensitive $filesystem - const ix = found.indexOf(name); - if (ix >= 0) { // case sensitive - const prefix = realcaseSync(dir); // recurse - if (prefix) { - return paths.join(prefix, found[ix]); - } - } - } - } catch (error) { - // silently ignore error - } - - return null; -} - -export function realpathSync(path: string): string { - try { - return fs.realpathSync(path); - } catch (error) { - - // We hit an error calling fs.realpathSync(). Since fs.realpathSync() is doing some path normalization - // we now do a similar normalization and then try again if we can access the path with read - // permissions at least. If that succeeds, we return that path. - // fs.realpath() is resolving symlinks and that can fail in certain cases. The workaround is - // to not resolve links but to simply see if the path is read accessible or not. - const normalizedPath = normalizePath(path); - fs.accessSync(normalizedPath, fs.constants.R_OK); // throws in case of an error - - return normalizedPath; - } -} - -export function realpath(path: string, callback: (error: Error | null, realpath: string) => void): void { - return fs.realpath(path, (error, realpath) => { - if (!error) { - return callback(null, realpath); - } - - // We hit an error calling fs.realpath(). Since fs.realpath() is doing some path normalization - // we now do a similar normalization and then try again if we can access the path with read - // permissions at least. If that succeeds, we return that path. - // fs.realpath() is resolving symlinks and that can fail in certain cases. The workaround is - // to not resolve links but to simply see if the path is read accessible or not. - const normalizedPath = normalizePath(path); - - return fs.access(normalizedPath, fs.constants.R_OK, error => { - return callback(error, normalizedPath); - }); - }); -} - -function normalizePath(path: string): string { - return strings.rtrim(paths.normalize(path), paths.sep); -} - -export function watch(path: string, onChange: (type: string, path?: string) => void, onError: (error: string) => void): IDisposable { - try { - const watcher = fs.watch(path); - - watcher.on('change', (type, raw) => { - let file: string | undefined; - if (raw) { // https://github.com/Microsoft/vscode/issues/38191 - file = raw.toString(); - if (platform.isMacintosh) { - // Mac: uses NFD unicode form on disk, but we want NFC - // See also https://github.com/nodejs/node/issues/2165 - file = normalizeNFC(file); - } - } - - onChange(type, file); - }); - - watcher.on('error', (code: number, signal: string) => onError(`Failed to watch ${path} for changes (${code}, ${signal})`)); - - return toDisposable(() => { - watcher.removeAllListeners(); - watcher.close(); - }); - } catch (error) { - fs.exists(path, exists => { - if (exists) { - onError(`Failed to watch ${path} for changes (${error.toString()})`); - } - }); - } - - return Disposable.None; -} - -export function sanitizeFilePath(candidate: string, cwd: string): string { - - // Special case: allow to open a drive letter without trailing backslash - if (platform.isWindows && strings.endsWith(candidate, ':')) { - candidate += paths.sep; - } - - // Ensure absolute - if (!paths.isAbsolute(candidate)) { - candidate = paths.join(cwd, candidate); - } - - // Ensure normalized - candidate = paths.normalize(candidate); - - // Ensure no trailing slash/backslash - if (platform.isWindows) { - candidate = strings.rtrim(candidate, paths.sep); - - // Special case: allow to open drive root ('C:\') - if (strings.endsWith(candidate, ':')) { - candidate += paths.sep; - } - - } else { - candidate = strings.rtrim(candidate, paths.sep); - - // Special case: allow to open root ('/') - if (!candidate) { - candidate = paths.sep; - } - } - - return candidate; -} \ No newline at end of file diff --git a/src/vs/base/node/extpath.ts b/src/vs/base/node/extpath.ts new file mode 100644 index 00000000000..b3b55b7aaee --- /dev/null +++ b/src/vs/base/node/extpath.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import { rtrim } from 'vs/base/common/strings'; +import { sep, join, normalize, dirname, basename } from 'vs/base/common/path'; +import { readdirSync } from 'vs/base/node/pfs'; +import { promisify } from 'util'; + +/** + * Copied from: https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83 + * + * Given an absolute, normalized, and existing file path 'realcase' returns the exact path that the file has on disk. + * On a case insensitive file system, the returned path might differ from the original path by character casing. + * On a case sensitive file system, the returned path will always be identical to the original path. + * In case of errors, null is returned. But you cannot use this function to verify that a path exists. + * realcaseSync does not handle '..' or '.' path segments and it does not take the locale into account. + */ +export function realcaseSync(path: string): string | null { + const dir = dirname(path); + if (path === dir) { // end recursion + return path; + } + + const name = (basename(path) /* can be '' for windows drive letters */ || path).toLowerCase(); + try { + const entries = readdirSync(dir); + const found = entries.filter(e => e.toLowerCase() === name); // use a case insensitive search + if (found.length === 1) { + // on a case sensitive filesystem we cannot determine here, whether the file exists or not, hence we need the 'file exists' precondition + const prefix = realcaseSync(dir); // recurse + if (prefix) { + return join(prefix, found[0]); + } + } else if (found.length > 1) { + // must be a case sensitive $filesystem + const ix = found.indexOf(name); + if (ix >= 0) { // case sensitive + const prefix = realcaseSync(dir); // recurse + if (prefix) { + return join(prefix, found[ix]); + } + } + } + } catch (error) { + // silently ignore error + } + + return null; +} + +export async function realpath(path: string): Promise { + try { + return await promisify(fs.realpath)(path); + } catch (error) { + + // We hit an error calling fs.realpath(). Since fs.realpath() is doing some path normalization + // we now do a similar normalization and then try again if we can access the path with read + // permissions at least. If that succeeds, we return that path. + // fs.realpath() is resolving symlinks and that can fail in certain cases. The workaround is + // to not resolve links but to simply see if the path is read accessible or not. + const normalizedPath = normalizePath(path); + + await promisify(fs.access)(normalizedPath, fs.constants.R_OK); + + return normalizedPath; + } +} + +export function realpathSync(path: string): string { + try { + return fs.realpathSync(path); + } catch (error) { + + // We hit an error calling fs.realpathSync(). Since fs.realpathSync() is doing some path normalization + // we now do a similar normalization and then try again if we can access the path with read + // permissions at least. If that succeeds, we return that path. + // fs.realpath() is resolving symlinks and that can fail in certain cases. The workaround is + // to not resolve links but to simply see if the path is read accessible or not. + const normalizedPath = normalizePath(path); + fs.accessSync(normalizedPath, fs.constants.R_OK); // throws in case of an error + + return normalizedPath; + } +} + +function normalizePath(path: string): string { + return rtrim(normalize(path), sep); +} \ No newline at end of file diff --git a/src/vs/base/node/flow.ts b/src/vs/base/node/flow.ts deleted file mode 100644 index 6214d738c0a..00000000000 --- a/src/vs/base/node/flow.ts +++ /dev/null @@ -1,187 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; - -/** - * Executes the given function (fn) over the given array of items (list) in parallel and returns the resulting errors and results as - * array to the callback (callback). The resulting errors and results are evaluated by calling the provided callback function. - */ -export function parallel(list: T[], fn: (item: T, callback: (err: Error, result: E) => void) => void, callback: (err: Array | null, result: E[]) => void): void { - let results = new Array(list.length); - let errors = new Array(list.length); - let didErrorOccur = false; - let doneCount = 0; - - if (list.length === 0) { - return callback(null, []); - } - - list.forEach((item, index) => { - fn(item, (error, result) => { - if (error) { - didErrorOccur = true; - results[index] = null; - errors[index] = error; - } else { - results[index] = result; - errors[index] = null; - } - - if (++doneCount === list.length) { - return callback(didErrorOccur ? errors : null, results); - } - }); - }); -} - -/** - * Executes the given function (fn) over the given array of items (param) in sequential order and returns the first occurred error or the result as - * array to the callback (callback). The resulting errors and results are evaluated by calling the provided callback function. The first param can - * either be a function that returns an array of results to loop in async fashion or be an array of items already. - */ -export function loop(param: (callback: (error: Error, result: T[]) => void) => void, fn: (item: T, callback: (error: Error | null, result: E | null) => void, index: number, total: number) => void, callback: (error: Error | null, result: E[] | null) => void): void; -export function loop(param: T[], fn: (item: T, callback: (error: Error | null, result: E | null) => void, index: number, total: number) => void, callback: (error: Error | null, result: E[] | null) => void): void; -export function loop(param: any, fn: (item: any, callback: (error: Error | null, result: E | null) => void, index: number, total: number) => void, callback: (error: Error | null, result: E[] | null) => void): void { - - // Assert - assert.ok(param, 'Missing first parameter'); - assert.ok(typeof (fn) === 'function', 'Second parameter must be a function that is called for each element'); - assert.ok(typeof (callback) === 'function', 'Third parameter must be a function that is called on error and success'); - - // Param is function, execute to retrieve array - if (typeof (param) === 'function') { - try { - param((error: Error, result: E[]) => { - if (error) { - callback(error, null); - } else { - loop(result, fn, callback); - } - }); - } catch (error) { - callback(error, null); - } - } - - // Expect the param to be an array and loop over it - else { - let results: E[] = []; - - let looper: (i: number) => void = function (i: number): void { - - // Still work to do - if (i < param.length) { - - // Execute function on array element - try { - fn(param[i], (error: any, result: E) => { - - // A method might only send a boolean value as return value (e.g. fs.exists), support this case gracefully - if (error === true || error === false) { - result = error; - error = null; - } - - // Quit looping on error - if (error) { - callback(error, null); - } - - // Otherwise push result on stack and continue looping - else { - if (result) { //Could be that provided function is not returning a result - results.push(result); - } - - process.nextTick(() => { - looper(i + 1); - }); - } - }, i, param.length); - } catch (error) { - callback(error, null); - } - } - - // Done looping, pass back results too callback function - else { - callback(null, results); - } - }; - - // Start looping with first element in array - looper(0); - } -} - -function Sequence(sequences: { (...param: any[]): void; }[]): void { - - // Assert - assert.ok(sequences.length > 1, 'Need at least one error handler and one function to process sequence'); - sequences.forEach((sequence) => { - assert.ok(typeof (sequence) === 'function'); - }); - - // Execute in Loop - let errorHandler = sequences.splice(0, 1)[0]; //Remove error handler - let sequenceResult: any = null; - - loop(sequences, (sequence, clb) => { - let sequenceFunction = function (error: any, result: any): void { - - // A method might only send a boolean value as return value (e.g. fs.exists), support this case gracefully - if (error === true || error === false) { - result = error; - error = null; - } - - // Handle Error and Result - if (error) { - clb(error, null); - } else { - sequenceResult = result; //Remember result of sequence - clb(null, null); //Don't pass on result to Looper as we are not aggregating it - } - }; - - // We call the sequence function setting "this" to be the callback we define here - // and we pass in the "sequenceResult" as first argument. Doing all this avoids having - // to pass in a callback to the sequence because the callback is already "this". - try { - sequence.call(sequenceFunction, sequenceResult); - } catch (error) { - clb(error, null); - } - }, (error, result) => { - if (error) { - errorHandler(error); - } - }); -} - -/** - * Takes a variable list of functions to execute in sequence. The first function must be the error handler and the - * following functions can do arbitrary work. "this" must be used as callback value for async functions to continue - * through the sequence: - * sequence( - * function errorHandler(error) { - * clb(error, null); - * }, - * - * function doSomethingAsync() { - * fs.doAsync(path, this); - * }, - * - * function done(result) { - * clb(null, result); - * } - * ); - */ -export function sequence(errorHandler: (error: Error) => void, ...sequences: Function[]): void; -export function sequence(sequences: Function[]): void; -export function sequence(sequences: any): void { - Sequence((Array.isArray(sequences)) ? sequences : Array.prototype.slice.call(arguments)); -} \ No newline at end of file diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index 2ee6601aad0..d8f617df434 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -20,8 +20,8 @@ import { TernarySearchTree } from 'vs/base/common/map'; // Sun xVM VirtualBox 08-00-27 export const virtualMachineHint: { value(): number } = new class { - private _virtualMachineOUIs: TernarySearchTree; - private _value: number; + private _virtualMachineOUIs?: TernarySearchTree; + private _value?: number; private _isVirtualMachineMacAdress(mac: string): boolean { if (!this._virtualMachineOUIs) { @@ -92,6 +92,12 @@ function getMacMachineId(): Promise { resolve(undefined); } }); + + // Timeout due to hang with reduced privileges #58392 + // TODO@sbatten: Remove this when getmac is patched + setTimeout(() => { + resolve(undefined); + }, 10000); } catch (err) { errors.onUnexpectedError(err); resolve(undefined); diff --git a/src/vs/base/node/languagePacks.d.ts b/src/vs/base/node/languagePacks.d.ts new file mode 100644 index 00000000000..241392e5eca --- /dev/null +++ b/src/vs/base/node/languagePacks.d.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface NLSConfiguration { + locale: string; + availableLanguages: { + [key: string]: string; + }; + pseudo?: boolean; +} + +export interface InternalNLSConfiguration extends NLSConfiguration { + _languagePackId: string; + _translationsConfigFile: string; + _cacheRoot: string; + _resolvedLanguagePackCoreLocation: string; + _corruptedFile: string; + _languagePackSupport?: boolean; +} + +export function getNLSConfiguration(commit: string, userDataPath: string, metaDataFile: string, locale: string): Promise; \ No newline at end of file diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js new file mode 100644 index 00000000000..445a6c5f7b3 --- /dev/null +++ b/src/vs/base/node/languagePacks.js @@ -0,0 +1,332 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +//@ts-check + +/** + * @param {NodeRequire} nodeRequire + * @param {typeof import('path')} path + * @param {typeof import('fs')} fs + * @param {typeof import('../common/performance')} perf + */ +function factory(nodeRequire, path, fs, perf) { + + /** + * @param {string} file + * @returns {Promise} + */ + function exists(file) { + return new Promise(c => fs.exists(file, c)); + } + + /** + * @param {string} file + * @returns {Promise} + */ + function touch(file) { + return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); + } + + /** + * @param {string} file + * @returns {Promise} + */ + function lstat(file) { + return new Promise((c, e) => fs.lstat(file, (err, stats) => err ? e(err) : c(stats))); + } + + /** + * @param {string} dir + * @returns {Promise} + */ + function readdir(dir) { + return new Promise((c, e) => fs.readdir(dir, (err, files) => err ? e(err) : c(files))); + } + + /** + * @param {string} dir + * @returns {Promise} + */ + function mkdir(dir) { + return new Promise((c, e) => fs.mkdir(dir, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); + } + + /** + * @param {string} dir + * @returns {Promise} + */ + function rmdir(dir) { + return new Promise((c, e) => fs.rmdir(dir, err => err ? e(err) : c(undefined))); + } + + /** + * @param {string} file + * @returns {Promise} + */ + function unlink(file) { + return new Promise((c, e) => fs.unlink(file, err => err ? e(err) : c(undefined))); + } + + /** + * @param {string} location + * @returns {Promise} + */ + function rimraf(location) { + return lstat(location).then(stat => { + if (stat.isDirectory() && !stat.isSymbolicLink()) { + return readdir(location) + .then(children => Promise.all(children.map(child => rimraf(path.join(location, child))))) + .then(() => rmdir(location)); + } else { + return unlink(location); + } + }, err => { + if (err.code === 'ENOENT') { + return undefined; + } + throw err; + }); + } + + /** + * @param {string} dir + * @returns {Promise} + */ + function mkdirp(dir) { + return mkdir(dir).then(null, err => { + if (err && err.code === 'ENOENT') { + const parent = path.dirname(dir); + + if (parent !== dir) { // if not arrived at root + return mkdirp(parent).then(() => mkdir(dir)); + } + } + + throw err; + }); + } + + function readFile(file) { + return new Promise(function (resolve, reject) { + fs.readFile(file, 'utf8', function (err, data) { + if (err) { + reject(err); + return; + } + resolve(data); + }); + }); + } + + /** + * @param {string} file + * @param {string} content + * @returns {Promise} + */ + function writeFile(file, content) { + return new Promise(function (resolve, reject) { + fs.writeFile(file, content, 'utf8', function (err) { + if (err) { + reject(err); + return; + } + resolve(); + }); + }); + } + + + /** + * @param {string} userDataPath + * @returns {object} + */ + function getLanguagePackConfigurations(userDataPath) { + const configFile = path.join(userDataPath, 'languagepacks.json'); + try { + return nodeRequire(configFile); + } catch (err) { + // Do nothing. If we can't read the file we have no + // language pack config. + } + return undefined; + } + + /** + * @param {object} config + * @param {string} locale + */ + function resolveLanguagePackLocale(config, locale) { + try { + while (locale) { + if (config[locale]) { + return locale; + } else { + const index = locale.lastIndexOf('-'); + if (index > 0) { + locale = locale.substring(0, index); + } else { + return undefined; + } + } + } + } catch (err) { + console.error('Resolving language pack configuration failed.', err); + } + return undefined; + } + + /** + * @param {string} commit + * @param {string} userDataPath + * @param {string} metaDataFile + * @param {string} locale + */ + function getNLSConfiguration(commit, userDataPath, metaDataFile, locale) { + if (locale === 'pseudo') { + return Promise.resolve({ locale: locale, availableLanguages: {}, pseudo: true }); + } + + if (process.env['VSCODE_DEV']) { + return Promise.resolve({ locale: locale, availableLanguages: {} }); + } + + // 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 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 === 'en-us')) { + return Promise.resolve({ locale: locale, availableLanguages: {} }); + } + + const initialLocale = locale; + + perf.mark('nlsGeneration:start'); + + const defaultResult = function (locale) { + perf.mark('nlsGeneration:end'); + return Promise.resolve({ locale: locale, availableLanguages: {} }); + }; + try { + if (!commit) { + return defaultResult(initialLocale); + } + const configs = getLanguagePackConfigurations(userDataPath); + if (!configs) { + return defaultResult(initialLocale); + } + locale = resolveLanguagePackLocale(configs, locale); + if (!locale) { + return defaultResult(initialLocale); + } + const packConfig = configs[locale]; + let mainPack; + if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { + return defaultResult(initialLocale); + } + return exists(mainPack).then(fileExists => { + if (!fileExists) { + return defaultResult(initialLocale); + } + const packId = packConfig.hash + '.' + locale; + const cacheRoot = path.join(userDataPath, 'clp', packId); + const coreLocation = path.join(cacheRoot, commit); + const translationsConfigFile = path.join(cacheRoot, 'tcf.json'); + const corruptedFile = path.join(cacheRoot, 'corrupted.info'); + const result = { + locale: initialLocale, + availableLanguages: { '*': locale }, + _languagePackId: packId, + _translationsConfigFile: translationsConfigFile, + _cacheRoot: cacheRoot, + _resolvedLanguagePackCoreLocation: coreLocation, + _corruptedFile: corruptedFile + }; + return exists(corruptedFile).then(corrupted => { + // The nls cache directory is corrupted. + let toDelete; + if (corrupted) { + toDelete = rimraf(cacheRoot); + } else { + toDelete = Promise.resolve(undefined); + } + return toDelete.then(() => { + return exists(coreLocation).then(fileExists => { + if (fileExists) { + // We don't wait for this. No big harm if we can't touch + touch(coreLocation).catch(() => { }); + perf.mark('nlsGeneration:end'); + return result; + } + return mkdirp(coreLocation).then(() => { + return Promise.all([readFile(metaDataFile), readFile(mainPack)]); + }).then(values => { + const metadata = JSON.parse(values[0]); + const packData = JSON.parse(values[1]).contents; + const bundles = Object.keys(metadata.bundles); + const writes = []; + for (let bundle of bundles) { + const modules = metadata.bundles[bundle]; + const target = Object.create(null); + for (let module of modules) { + const keys = metadata.keys[module]; + const defaultMessages = metadata.messages[module]; + const translations = packData[module]; + let targetStrings; + if (translations) { + targetStrings = []; + for (let i = 0; i < keys.length; i++) { + const elem = keys[i]; + const key = typeof elem === 'string' ? elem : elem.key; + let translatedMessage = translations[key]; + if (translatedMessage === undefined) { + translatedMessage = defaultMessages[i]; + } + targetStrings.push(translatedMessage); + } + } else { + targetStrings = defaultMessages; + } + target[module] = targetStrings; + } + writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target))); + } + writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations))); + return Promise.all(writes); + }).then(() => { + perf.mark('nlsGeneration:end'); + return result; + }).catch(err => { + console.error('Generating translation files failed.', err); + return defaultResult(locale); + }); + }); + }); + }); + }); + } catch (err) { + console.error('Generating translation files failed.', err); + return defaultResult(locale); + } + } + + return { + getNLSConfiguration + }; +} + + +if (typeof define === 'function') { + // amd + define(['path', 'fs', 'vs/base/common/performance'], function (path, fs, perf) { return factory(require.__$__nodeRequire, path, fs, perf); }); +} else if (typeof module === 'object' && typeof module.exports === 'object') { + const path = require('path'); + const fs = require('fs'); + const perf = require('../common/performance'); + module.exports = factory(require, path, fs, perf); +} else { + throw new Error('Unknown context'); +} \ No newline at end of file diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 3222a44a14a..6e6783f9f1f 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -3,91 +3,228 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as extfs from 'vs/base/node/extfs'; -import { join } from 'path'; -import { nfcall, Queue } from 'vs/base/common/async'; +import { join, dirname } from 'vs/base/common/path'; +import { Queue } from 'vs/base/common/async'; import * as fs from 'fs'; import * as os from 'os'; import * as platform from 'vs/base/common/platform'; -import { once } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; +import { endsWith } from 'vs/base/common/strings'; +import { promisify } from 'util'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { isRootOrDriveLetter } from 'vs/base/common/extpath'; +import { generateUuid } from 'vs/base/common/uuid'; +import { normalizeNFC } from 'vs/base/common/normalization'; +import { encode, encodeStream } from 'vs/base/node/encoding'; -export function readdir(path: string): Promise { - return nfcall(extfs.readdir, path); +export enum RimRafMode { + + /** + * Slow version that unlinks each file and folder. + */ + UNLINK, + + /** + * Fast version that first moves the file/folder + * into a temp directory and then deletes that + * without waiting for it. + */ + MOVE } -export function exists(path: string): TPromise { - return new TPromise(c => fs.exists(path, c)); +export async function rimraf(path: string, mode = RimRafMode.UNLINK): Promise { + if (isRootOrDriveLetter(path)) { + throw new Error('rimraf - will refuse to recursively delete root'); + } + + // delete: via unlink + if (mode === RimRafMode.UNLINK) { + return rimrafUnlink(path); + } + + // delete: via move + return rimrafMove(path); } -export function chmod(path: string, mode: number): Promise { - return nfcall(fs.chmod, path, mode); -} +async function rimrafUnlink(path: string): Promise { + try { + const stat = await lstat(path); -export import mkdirp = extfs.mkdirp; -import { TPromise } from 'vs/base/common/winjs.base'; - -export function rimraf(path: string): Promise { - return lstat(path).then(stat => { + // Folder delete (recursive) - NOT for symbolic links though! if (stat.isDirectory() && !stat.isSymbolicLink()) { - return readdir(path) - .then(children => TPromise.join(children.map(child => rimraf(join(path, child))))) - .then(() => rmdir(path)); - } else { + + // Children + const children = await readdir(path); + await Promise.all(children.map(child => rimrafUnlink(join(path, child)))); + + // Folder + await promisify(fs.rmdir)(path); + } + + // Single file delete + else { + + // chmod as needed to allow for unlink + const mode = stat.mode; + if (!(mode & 128)) { // 128 === 0200 + await chmod(path, mode | 128); + } + return unlink(path); } - }, (err: NodeJS.ErrnoException) => { - if (err.code === 'ENOENT') { - return void 0; + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; } - - return TPromise.wrapError(err); - }); + } } -export function realpath(path: string): Promise { - return nfcall(extfs.realpath, path); +async function rimrafMove(path: string): Promise { + try { + const pathInTemp = join(os.tmpdir(), generateUuid()); + try { + await rename(path, pathInTemp); + } catch (error) { + return rimrafUnlink(path); // if rename fails, delete without tmp dir + } + + // Delete but do not return as promise + rimrafUnlink(pathInTemp); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } +} + +export function rimrafSync(path: string): void { + if (isRootOrDriveLetter(path)) { + throw new Error('rimraf - will refuse to recursively delete root'); + } + + try { + const stat = fs.lstatSync(path); + + // Folder delete (recursive) - NOT for symbolic links though! + if (stat.isDirectory() && !stat.isSymbolicLink()) { + + // Children + const children = readdirSync(path); + children.map(child => rimrafSync(join(path, child))); + + // Folder + fs.rmdirSync(path); + } + + // Single file delete + else { + + // chmod as needed to allow for unlink + const mode = stat.mode; + if (!(mode & 128)) { // 128 === 0200 + fs.chmodSync(path, mode | 128); + } + + return fs.unlinkSync(path); + } + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } +} + +export async function readdir(path: string): Promise { + return handleDirectoryChildren(await promisify(fs.readdir)(path)); +} + +export function readdirSync(path: string): string[] { + return handleDirectoryChildren(fs.readdirSync(path)); +} + +function handleDirectoryChildren(children: string[]): string[] { + // Mac: uses NFD unicode form on disk, but we want NFC + // See also https://github.com/nodejs/node/issues/2165 + if (platform.isMacintosh) { + return children.map(child => normalizeNFC(child)); + } + + return children; +} + +export function exists(path: string): Promise { + return promisify(fs.exists)(path); +} + +export function chmod(path: string, mode: number): Promise { + return promisify(fs.chmod)(path, mode); } export function stat(path: string): Promise { - return nfcall(fs.stat, path); + return promisify(fs.stat)(path); } -export function statLink(path: string): Promise<{ stat: fs.Stats, isSymbolicLink: boolean }> { - return nfcall(extfs.statLink, path); +export interface IStatAndLink { + stat: fs.Stats; + isSymbolicLink: boolean; +} + +export async function statLink(path: string): Promise { + + // First stat the link + let linkStat: fs.Stats | undefined; + let linkStatError: NodeJS.ErrnoException | undefined; + try { + linkStat = await lstat(path); + } catch (error) { + linkStatError = error; + } + + // Then stat the target and return that + const isLink = !!(linkStat && linkStat.isSymbolicLink()); + if (linkStatError || isLink) { + const fileStat = await stat(path); + + return { stat: fileStat, isSymbolicLink: isLink }; + } + + return { stat: linkStat!, isSymbolicLink: false }; } export function lstat(path: string): Promise { - return nfcall(fs.lstat, path); + return promisify(fs.lstat)(path); } export function rename(oldPath: string, newPath: string): Promise { - return nfcall(fs.rename, oldPath, newPath); + return promisify(fs.rename)(oldPath, newPath); } -export function rmdir(path: string): Promise { - return nfcall(fs.rmdir, path); +export function renameIgnoreError(oldPath: string, newPath: string): Promise { + return new Promise(resolve => { + fs.rename(oldPath, newPath, () => resolve()); + }); } export function unlink(path: string): Promise { - return nfcall(fs.unlink, path); + return promisify(fs.unlink)(path); } export function symlink(target: string, path: string, type?: string): Promise { - return nfcall(fs.symlink, target, path, type); + return promisify(fs.symlink)(target, path, type); } export function readlink(path: string): Promise { - return nfcall(fs.readlink, path); + return promisify(fs.readlink)(path); } export function truncate(path: string, len: number): Promise { - return nfcall(fs.truncate, path, len); + return promisify(fs.truncate)(path, len); } export function readFile(path: string): Promise; export function readFile(path: string, encoding: string): Promise; export function readFile(path: string, encoding?: string): Promise { - return nfcall(fs.readFile, path, encoding); + return promisify(fs.readFile)(path, encoding); } // According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback) @@ -95,15 +232,15 @@ export function readFile(path: string, encoding?: string): Promise } = Object.create(null); -export function writeFile(path: string, data: string, options?: extfs.IWriteFileOptions): Promise; -export function writeFile(path: string, data: Buffer, options?: extfs.IWriteFileOptions): Promise; -export function writeFile(path: string, data: Uint8Array, options?: extfs.IWriteFileOptions): Promise; -export function writeFile(path: string, data: NodeJS.ReadableStream, options?: extfs.IWriteFileOptions): Promise; -export function writeFile(path: string, data: any, options?: extfs.IWriteFileOptions): Promise; -export function writeFile(path: string, data: any, options?: extfs.IWriteFileOptions): any { +export function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise; +export function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise; +export function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise; +export function writeFile(path: string, data: NodeJS.ReadableStream, options?: IWriteFileOptions): Promise; +export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise; +export function writeFile(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options?: IWriteFileOptions): Promise { const queueKey = toQueueKey(path); - return ensureWriteFileQueue(queueKey).queue(() => nfcall(extfs.writeFileAndFlush, path, data, options)); + return ensureWriteFileQueue(queueKey).queue(() => writeFileAndFlush(path, data, options)); } function toQueueKey(path: string): string { @@ -121,7 +258,7 @@ function ensureWriteFileQueue(queueKey: string): Queue { writeFileQueue = new Queue(); writeFilePathQueue[queueKey] = writeFileQueue; - const onFinish = once(writeFileQueue.onFinished); + const onFinish = Event.once(writeFileQueue.onFinished); onFinish(() => { delete writeFilePathQueue[queueKey]; writeFileQueue.dispose(); @@ -131,49 +268,241 @@ function ensureWriteFileQueue(queueKey: string): Queue { return writeFileQueue; } -/** -* Read a dir and return only subfolders -*/ -export function readDirsInDir(dirPath: string): Promise { - return readdir(dirPath).then(children => { - return TPromise.join(children.map(c => dirExists(join(dirPath, c)))).then(exists => { - return children.filter((_, i) => exists[i]); +export interface IWriteFileOptions { + mode?: number; + flag?: string; + encoding?: { + charset: string; + addBOM: boolean; + }; +} + +interface IEnsuredWriteFileOptions extends IWriteFileOptions { + mode: number; + flag: string; +} + +let canFlush = true; +function writeFileAndFlush(path: string, data: string | Buffer | NodeJS.ReadableStream | Uint8Array, options: IWriteFileOptions | undefined): Promise { + const ensuredOptions = ensureWriteOptions(options); + + return new Promise((resolve, reject) => { + if (typeof data === 'string' || Buffer.isBuffer(data) || data instanceof Uint8Array) { + doWriteFileAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve()); + } else { + doWriteFileStreamAndFlush(path, data, ensuredOptions, error => error ? reject(error) : resolve()); + } + }); +} + +function doWriteFileStreamAndFlush(path: string, reader: NodeJS.ReadableStream, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void { + + // finish only once + let finished = false; + const finish = (error?: Error) => { + if (!finished) { + finished = true; + + // in error cases we need to manually close streams + // if the write stream was successfully opened + if (error) { + if (isOpen) { + writer.once('close', () => callback(error)); + writer.destroy(); + } else { + callback(error); + } + } + + // otherwise just return without error + else { + callback(); + } + } + }; + + // create writer to target. we set autoClose: false because we want to use the streams + // file descriptor to call fs.fdatasync to ensure the data is flushed to disk + const writer = fs.createWriteStream(path, { mode: options.mode, flags: options.flag, autoClose: false }); + + // Event: 'open' + // Purpose: save the fd for later use and start piping + // Notes: will not be called when there is an error opening the file descriptor! + let fd: number; + let isOpen: boolean; + writer.once('open', descriptor => { + fd = descriptor; + isOpen = true; + + // if an encoding is provided, we need to pipe the stream through + // an encoder stream and forward the encoding related options + if (options.encoding) { + reader = reader.pipe(encodeStream(options.encoding.charset, { addBOM: options.encoding.addBOM })); + } + + // start data piping only when we got a successful open. this ensures that we do + // not consume the stream when an error happens and helps to fix this issue: + // https://github.com/Microsoft/vscode/issues/42542 + reader.pipe(writer); + }); + + // Event: 'error' + // Purpose: to return the error to the outside and to close the write stream (does not happen automatically) + reader.once('error', error => finish(error)); + writer.once('error', error => finish(error)); + + // Event: 'finish' + // Purpose: use fs.fdatasync to flush the contents to disk + // Notes: event is called when the writer has finished writing to the underlying resource. we must call writer.close() + // because we have created the WriteStream with autoClose: false + writer.once('finish', () => { + + // flush to disk + if (canFlush && isOpen) { + fs.fdatasync(fd, (syncError: Error) => { + + // In some exotic setups it is well possible that node fails to sync + // In that case we disable flushing and warn to the console + if (syncError) { + console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError); + canFlush = false; + } + + writer.destroy(); + }); + } else { + writer.destroy(); + } + }); + + // Event: 'close' + // Purpose: signal we are done to the outside + // Notes: event is called when the writer's filedescriptor is closed + writer.once('close', () => finish()); +} + +// Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk +// We do this in cases where we want to make sure the data is really on disk and +// not in some cache. +// +// See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194 +function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, options: IEnsuredWriteFileOptions, callback: (error?: Error) => void): void { + if (options.encoding) { + data = encode(data instanceof Uint8Array ? Buffer.from(data) : data, options.encoding.charset, { addBOM: options.encoding.addBOM }); + } + + if (!canFlush) { + return fs.writeFile(path, data, { mode: options.mode, flag: options.flag }, callback); + } + + // Open the file with same flags and mode as fs.writeFile() + fs.open(path, options.flag, options.mode, (openError, fd) => { + if (openError) { + return callback(openError); + } + + // It is valid to pass a fd handle to fs.writeFile() and this will keep the handle open! + fs.writeFile(fd, data, writeError => { + if (writeError) { + return fs.close(fd, () => callback(writeError)); // still need to close the handle on error! + } + + // Flush contents (not metadata) of the file to disk + fs.fdatasync(fd, (syncError: Error) => { + + // In some exotic setups it is well possible that node fails to sync + // In that case we disable flushing and warn to the console + if (syncError) { + console.warn('[node.js fs] fdatasync is now disabled for this session because it failed: ', syncError); + canFlush = false; + } + + return fs.close(fd, closeError => callback(closeError)); + }); }); }); } -/** -* `path` exists and is a directory -*/ -export function dirExists(path: string): Promise { - return stat(path).then(stat => stat.isDirectory(), () => false); -} +export function writeFileSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void { + const ensuredOptions = ensureWriteOptions(options); -/** -* `path` exists and is a file. -*/ -export function fileExists(path: string): Promise { - return stat(path).then(stat => stat.isFile(), () => false); -} - -/** - * Deletes a path from disk. - */ -let _tmpDir: string | null = null; -function getTmpDir(): string { - if (!_tmpDir) { - _tmpDir = os.tmpdir(); + if (ensuredOptions.encoding) { + data = encode(data, ensuredOptions.encoding.charset, { addBOM: ensuredOptions.encoding.addBOM }); + } + + if (!canFlush) { + return fs.writeFileSync(path, data, { mode: ensuredOptions.mode, flag: ensuredOptions.flag }); + } + + // Open the file with same flags and mode as fs.writeFile() + const fd = fs.openSync(path, ensuredOptions.flag, ensuredOptions.mode); + + try { + + // It is valid to pass a fd handle to fs.writeFile() and this will keep the handle open! + fs.writeFileSync(fd, data); + + // Flush contents (not metadata) of the file to disk + try { + fs.fdatasyncSync(fd); + } catch (syncError) { + console.warn('[node.js fs] fdatasyncSync is now disabled for this session because it failed: ', syncError); + canFlush = false; + } + } finally { + fs.closeSync(fd); } - return _tmpDir; -} -export function del(path: string, tmp = getTmpDir()): Promise { - return nfcall(extfs.del, path, tmp); } -export function whenDeleted(path: string): TPromise { +function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptions { + if (!options) { + return { mode: 0o666, flag: 'w' }; + } + + return { + mode: typeof options.mode === 'number' ? options.mode : 0o666, + flag: typeof options.flag === 'string' ? options.flag : 'w', + encoding: options.encoding + }; +} + +export async function readDirsInDir(dirPath: string): Promise { + const children = await readdir(dirPath); + const directories: string[] = []; + + for (const child of children) { + if (await dirExists(join(dirPath, child))) { + directories.push(child); + } + } + + return directories; +} + +export async function dirExists(path: string): Promise { + try { + const fileStat = await stat(path); + + return fileStat.isDirectory(); + } catch (error) { + return false; + } +} + +export async function fileExists(path: string): Promise { + try { + const fileStat = await stat(path); + + return fileStat.isFile(); + } catch (error) { + return false; + } +} + +export function whenDeleted(path: string): Promise { // Complete when wait marker file is deleted - return new TPromise(c => { + return new Promise(resolve => { let running = false; const interval = setInterval(() => { if (!running) { @@ -183,7 +512,7 @@ export function whenDeleted(path: string): TPromise { if (!exists) { clearInterval(interval); - c(void 0); + resolve(undefined); } }); } @@ -191,6 +520,165 @@ export function whenDeleted(path: string): TPromise { }); } -export function copy(source: string, target: string): Promise { - return nfcall(extfs.copy, source, target); +export async function move(source: string, target: string): Promise { + if (source === target) { + return Promise.resolve(); + } + + async function updateMtime(path: string): Promise { + const stat = await lstat(path); + if (stat.isDirectory() || stat.isSymbolicLink()) { + return Promise.resolve(); // only for files + } + + const fd = await promisify(fs.open)(path, 'a'); + try { + await promisify(fs.futimes)(fd, stat.atime, new Date()); + } catch (error) { + //ignore + } + + return promisify(fs.close)(fd); + } + + try { + await rename(source, target); + await updateMtime(target); + } catch (error) { + + // In two cases we fallback to classic copy and delete: + // + // 1.) The EXDEV error indicates that source and target are on different devices + // In this case, fallback to using a copy() operation as there is no way to + // rename() between different devices. + // + // 2.) The user tries to rename a file/folder that ends with a dot. This is not + // really possible to move then, at least on UNC devices. + if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || endsWith(source, '.')) { + await copy(source, target); + await rimraf(source, RimRafMode.MOVE); + await updateMtime(target); + } else { + throw error; + } + } } + +export async function copy(source: string, target: string, copiedSourcesIn?: { [path: string]: boolean }): Promise { + const copiedSources = copiedSourcesIn ? copiedSourcesIn : Object.create(null); + + const fileStat = await stat(source); + if (!fileStat.isDirectory()) { + return doCopyFile(source, target, fileStat.mode & 511); + } + + if (copiedSources[source]) { + return Promise.resolve(); // escape when there are cycles (can happen with symlinks) + } + + copiedSources[source] = true; // remember as copied + + // Create folder + await mkdirp(target, fileStat.mode & 511); + + // Copy each file recursively + const files = await readdir(source); + for (let i = 0; i < files.length; i++) { + const file = files[i]; + await copy(join(source, file), join(target, file), copiedSources); + } +} + +async function doCopyFile(source: string, target: string, mode: number): Promise { + return new Promise((resolve, reject) => { + const reader = fs.createReadStream(source); + const writer = fs.createWriteStream(target, { mode }); + + let finished = false; + const finish = (error?: Error) => { + if (!finished) { + finished = true; + + // in error cases, pass to callback + if (error) { + return reject(error); + } + + // we need to explicitly chmod because of https://github.com/nodejs/node/issues/1104 + fs.chmod(target, mode, error => error ? reject(error) : resolve()); + } + }; + + // handle errors properly + reader.once('error', error => finish(error)); + writer.once('error', error => finish(error)); + + // we are done (underlying fd has been closed) + writer.once('close', () => finish()); + + // start piping + reader.pipe(writer); + }); +} + +export async function mkdirp(path: string, mode?: number, token?: CancellationToken): Promise { + const mkdir = async () => { + try { + await promisify(fs.mkdir)(path, mode); + } catch (error) { + + // ENOENT: a parent folder does not exist yet + if (error.code === 'ENOENT') { + return Promise.reject(error); + } + + // Any other error: check if folder exists and + // return normally in that case if its a folder + try { + const fileStat = await stat(path); + if (!fileStat.isDirectory()) { + return Promise.reject(new Error(`'${path}' exists and is not a directory.`)); + } + } catch (statError) { + throw error; // rethrow original error + } + } + }; + + // stop at root + if (path === dirname(path)) { + return Promise.resolve(); + } + + try { + await mkdir(); + } catch (error) { + + // Respect cancellation + if (token && token.isCancellationRequested) { + return Promise.resolve(); + } + + // ENOENT: a parent folder does not exist yet, continue + // to create the parent folder and then try again. + if (error.code === 'ENOENT') { + await mkdirp(dirname(path), mode); + + return mkdir(); + } + + // Any other error + return Promise.reject(error); + } +} + +// See https://github.com/Microsoft/vscode/issues/30180 +const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB +const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB + +// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149 +const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB +const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB + +export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE; +export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; \ No newline at end of file diff --git a/src/vs/base/node/ports.ts b/src/vs/base/node/ports.ts index 4a4316a8bf1..d0334628e47 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -9,8 +9,8 @@ import * as net from 'net'; * @returns Returns a random port between 1025 and 65535. */ export function randomPort(): number { - let min = 1025; - let max = 65535; + const min = 1025; + const max = 65535; return min + Math.floor((max - min) * Math.random()); } @@ -18,7 +18,7 @@ export function randomPort(): number { * Given a start point and a max number of retries, will find a port that * is openable. Will return 0 in case no free port can be found. */ -export function findFreePort(startPort: number, giveUpAfter: number, timeout: number): Thenable { +export function findFreePort(startPort: number, giveUpAfter: number, timeout: number): Promise { let done = false; return new Promise(resolve => { diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 8ea9f18803c..df8690d0011 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; +import * as path from 'vs/base/common/path'; import * as fs from 'fs'; import * as cp from 'child_process'; import * as nls from 'vs/nls'; import * as Types from 'vs/base/common/types'; import { IStringDictionary } from 'vs/base/common/collections'; import * as Objects from 'vs/base/common/objects'; -import * as TPath from 'vs/base/common/paths'; +import * as extpath from 'vs/base/common/extpath'; 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 ValueCallback = (value?: T | Thenable) => void; +export type ValueCallback = (value?: T | Promise) => void; export type ErrorCallback = (error?: any) => void; export type ProgressCallback = (progress: T) => void; @@ -42,7 +42,7 @@ function getWindowsCode(status: number): TerminateResponseCode { export function terminateProcess(process: cp.ChildProcess, cwd?: string): TerminateResponse { if (Platform.isWindows) { try { - let options: any = { + const options: any = { stdio: ['pipe', 'pipe', 'ignore'] }; if (cwd) { @@ -54,8 +54,8 @@ export function terminateProcess(process: cp.ChildProcess, cwd?: string): Termin } } else if (Platform.isLinux || Platform.isMacintosh) { try { - let cmd = getPathFromAmdModule(require, 'vs/base/node/terminateProcess.sh'); - let result = cp.spawnSync(cmd, [process.pid.toString()]); + const cmd = getPathFromAmdModule(require, 'vs/base/node/terminateProcess.sh'); + const result = cp.spawnSync(cmd, [process.pid.toString()]); if (result.error) { return { success: false, error: result.error }; } @@ -107,13 +107,13 @@ export abstract class AbstractProcess { public constructor(executable: Executable); public constructor(cmd: string, args: string[] | undefined, shell: boolean, options: CommandOptions | undefined); public constructor(arg1: string | Executable, arg2?: string[], arg3?: boolean, arg4?: CommandOptions) { - if (arg2 !== void 0 && arg3 !== void 0 && arg4 !== void 0) { + if (arg2 !== undefined && arg3 !== undefined && arg4 !== undefined) { this.cmd = arg1; this.args = arg2; this.shell = arg3; this.options = arg4; } else { - let executable = arg1; + const executable = arg1; this.cmd = executable.command; this.shell = executable.isShellCommand; this.args = executable.args.slice(0); @@ -124,7 +124,7 @@ export abstract class AbstractProcess { this.terminateRequested = false; if (this.options.env) { - let newEnv: IStringDictionary = Object.create(null); + const newEnv: IStringDictionary = Object.create(null); Object.keys(process.env).forEach((key) => { newEnv[key] = process.env[key]!; }); @@ -137,7 +137,7 @@ export abstract class AbstractProcess { public getSanitizedCommand(): string { let result = this.cmd.toLowerCase(); - let index = result.lastIndexOf(path.sep); + const index = result.lastIndexOf(path.sep); if (index !== -1) { result = result.substring(index + 1); } @@ -148,13 +148,13 @@ export abstract class AbstractProcess { } public start(pp: ProgressCallback): Promise { - if (Platform.isWindows && ((this.options && this.options.cwd && TPath.isUNC(this.options.cwd)) || !this.options && TPath.isUNC(process.cwd()))) { + if (Platform.isWindows && ((this.options && this.options.cwd && extpath.isUNC(this.options.cwd)) || !this.options && extpath.isUNC(process.cwd()))) { return Promise.reject(new Error(nls.localize('TaskRunner.UNC', 'Can\'t execute a shell command on a UNC drive.'))); } return this.useExec().then((useExec) => { let cc: ValueCallback; let ee: ErrorCallback; - let result = new Promise((c, e) => { + const result = new Promise((c, e) => { cc = c; ee = e; }); @@ -166,7 +166,7 @@ export abstract class AbstractProcess { } this.childProcess = cp.exec(cmd, this.options, (error, stdout, stderr) => { this.childProcess = null; - let err: any = error; + const err: any = error; // This is tricky since executing a command shell reports error back in case the executed command return an // error or the command didn't exist at all. So we can't blindly treat an error as a failed command. So we // always parse the output and report success unless the job got killed. @@ -178,11 +178,11 @@ export abstract class AbstractProcess { }); } else { let childProcess: cp.ChildProcess | null = null; - let closeHandler = (data: any) => { + const closeHandler = (data: any) => { this.childProcess = null; this.childProcessPromise = null; this.handleClose(data, cc, pp, ee); - let result: SuccessData = { + const result: SuccessData = { terminated: this.terminateRequested }; if (Types.isNumber(data)) { @@ -191,12 +191,12 @@ export abstract class AbstractProcess { cc(result); }; if (this.shell && Platform.isWindows) { - let options: any = Objects.deepClone(this.options); + const options: any = Objects.deepClone(this.options); options.windowsVerbatimArguments = true; options.detached = false; let quotedCommand: boolean = false; let quotedArg: boolean = false; - let commandLine: string[] = []; + const commandLine: string[] = []; let quoted = this.ensureQuotes(this.cmd); commandLine.push(quoted.value); quotedCommand = quoted.quoted; @@ -207,7 +207,7 @@ export abstract class AbstractProcess { quotedArg = quotedArg && quoted.quoted; }); } - let args: string[] = [ + const args: string[] = [ '/s', '/c', ]; @@ -287,7 +287,7 @@ export abstract class AbstractProcess { } return this.childProcessPromise.then((childProcess) => { this.terminateRequested = true; - let result = terminateProcess(childProcess, this.options.cwd); + const result = terminateProcess(childProcess, this.options.cwd); if (result.success) { this.childProcess = null; } @@ -300,14 +300,14 @@ export abstract class AbstractProcess { private useExec(): Promise { return new Promise((c, e) => { if (!this.shell || !Platform.isWindows) { - c(false); + return c(false); } - let cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']); + const cmdShell = cp.spawn(getWindowsShell(), ['/s', '/c']); cmdShell.on('error', (error: Error) => { - c(true); + return c(true); }); cmdShell.on('exit', (data: any) => { - c(false); + return c(false); }); }); } @@ -326,12 +326,12 @@ export class LineProcess extends AbstractProcess { protected handleExec(cc: ValueCallback, pp: ProgressCallback, error: Error, stdout: Buffer, stderr: Buffer) { [stdout, stderr].forEach((buffer: Buffer, index: number) => { - let lineDecoder = new LineDecoder(); - let lines = lineDecoder.write(buffer); + const lineDecoder = new LineDecoder(); + const lines = lineDecoder.write(buffer); lines.forEach((line) => { pp({ line: line, source: index === 0 ? Source.stdout : Source.stderr }); }); - let line = lineDecoder.end(); + const line = lineDecoder.end(); if (line) { pp({ line: line, source: index === 0 ? Source.stdout : Source.stderr }); } @@ -343,11 +343,11 @@ export class LineProcess extends AbstractProcess { this.stdoutLineDecoder = new LineDecoder(); this.stderrLineDecoder = new LineDecoder(); childProcess.stdout.on('data', (data: Buffer) => { - let lines = this.stdoutLineDecoder.write(data); + const lines = this.stdoutLineDecoder.write(data); lines.forEach(line => pp({ line: line, source: Source.stdout })); }); childProcess.stderr.on('data', (data: Buffer) => { - let lines = this.stderrLineDecoder.write(data); + const lines = this.stderrLineDecoder.write(data); lines.forEach(line => pp({ line: line, source: Source.stderr })); }); } @@ -380,7 +380,7 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender return; } - let result = childProcess.send(msg, (error: Error) => { + const result = childProcess.send(msg, (error: Error) => { if (error) { console.error(error); // unlikely to happen, best we can do is log this error } @@ -409,20 +409,20 @@ export namespace win32 { if (path.isAbsolute(command)) { return command; } - if (cwd === void 0) { + if (cwd === undefined) { cwd = process.cwd(); } - let dir = path.dirname(command); + const dir = path.dirname(command); if (dir !== '.') { // We have a directory and the directory is relative (see above). Make the path absolute // to the current working directory. return path.join(cwd, command); } - if (paths === void 0 && Types.isString(process.env.PATH)) { + if (paths === undefined && Types.isString(process.env.PATH)) { paths = process.env.PATH.split(path.delimiter); } // No PATH environment. Make path absolute to the cwd. - if (paths === void 0 || paths.length === 0) { + if (paths === undefined || paths.length === 0) { return path.join(cwd, command); } // We have a simple file name. We get the path variable from the env @@ -449,4 +449,4 @@ export namespace win32 { } return path.join(cwd, command); } -} \ No newline at end of file +} diff --git a/src/vs/base/node/ps.sh b/src/vs/base/node/ps.sh new file mode 100755 index 00000000000..4fe31039c5a --- /dev/null +++ b/src/vs/base/node/ps.sh @@ -0,0 +1,39 @@ +#!/bin/sh +PAGESIZE=`getconf PAGESIZE`; +TOTAL_MEMORY=`cat /proc/meminfo | head -n 1 | awk '{print $2}'`; + +# Mimic the output of ps -ax -o pid=,ppid=,pcpu=,pmem=,command= +# Read all numeric subdirectories in /proc +for pid in `cd /proc && ls -d [0-9]*` + do { + if [ -e /proc/$pid/stat ] + then + echo $pid; + + # ppid is the word at index 4 in the stat file for the process + awk '{print $4}' /proc/$pid/stat; + + # pcpu - calculation will be done later, this is a placeholder value + echo "0.0" + + # pmem - ratio of the process's working set size to total memory. + # use the page size to convert to bytes, total memory is in KB + # multiplied by 100 to get percentage, extra 10 to be able to move + # the decimal over by one place + RESIDENT_SET_SIZE=`awk '{print $24}' /proc/$pid/stat`; + PERCENT_MEMORY=$(((1000 * $PAGESIZE * $RESIDENT_SET_SIZE) / ($TOTAL_MEMORY * 1024))); + if [ $PERCENT_MEMORY -lt 10 ] + then + # replace the last character with 0. the last character + echo $PERCENT_MEMORY | sed 's/.$/0.&/'; #pmem + else + # insert . before the last character + echo $PERCENT_MEMORY | sed 's/.$/.&/'; + fi + + # cmdline + xargs -0 < /proc/$pid/cmdline; + fi + } | tr "\n" "\t"; # Replace newlines with tab so that all info for a process is shown on one line + echo; # But add new lines between processes +done diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index a11b552b851..55c3a73da57 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -4,27 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { exec } from 'child_process'; - +import { ProcessItem } from 'vs/base/common/processes'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -export interface ProcessItem { - name: string; - cmd: string; - pid: number; - ppid: number; - load: number; - mem: number; - - children?: ProcessItem[]; -} - export function listProcesses(rootPid: number): Promise { return new Promise((resolve, reject) => { - let rootItem: ProcessItem; + let rootItem: ProcessItem | undefined; const map = new Map(); + function addToTree(pid: number, ppid: number, cmd: string, load: number, mem: number) { const parent = map.get(ppid); @@ -109,7 +99,7 @@ export function listProcesses(rootPid: number): Promise { } while (matches); if (result) { - if (cmd.indexOf('node ') !== 0) { + if (cmd.indexOf('node ') < 0 && cmd.indexOf('node.exe') < 0) { return `electron_node ${result}`; } } @@ -151,7 +141,7 @@ export function listProcesses(rootPid: number): Promise { rootItem = processItems.get(rootPid); if (rootItem) { processItems.forEach(item => { - let parent = processItems.get(item.ppid); + const parent = processItems.get(item.ppid); if (parent) { if (!parent.children) { parent.children = []; @@ -173,63 +163,87 @@ export function listProcesses(rootPid: number): Promise { }, windowsProcessTree.ProcessDataFlag.CommandLine | windowsProcessTree.ProcessDataFlag.Memory); }); } else { // OS X & Linux - - const CMD = '/bin/ps -ax -o pid=,ppid=,pcpu=,pmem=,command='; - const PID_CMD = /^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/; - - exec(CMD, { maxBuffer: 1000 * 1024 }, (err, stdout, stderr) => { - - if (err || stderr) { - reject(err || stderr.toString()); - } else { - - const lines = stdout.toString().split('\n'); - for (const line of lines) { - let matches = PID_CMD.exec(line.trim()); - if (matches && matches.length === 6) { - addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4])); + function calculateLinuxCpuUsage() { + // Flatten rootItem to get a list of all VSCode processes + let processes = [rootItem]; + const pids: number[] = []; + while (processes.length) { + const process = processes.shift(); + if (process) { + pids.push(process.pid); + if (process.children) { + processes = processes.concat(process.children); } } + } - if (process.platform === 'linux') { - // Flatten rootItem to get a list of all VSCode processes - let processes = [rootItem]; - const pids: number[] = []; - while (processes.length) { - const process = processes.shift(); - if (process) { - pids.push(process.pid); - if (process.children) { - processes = processes.concat(process.children); - } - } + // 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(getPathFromAmdModule(require, 'vs/base/node/cpuUsage.sh')); + cmd += ' ' + pids.join(' '); + + exec(cmd, {}, (err, stdout, stderr) => { + if (err || stderr) { + reject(err || new Error(stderr.toString())); + } else { + const cpuUsage = stdout.toString().split('\n'); + for (let i = 0; i < pids.length; i++) { + const processInfo = map.get(pids[i])!; + processInfo.load = parseFloat(cpuUsage[i]); } - // 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(getPathFromAmdModule(require, 'vs/base/node/cpuUsage.sh')); - cmd += ' ' + pids.join(' '); - - exec(cmd, {}, (err, stdout, stderr) => { - if (err || stderr) { - reject(err || stderr.toString()); - } else { - const cpuUsage = stdout.toString().split('\n'); - for (let i = 0; i < pids.length; i++) { - const processInfo = map.get(pids[i]); - processInfo.load = parseFloat(cpuUsage[i]); - } - - resolve(rootItem); - } - }); - } else { resolve(rootItem); } + }); + } + exec('which ps', {}, (err, stdout, stderr) => { + if (err || stderr) { + if (process.platform !== 'linux') { + reject(err || new Error(stderr.toString())); + } else { + const cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/ps.sh')); + exec(cmd, {}, (err, stdout, stderr) => { + if (err || stderr) { + reject(err || new Error(stderr.toString())); + } else { + parsePsOutput(stdout, addToTree); + calculateLinuxCpuUsage(); + } + }); + } + } else { + const ps = stdout.toString().trim(); + const args = '-ax -o pid=,ppid=,pcpu=,pmem=,command='; + + // Set numeric locale to ensure '.' is used as the decimal separator + exec(`${ps} ${args}`, { maxBuffer: 1000 * 1024, env: { LC_NUMERIC: 'en_US.UTF-8' } }, (err, stdout, stderr) => { + if (err || stderr) { + reject(err || new Error(stderr.toString())); + } else { + parsePsOutput(stdout, addToTree); + + if (process.platform === 'linux') { + calculateLinuxCpuUsage(); + } else { + resolve(rootItem); + } + } + }); } }); } }); } + +function parsePsOutput(stdout: string, addToTree: (pid: number, ppid: number, cmd: string, load: number, mem: number) => void): void { + const PID_CMD = /^\s*([0-9]+)\s+([0-9]+)\s+([0-9]+\.[0-9]+)\s+([0-9]+\.[0-9]+)\s+(.+)$/; + const lines = stdout.toString().split('\n'); + for (const line of lines) { + const matches = PID_CMD.exec(line.trim()); + if (matches && matches.length === 6) { + addToTree(parseInt(matches[1]), parseInt(matches[2]), matches[5], parseFloat(matches[3]), parseFloat(matches[4])); + } + } +} \ No newline at end of file diff --git a/src/vs/base/node/request.ts b/src/vs/base/node/request.ts index 6a8b5ad0780..17731debc5b 100644 --- a/src/vs/base/node/request.ts +++ b/src/vs/base/node/request.ts @@ -81,7 +81,7 @@ export function request(options: IRequestOptions, token: CancellationToken): Pro opts.auth = options.user + ':' + options.password; } - req = rawRequest(opts, (res: http.ClientResponse) => { + req = rawRequest(opts, (res: http.IncomingMessage) => { const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { request(assign({}, options, { @@ -136,7 +136,7 @@ export function download(filePath: string, context: IRequestContext): Promise((c, e) => { const out = createWriteStream(filePath); - out.once('finish', () => c(void 0)); + out.once('finish', () => c(undefined)); context.stream.once('error', e); context.stream.pipe(out); }); @@ -152,14 +152,14 @@ export function asText(context: IRequestContext): Promise { return c(null); } - let buffer: string[] = []; + const buffer: string[] = []; context.stream.on('data', (d: string) => buffer.push(d)); context.stream.on('end', () => c(buffer.join(''))); context.stream.on('error', e); }); } -export function asJson(context: IRequestContext): Promise { +export function asJson(context: IRequestContext): Promise { return new Promise((c, e) => { if (!isSuccess(context)) { return e('Server returned ' + context.res.statusCode); diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 32525743f10..593163b67c3 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -9,32 +9,47 @@ import { Emitter, Event } from 'vs/base/common/event'; import { ThrottledDelayer, timeout } from 'vs/base/common/async'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { mapToString, setToString } from 'vs/base/common/map'; -import { basename } from 'path'; -import { mark } from 'vs/base/common/performance'; -import { rename } from 'vs/base/node/pfs'; +import { basename } from 'vs/base/common/path'; +import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; +import { fill } from 'vs/base/common/arrays'; + +export enum StorageHint { + + // A hint to the storage that the storage + // does not exist on disk yet. This allows + // the storage library to improve startup + // time by not checking the storage for data. + STORAGE_DOES_NOT_EXIST +} export interface IStorageOptions { - path: string; - - logging?: IStorageLoggingOptions; + hint?: StorageHint; } -export interface IStorageLoggingOptions { - logError?: (error: string | Error) => void; - - trace?: boolean; - logTrace?: (msg: string) => void; - +export interface IUpdateRequest { + insert?: Map; + delete?: Set; } -enum StorageState { - None, - Initialized, - Closed +export interface IStorageItemsChangeEvent { + items: Map; +} + +export interface IStorageDatabase { + + readonly onDidChangeItemsExternal: Event; + + getItems(): Promise>; + updateItems(request: IUpdateRequest): Promise; + + close(recovery?: () => Map): Promise; + + checkIntegrity(full: boolean): Promise; } export interface IStorage extends IDisposable { + readonly items: Map; readonly size: number; readonly onDidChangeStorage: Event; @@ -46,29 +61,33 @@ export interface IStorage extends IDisposable { getBoolean(key: string, fallbackValue: boolean): boolean; getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getInteger(key: string, fallbackValue: number): number; - getInteger(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; - set(key: string, value: any): Thenable; - delete(key: string): Thenable; + set(key: string, value: string | boolean | number | undefined | null): Promise; + delete(key: string): Promise; - close(): Thenable; + close(): Promise; - getItems(): Promise>; checkIntegrity(full: boolean): Promise; } +enum StorageState { + None, + Initialized, + Closed +} + export class Storage extends Disposable implements IStorage { _serviceBrand: any; - private static readonly FLUSH_DELAY = 100; + private static readonly DEFAULT_FLUSH_DELAY = 100; - private _onDidChangeStorage: Emitter = this._register(new Emitter()); + private readonly _onDidChangeStorage: Emitter = this._register(new Emitter()); get onDidChangeStorage(): Event { return this._onDidChangeStorage.event; } private state = StorageState.None; - private storage: SQLiteStorageImpl; private cache: Map = new Map(); private flushDelayer: ThrottledDelayer; @@ -76,28 +95,78 @@ export class Storage extends Disposable implements IStorage { private pendingDeletes: Set = new Set(); private pendingInserts: Map = new Map(); - constructor(options: IStorageOptions) { + constructor( + protected database: IStorageDatabase, + private options: IStorageOptions = Object.create(null) + ) { super(); - this.storage = new SQLiteStorageImpl(options); + this.flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); - this.flushDelayer = this._register(new ThrottledDelayer(Storage.FLUSH_DELAY)); + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.database.onDidChangeItemsExternal(e => this.onDidChangeItemsExternal(e))); + } + + private onDidChangeItemsExternal(e: IStorageItemsChangeEvent): void { + // items that change external require us to update our + // caches with the values. we just accept the value and + // emit an event if there is a change. + e.items.forEach((value, key) => this.accept(key, value)); + } + + private accept(key: string, value: string): void { + if (this.state === StorageState.Closed) { + return; // Return early if we are already closed + } + + let changed = false; + + // Item got removed, check for deletion + if (isUndefinedOrNull(value)) { + changed = this.cache.delete(key); + } + + // Item got updated, check for change + else { + const currentValue = this.cache.get(key); + if (currentValue !== value) { + this.cache.set(key, value); + changed = true; + } + } + + // Signal to outside listeners + if (changed) { + this._onDidChangeStorage.fire(key); + } + } + + get items(): Map { + return this.cache; } get size(): number { return this.cache.size; } - init(): Promise { + async init(): Promise { if (this.state !== StorageState.None) { return Promise.resolve(); // either closed or already initialized } this.state = StorageState.Initialized; - return this.storage.getItems().then(items => { - this.cache = items; - }); + if (this.options.hint === StorageHint.STORAGE_DOES_NOT_EXIST) { + // return early if we know the storage file does not exist. this is a performance + // optimization to not load all items of the underlying storage if we know that + // there can be no items because the storage does not exist. + return Promise.resolve(); + } + + this.cache = await this.database.getItems(); } get(key: string, fallbackValue: string): string; @@ -124,9 +193,9 @@ export class Storage extends Disposable implements IStorage { return value === 'true'; } - getInteger(key: string, fallbackValue: number): number; - getInteger(key: string, fallbackValue?: number): number | undefined; - getInteger(key: string, fallbackValue?: number): number | undefined { + getNumber(key: string, fallbackValue: number): number; + getNumber(key: string, fallbackValue?: number): number | undefined; + getNumber(key: string, fallbackValue?: number): number | undefined { const value = this.get(key); if (isUndefinedOrNull(value)) { @@ -136,7 +205,7 @@ export class Storage extends Disposable implements IStorage { return parseInt(value, 10); } - set(key: string, value: any): Thenable { + set(key: string, value: string | boolean | number | null | undefined): Promise { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -167,7 +236,7 @@ export class Storage extends Disposable implements IStorage { return this.flushDelayer.trigger(() => this.flushPending()); } - delete(key: string): Thenable { + delete(key: string): Promise { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -191,7 +260,7 @@ export class Storage extends Disposable implements IStorage { return this.flushDelayer.trigger(() => this.flushPending()); } - close(): Thenable { + async close(): Promise { if (this.state === StorageState.Closed) { return Promise.resolve(); // return if already closed } @@ -202,11 +271,22 @@ export class Storage extends Disposable implements IStorage { // Trigger new flush to ensure data is persisted and then close // even if there is an error flushing. We must always ensure // the DB is closed to avoid corruption. - const onDone = () => this.storage.close(); - return this.flushDelayer.trigger(() => this.flushPending(), 0 /* immediately */).then(onDone, onDone); + // + // Recovery: we pass our cache over as recovery option in case + // the DB is not healthy. + try { + await this.flushDelayer.trigger(() => this.flushPending(), 0 /* as soon as possible */); + } catch (error) { + // Ignore + } + + await this.database.close(() => this.cache); } - private flushPending(): Thenable { + private flushPending(): Promise { + if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) { + return Promise.resolve(); // return early if nothing to do + } // Get pending data const updateRequest: IUpdateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes }; @@ -216,56 +296,79 @@ export class Storage extends Disposable implements IStorage { this.pendingInserts = new Map(); // Update in storage - return this.storage.updateItems(updateRequest); - } - - getItems(): Promise> { - return this.storage.getItems(); + return this.database.updateItems(updateRequest); } checkIntegrity(full: boolean): Promise { - return this.storage.checkIntegrity(full); + return this.database.checkIntegrity(full); } } -export interface IUpdateRequest { - readonly insert?: Map; - readonly delete?: Set; +interface IDatabaseConnection { + db: Database; + + isInMemory: boolean; + + isErroneous?: boolean; + lastError?: string; } -export class SQLiteStorageImpl { +export interface ISQLiteStorageDatabaseOptions { + logging?: ISQLiteStorageDatabaseLoggingOptions; +} - private static measuredRequireDuration: boolean; // TODO@Ben remove me after a while +export interface ISQLiteStorageDatabaseLoggingOptions { + logError?: (error: string | Error) => void; + logTrace?: (msg: string) => void; +} + +export class SQLiteStorageDatabase implements IStorageDatabase { + + static IN_MEMORY_PATH = ':memory:'; + + get onDidChangeItemsExternal(): Event { return Event.None; } // since we are the only client, there can be no external changes private static BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY + private static MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement - private db: Promise; + private path: string; private name: string; - private logger: SQLiteStorageLogger; - constructor(private options: IStorageOptions) { - this.name = basename(options.path); - this.logger = new SQLiteStorageLogger(options.logging); - this.db = this.open(); + private logger: SQLiteStorageDatabaseLogger; + + private whenConnected: Promise; + + constructor(path: string, options: ISQLiteStorageDatabaseOptions = Object.create(null)) { + this.path = path; + this.name = basename(path); + + this.logger = new SQLiteStorageDatabaseLogger(options.logging); + + this.whenConnected = this.connect(path); } - getItems(): Promise> { - return this.db.then(db => { - const items = new Map(); + async getItems(): Promise> { + const connection = await this.whenConnected; - return this.all(db, 'SELECT * FROM ItemTable').then(rows => { - rows.forEach(row => items.set(row.key, row.value)); + const items = new Map(); - if (this.logger.isTracing) { - this.logger.trace(`[storage ${this.name}] getItems(): ${mapToString(items)}`); - } + const rows = await this.all(connection, 'SELECT * FROM ItemTable'); + rows.forEach(row => items.set(row.key, row.value)); - return items; - }); - }); + if (this.logger.isTracing) { + this.logger.trace(`[storage ${this.name}] getItems(): ${items.size} rows`); + } + + return items; } - updateItems(request: IUpdateRequest): Promise { + async updateItems(request: IUpdateRequest): Promise { + const connection = await this.whenConnected; + + return this.doUpdateItems(connection, request); + } + + private doUpdateItems(connection: IDatabaseConnection, request: IUpdateRequest): Promise { let updateCount = 0; if (request.insert) { updateCount += request.insert.size; @@ -282,155 +385,260 @@ export class SQLiteStorageImpl { this.logger.trace(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); } - return this.db.then(db => { - return this.transaction(db, () => { - if (request.insert && request.insert.size > 0) { - this.prepare(db, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { - request.insert!.forEach((value, key) => { - stmt.run([key, value]); - }); - }); - } + return this.transaction(connection, () => { - if (request.delete && request.delete.size) { - this.prepare(db, 'DELETE FROM ItemTable WHERE key=?', stmt => { - request.delete!.forEach(key => { - stmt.run(key); + // INSERT + if (request.insert && request.insert.size > 0) { + const keysValuesChunks: (string[])[] = []; + keysValuesChunks.push([]); // seed with initial empty chunk + + // Split key/values into chunks of SQLiteStorageDatabase.MAX_HOST_PARAMETERS + // so that we can efficiently run the INSERT with as many HOST parameters as possible + let currentChunkIndex = 0; + request.insert.forEach((value, key) => { + let keyValueChunk = keysValuesChunks[currentChunkIndex]; + + if (keyValueChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { + currentChunkIndex++; + keyValueChunk = []; + keysValuesChunks.push(keyValueChunk); + } + + keyValueChunk.push(key, value); + }); + + keysValuesChunks.forEach(keysValuesChunk => { + this.prepare(connection, `INSERT INTO ItemTable VALUES ${fill(keysValuesChunk.length / 2, '(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => { + const keys: string[] = []; + let length = 0; + request.insert!.forEach((value, key) => { + keys.push(key); + length += value.length; }); + + return `Keys: ${keys.join(', ')} Length: ${length}`; }); - } - }); + }); + } + + // DELETE + if (request.delete && request.delete.size) { + const keysChunks: (string[])[] = []; + keysChunks.push([]); // seed with initial empty chunk + + // Split keys into chunks of SQLiteStorageDatabase.MAX_HOST_PARAMETERS + // so that we can efficiently run the DELETE with as many HOST parameters + // as possible + let currentChunkIndex = 0; + request.delete.forEach(key => { + let keyChunk = keysChunks[currentChunkIndex]; + + if (keyChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { + currentChunkIndex++; + keyChunk = []; + keysChunks.push(keyChunk); + } + + keyChunk.push(key); + }); + + keysChunks.forEach(keysChunk => { + this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${fill(keysChunk.length, '?').join(',')})`, stmt => stmt.run(keysChunk), () => { + const keys: string[] = []; + request.delete!.forEach(key => { + keys.push(key); + }); + + return `Keys: ${keys.join(', ')}`; + }); + }); + } }); } - close(): Promise { + async close(recovery?: () => Map): Promise { this.logger.trace(`[storage ${this.name}] close()`); - return this.db.then(db => { - return new Promise((resolve, reject) => { - db.close(error => { - if (error) { - this.logger.error(`[storage ${this.name}] close(): ${error}`); + const connection = await this.whenConnected; - return reject(error); - } + return this.doClose(connection, recovery); + } + private doClose(connection: IDatabaseConnection, recovery?: () => Map): Promise { + return new Promise((resolve, reject) => { + connection.db.close(closeError => { + if (closeError) { + this.handleSQLiteError(connection, closeError, `[storage ${this.name}] close(): ${closeError}`); + } + + // Return early if this storage was created only in-memory + // e.g. when running tests we do not need to backup. + if (this.path === SQLiteStorageDatabase.IN_MEMORY_PATH) { return resolve(); - }); + } + + // If the DB closed successfully and we are not running in-memory + // and the DB did not get errors during runtime, make a backup + // of the DB so that we can use it as fallback in case the actual + // DB becomes corrupt in the future. + if (!connection.isErroneous && !connection.isInMemory) { + return this.backup().then(resolve, error => { + this.logger.error(`[storage ${this.name}] backup(): ${error}`); + + return resolve(); // ignore failing backup + }); + } + + // Recovery: if we detected errors while using the DB or we are using + // an inmemory DB (as a fallback to not being able to open the DB initially) + // and we have a recovery function provided, we recreate the DB with this + // data to recover all known data without loss if possible. + if (typeof recovery === 'function') { + + // Delete the existing DB. If the path does not exist or fails to + // be deleted, we do not try to recover anymore because we assume + // that the path is no longer writeable for us. + return unlink(this.path).then(() => { + + // Re-open the DB fresh + return this.doConnect(this.path).then(recoveryConnection => { + const closeRecoveryConnection = () => { + return this.doClose(recoveryConnection, undefined /* do not attempt to recover again */); + }; + + // Store items + return this.doUpdateItems(recoveryConnection, { insert: recovery() }).then(() => closeRecoveryConnection(), error => { + + // In case of an error updating items, still ensure to close the connection + // to prevent SQLITE_BUSY errors when the connection is restablished + closeRecoveryConnection(); + + return Promise.reject(error); + }); + }); + }).then(resolve, reject); + } + + // Finally without recovery we just reject + return reject(closeError || new Error('Database has errors or is in-memory without recovery option')); }); }); } - checkIntegrity(full: boolean): Promise { + private backup(): Promise { + const backupPath = this.toBackupPath(this.path); + + return copy(this.path, backupPath); + } + + private toBackupPath(path: string): string { + return `${path}.backup`; + } + + async checkIntegrity(full: boolean): Promise { this.logger.trace(`[storage ${this.name}] checkIntegrity(full: ${full})`); - return this.db.then(db => { - return this.get(db, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { - return full ? row['integrity_check'] : row['quick_check']; - }); - }); + const connection = await this.whenConnected; + const row = await this.get(connection, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check'); + + const integrity = full ? row['integrity_check'] : row['quick_check']; + + if (connection.isErroneous) { + return `${integrity} (last error: ${connection.lastError})`; + } + + if (connection.isInMemory) { + return `${integrity} (in-memory!)`; + } + + return integrity; } - private open(): Promise { - this.logger.trace(`[storage ${this.name}] open()`); + private connect(path: string, retryOnBusy: boolean = true): Promise { + this.logger.trace(`[storage ${this.name}] open(${path}, retryOnBusy: ${retryOnBusy})`); - return new Promise((resolve, reject) => { - const fallbackToInMemoryDatabase = (error: Error) => { - this.logger.error(`[storage ${this.name}] open(): Error (open DB): ${error}`); - this.logger.error(`[storage ${this.name}] open(): Falling back to in-memory DB`); + return this.doConnect(path).then(undefined, error => { + this.logger.error(`[storage ${this.name}] open(): Unable to open DB due to ${error}`); - // In case of any error to open the DB, use an in-memory - // DB so that we always have a valid DB to talk to. - this.doOpen(':memory:').then(resolve, reject); - }; - - this.doOpen(this.options.path).then(resolve, error => { - - // TODO@Ben check if this is still happening. This error code should only arise if - // another process is locking the same DB we want to open at that time. This typically - // never happens because a DB connection is limited per window. However, in the event - // of a window reload, it may be possible that the previous connection was not properly - // closed while the new connection is already established. - if (error.code === 'SQLITE_BUSY') { - this.logger.error(`[storage ${this.name}] open(): Retrying after ${SQLiteStorageImpl.BUSY_OPEN_TIMEOUT}ms due to SQLITE_BUSY`); - - // Retry after 2s if the DB is busy - timeout(SQLiteStorageImpl.BUSY_OPEN_TIMEOUT).then(() => this.doOpen(this.options.path).then(resolve, fallbackToInMemoryDatabase)); - } - - // This error code indicates that even though the DB file exists, - // SQLite cannot open it and signals it is corrupt or not a DB. - else if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { - this.logger.error(`[storage ${this.name}] open(): Recreating DB due to ${error.code}`); - - // Move corrupt DB to different filename and start fresh - const randomSuffix = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 4); - rename(this.options.path, `${this.options.path}.${randomSuffix}.corrupt`) - .then(() => this.doOpen(this.options.path)).then(resolve, fallbackToInMemoryDatabase); - } - - // Otherwise give up and fallback to in-memory DB - else { - fallbackToInMemoryDatabase(error); - } - }); - }); - } - - private doOpen(path: string): Promise { - // TODO@Ben clean up performance markers - return new Promise((resolve, reject) => { - let measureRequireDuration = false; - if (!SQLiteStorageImpl.measuredRequireDuration) { - SQLiteStorageImpl.measuredRequireDuration = true; - measureRequireDuration = true; - - mark('willRequireSQLite'); + // SQLITE_BUSY should only arise if another process is locking the same DB we want + // to open at that time. This typically never happens because a DB connection is + // limited per window. However, in the event of a window reload, it may be possible + // that the previous connection was not properly closed while the new connection is + // already established. + // + // In this case we simply wait for some time and retry once to establish the connection. + // + if (error.code === 'SQLITE_BUSY' && retryOnBusy) { + return timeout(SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT).then(() => this.connect(path, false /* not another retry */)); } - import('vscode-sqlite3').then(sqlite3 => { - if (measureRequireDuration) { - mark('didRequireSQLite'); - } - const db = new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { - if (error) { - return reject(error); - } + // Otherwise, best we can do is to recover from a backup if that exists, as such we + // move the DB to a different filename and try to load from backup. If that fails, + // a new empty DB is being created automatically. + // + // The final fallback is to use an in-memory DB which should only happen if the target + // folder is really not writeable for us. + // + return unlink(path) + .then(() => renameIgnoreError(this.toBackupPath(path), path)) + .then(() => this.doConnect(path)) + .then(undefined, error => { + this.logger.error(`[storage ${this.name}] open(): Unable to use backup due to ${error}`); - // The following exec() statement serves two purposes: - // - create the DB if it does not exist yet - // - validate that the DB is not corrupt (the open() call does not throw otherwise) - mark('willSetupSQLiteSchema'); - this.exec(db, [ - 'PRAGMA user_version = 1;', - 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' - ].join('')).then(() => { - mark('didSetupSQLiteSchema'); - - resolve(db); - }, error => { - mark('didSetupSQLiteSchema'); - - reject(error); - }); + // In case of any error to open the DB, use an in-memory + // DB so that we always have a valid DB to talk to. + return this.doConnect(SQLiteStorageDatabase.IN_MEMORY_PATH); }); + }); + } + + private handleSQLiteError(connection: IDatabaseConnection, error: Error & { code?: string }, msg: string): void { + connection.isErroneous = true; + connection.lastError = msg; + + this.logger.error(msg); + } + + private doConnect(path: string): Promise { + return new Promise((resolve, reject) => { + import('vscode-sqlite3').then(sqlite3 => { + const connection: IDatabaseConnection = { + db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + if (error) { + return connection.db ? connection.db.close(() => reject(error)) : reject(error); + } + + // The following exec() statement serves two purposes: + // - create the DB if it does not exist yet + // - validate that the DB is not corrupt (the open() call does not throw otherwise) + return this.exec(connection, [ + 'PRAGMA user_version = 1;', + 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' + ].join('')).then(() => { + return resolve(connection); + }, error => { + return connection.db.close(() => reject(error)); + }); + }), + isInMemory: path === SQLiteStorageDatabase.IN_MEMORY_PATH + }; // Errors - db.on('error', error => this.logger.error(`[storage ${this.name}] Error (event): ${error}`)); + connection.db.on('error', error => this.handleSQLiteError(connection, error, `[storage ${this.name}] Error (event): ${error}`)); // Tracing if (this.logger.isTracing) { - db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); + connection.db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); } - }); + }, reject); }); } - private exec(db: Database, sql: string): Promise { + private exec(connection: IDatabaseConnection, sql: string): Promise { return new Promise((resolve, reject) => { - db.exec(sql, error => { + connection.db.exec(sql, error => { if (error) { - this.logger.error(`[storage ${this.name}] exec(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] exec(): ${error}`); return reject(error); } @@ -440,11 +648,11 @@ export class SQLiteStorageImpl { }); } - private get(db: Database, sql: string): Promise { + private get(connection: IDatabaseConnection, sql: string): Promise { return new Promise((resolve, reject) => { - db.get(sql, (error, row) => { + connection.db.get(sql, (error, row) => { if (error) { - this.logger.error(`[storage ${this.name}] get(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] get(): ${error}`); return reject(error); } @@ -454,11 +662,11 @@ export class SQLiteStorageImpl { }); } - private all(db: Database, sql: string): Promise<{ key: string, value: string }[]> { + private all(connection: IDatabaseConnection, sql: string): Promise<{ key: string, value: string }[]> { return new Promise((resolve, reject) => { - db.all(sql, (error, rows) => { + connection.db.all(sql, (error, rows) => { if (error) { - this.logger.error(`[storage ${this.name}] all(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] all(): ${error}`); return reject(error); } @@ -468,16 +676,16 @@ export class SQLiteStorageImpl { }); } - private transaction(db: Database, transactions: () => void): Promise { + private transaction(connection: IDatabaseConnection, transactions: () => void): Promise { return new Promise((resolve, reject) => { - db.serialize(() => { - db.run('BEGIN TRANSACTION'); + connection.db.serialize(() => { + connection.db.run('BEGIN TRANSACTION'); transactions(); - db.run('END TRANSACTION', error => { + connection.db.run('END TRANSACTION', error => { if (error) { - this.logger.error(`[storage ${this.name}] transaction(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] transaction(): ${error}`); return reject(error); } @@ -488,11 +696,11 @@ export class SQLiteStorageImpl { }); } - private prepare(db: Database, sql: string, runCallback: (stmt: Statement) => void): void { - const stmt = db.prepare(sql); + private prepare(connection: IDatabaseConnection, sql: string, runCallback: (stmt: Statement) => void, errorDetails: () => string): void { + const stmt = connection.db.prepare(sql); - const statementErrorListener = error => { - this.logger.error(`[storage ${this.name}] prepare(): ${error} (${sql})`); + const statementErrorListener = (error: Error) => { + this.handleSQLiteError(connection, error, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`); }; stmt.on('error', statementErrorListener); @@ -509,64 +717,56 @@ export class SQLiteStorageImpl { } } -class SQLiteStorageLogger { - private readonly logTrace: boolean; - private readonly logError: boolean; +class SQLiteStorageDatabaseLogger { + private readonly logTrace: (msg: string) => void; + private readonly logError: (error: string | Error) => void; - constructor(private readonly options?: IStorageLoggingOptions) { - this.logTrace = !!(options && options.logTrace); - this.logError = !!(options && options.logError); + constructor(options?: ISQLiteStorageDatabaseLoggingOptions) { + if (options && typeof options.logTrace === 'function') { + this.logTrace = options.logTrace; + } + + if (options && typeof options.logError === 'function') { + this.logError = options.logError; + } } get isTracing(): boolean { - return this.logTrace; + return !!this.logTrace; } trace(msg: string): void { - if (this.logTrace && this.options && this.options.logTrace) { - this.options.logTrace(msg); + if (this.logTrace) { + this.logTrace(msg); } } error(error: string | Error): void { - if (this.logError && this.options && this.options.logError) { - this.options.logError(error); + if (this.logError) { + this.logError(error); } } } -export class NullStorage extends Disposable implements IStorage { +export class InMemoryStorageDatabase implements IStorageDatabase { - readonly size = 0; - readonly onDidChangeStorage = Event.None; + readonly onDidChangeItemsExternal = Event.None; private items = new Map(); - init(): Promise { return Promise.resolve(); } - - get(key: string, fallbackValue: string): string; - get(key: string, fallbackValue?: string): string | undefined; - get(key: string, fallbackValue?: string): string | undefined { - return void 0; + getItems(): Promise> { + return Promise.resolve(this.items); } - getBoolean(key: string, fallbackValue: boolean): boolean; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; - getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { - return void 0; - } + updateItems(request: IUpdateRequest): Promise { + if (request.insert) { + request.insert.forEach((value, key) => this.items.set(key, value)); + } - getInteger(key: string, fallbackValue: number): number; - getInteger(key: string, fallbackValue?: number): number | undefined; - getInteger(key: string, fallbackValue?: number): number | undefined { - return void 0; - } + if (request.delete) { + request.delete.forEach(key => this.items.delete(key)); + } - set(key: string, value: any): Promise { - return Promise.resolve(); - } - - delete(key: string): Promise { return Promise.resolve(); } @@ -574,10 +774,6 @@ export class NullStorage extends Disposable implements IStorage { return Promise.resolve(); } - getItems(): Promise> { - return Promise.resolve(this.items); - } - checkIntegrity(full: boolean): Promise { return Promise.resolve('ok'); } diff --git a/src/vs/base/node/stream.ts b/src/vs/base/node/stream.ts index 845afdab8e9..12f66a76556 100644 --- a/src/vs/base/node/stream.ts +++ b/src/vs/base/node/stream.ts @@ -5,63 +5,6 @@ import * as fs from 'fs'; -export interface ReadResult { - buffer: Buffer | null; - bytesRead: number; -} - -/** - * Reads totalBytes from the provided file. - */ -export function readExactlyByFile(file: string, totalBytes: number): Promise { - return new Promise((resolve, reject) => { - fs.open(file, 'r', null, (err, fd) => { - if (err) { - return reject(err); - } - - function end(err: Error | null, resultBuffer: Buffer | null, bytesRead: number): void { - fs.close(fd, closeError => { - if (closeError) { - return reject(closeError); - } - - if (err && (err).code === 'EISDIR') { - return reject(err); // we want to bubble this error up (file is actually a folder) - } - - return resolve({ buffer: resultBuffer, bytesRead }); - }); - } - - const buffer = Buffer.allocUnsafe(totalBytes); - let offset = 0; - - function readChunk(): void { - fs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => { - if (err) { - return end(err, null, 0); - } - - if (bytesRead === 0) { - return end(null, buffer, offset); - } - - offset += bytesRead; - - if (offset === totalBytes) { - return end(null, buffer, offset); - } - - return readChunk(); - }); - } - - readChunk(); - }); - }); -} - /** * Reads a file until a matching string is found. * @@ -92,7 +35,7 @@ export function readToMatchingString(file: string, matchingString: string, chunk }); } - let buffer = Buffer.allocUnsafe(maximumBytesToRead); + const buffer = Buffer.allocUnsafe(maximumBytesToRead); let offset = 0; function readChunk(): void { diff --git a/src/vs/platform/node/test/fixtures/extract.zip b/src/vs/base/node/test/fixtures/extract.zip similarity index 100% rename from src/vs/platform/node/test/fixtures/extract.zip rename to src/vs/base/node/test/fixtures/extract.zip diff --git a/src/vs/platform/node/test/zip.test.ts b/src/vs/base/node/test/zip.test.ts similarity index 86% rename from src/vs/platform/node/test/zip.test.ts rename to src/vs/base/node/test/zip.test.ts index cec3c261038..f79bddaa98c 100644 --- a/src/vs/platform/node/test/zip.test.ts +++ b/src/vs/base/node/test/zip.test.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as path from 'path'; +import * as path from 'vs/base/common/path'; import * as os from 'os'; -import { extract } from 'vs/platform/node/zip'; +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'; import { createCancelablePromise } from 'vs/base/common/async'; @@ -21,7 +20,7 @@ suite('Zip', () => { const fixture = path.join(fixtures, 'extract.zip'); const target = path.join(os.tmpdir(), generateUuid()); - return createCancelablePromise(token => extract(fixture, target, {}, new NullLogService(), token) + return createCancelablePromise(token => extract(fixture, target, {}, token) .then(() => exists(path.join(target, 'extension'))) .then(exists => assert(exists)) .then(() => rimraf(target))); diff --git a/src/vs/base/node/watcher.ts b/src/vs/base/node/watcher.ts new file mode 100644 index 00000000000..b51f73c6eaa --- /dev/null +++ b/src/vs/base/node/watcher.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { join, basename } from 'vs/base/common/path'; +import { watch } from 'fs'; +import { isMacintosh } from 'vs/base/common/platform'; +import { normalizeNFC } from 'vs/base/common/normalization'; +import { toDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { exists, readdir } from 'vs/base/node/pfs'; + +export function watchFile(path: string, onChange: (type: 'changed' | 'deleted', path: string) => void, onError: (error: string) => void): IDisposable { + return doWatchNonRecursive({ path, isDirectory: false }, onChange, onError); +} + +export function watchFolder(path: string, onChange: (type: 'added' | 'changed' | 'deleted', path: string) => void, onError: (error: string) => void): IDisposable { + return doWatchNonRecursive({ path, isDirectory: true }, onChange, onError); +} + +export const CHANGE_BUFFER_DELAY = 100; + +function doWatchNonRecursive(file: { path: string, isDirectory: boolean }, onChange: (type: 'added' | 'changed' | 'deleted', path: string) => void, onError: (error: string) => void): IDisposable { + const originalFileName = basename(file.path); + const mapPathToStatDisposable = new Map(); + + let disposed = false; + let watcherDisposables: IDisposable[] = [toDisposable(() => { + mapPathToStatDisposable.forEach(disposable => dispose(disposable)); + mapPathToStatDisposable.clear(); + })]; + + try { + + // Creating watcher can fail with an exception + const watcher = watch(file.path); + watcherDisposables.push(toDisposable(() => { + watcher.removeAllListeners(); + watcher.close(); + })); + + // Folder: resolve children to emit proper events + const folderChildren: Set = new Set(); + if (file.isDirectory) { + readdir(file.path).then(children => children.forEach(child => folderChildren.add(child))); + } + + watcher.on('error', (code: number, signal: string) => { + if (!disposed) { + onError(`Failed to watch ${file.path} for changes using fs.watch() (${code}, ${signal})`); + } + }); + + watcher.on('change', (type, raw) => { + if (disposed) { + return; // ignore if already disposed + } + + // Normalize file name + let changedFileName: string = ''; + if (raw) { // https://github.com/Microsoft/vscode/issues/38191 + changedFileName = raw.toString(); + if (isMacintosh) { + // Mac: uses NFD unicode form on disk, but we want NFC + // See also https://github.com/nodejs/node/issues/2165 + changedFileName = normalizeNFC(changedFileName); + } + } + + if (!changedFileName || (type !== 'change' && type !== 'rename')) { + return; // ignore unexpected events + } + + // File path: use path directly for files and join with changed file name otherwise + const changedFilePath = file.isDirectory ? join(file.path, changedFileName) : file.path; + + // File + if (!file.isDirectory) { + if (type === 'rename' || changedFileName !== originalFileName) { + // The file was either deleted or renamed. Many tools apply changes to files in an + // atomic way ("Atomic Save") by first renaming the file to a temporary name and then + // renaming it back to the original name. Our watcher will detect this as a rename + // and then stops to work on Mac and Linux because the watcher is applied to the + // inode and not the name. The fix is to detect this case and trying to watch the file + // again after a certain delay. + // In addition, we send out a delete event if after a timeout we detect that the file + // does indeed not exist anymore. + + const timeoutHandle = setTimeout(async () => { + const fileExists = await exists(changedFilePath); + + if (disposed) { + return; // ignore if disposed by now + } + + // File still exists, so emit as change event and reapply the watcher + if (fileExists) { + onChange('changed', changedFilePath); + + watcherDisposables = [doWatchNonRecursive(file, onChange, onError)]; + } + + // File seems to be really gone, so emit a deleted event + else { + onChange('deleted', changedFilePath); + } + }, CHANGE_BUFFER_DELAY); + + // Very important to dispose the watcher which now points to a stale inode + // and wire in a new disposable that tracks our timeout that is installed + dispose(watcherDisposables); + watcherDisposables = [toDisposable(() => clearTimeout(timeoutHandle))]; + } else { + onChange('changed', changedFilePath); + } + } + + // Folder + else { + + // Children add/delete + if (type === 'rename') { + + // Cancel any previous stats for this file path if existing + const statDisposable = mapPathToStatDisposable.get(changedFilePath); + if (statDisposable) { + dispose(statDisposable); + } + + // Wait a bit and try see if the file still exists on disk to decide on the resulting event + const timeoutHandle = setTimeout(async () => { + mapPathToStatDisposable.delete(changedFilePath); + + const fileExists = await exists(changedFilePath); + + if (disposed) { + return; // ignore if disposed by now + } + + // Figure out the correct event type: + // File Exists: either 'added' or 'changed' if known before + // File Does not Exist: always 'deleted' + let type: 'added' | 'deleted' | 'changed'; + if (fileExists) { + if (folderChildren.has(changedFileName)) { + type = 'changed'; + } else { + type = 'added'; + folderChildren.add(changedFileName); + } + } else { + folderChildren.delete(changedFileName); + type = 'deleted'; + } + + onChange(type, changedFilePath); + }, CHANGE_BUFFER_DELAY); + + mapPathToStatDisposable.set(changedFilePath, toDisposable(() => clearTimeout(timeoutHandle))); + } + + // Other events + else { + + // Figure out the correct event type: if this is the + // first time we see this child, it can only be added + let type: 'added' | 'changed'; + if (folderChildren.has(changedFileName)) { + type = 'changed'; + } else { + type = 'added'; + folderChildren.add(changedFileName); + } + + onChange(type, changedFilePath); + } + } + }); + } catch (error) { + exists(file.path).then(exists => { + if (exists && !disposed) { + onError(`Failed to watch ${file.path} for changes using fs.watch() (${error.toString()})`); + } + }); + } + + return toDisposable(() => { + disposed = true; + + watcherDisposables = dispose(watcherDisposables); + }); +} \ No newline at end of file diff --git a/src/vs/platform/node/zip.ts b/src/vs/base/node/zip.ts similarity index 80% rename from src/vs/platform/node/zip.ts rename to src/vs/base/node/zip.ts index fc1a1a87af1..3c474dc0343 100644 --- a/src/vs/platform/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -4,16 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as path from 'path'; +import * as path from 'vs/base/common/path'; import { createWriteStream, WriteStream } from 'fs'; import { Readable } from 'stream'; -import { nfcall, ninvoke, Sequencer, createCancelablePromise } from 'vs/base/common/async'; +import { Sequencer, createCancelablePromise } from 'vs/base/common/async'; import { mkdirp, rimraf } from 'vs/base/node/pfs'; import { open as _openZip, Entry, ZipFile } from 'yauzl'; import * as yazl from 'yazl'; -import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { once } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; export interface IExtractOptions { overwrite?: boolean; @@ -50,7 +49,7 @@ export class ExtractError extends Error { } function modeFromEntry(entry: Entry) { - let attr = entry.externalFileAttributes >> 16 || 33188; + const attr = entry.externalFileAttributes >> 16 || 33188; return [448 /* S_IRWXU */, 56 /* S_IRWXG */, 7 /* S_IRWXO */] .map(mask => attr & mask) @@ -62,7 +61,7 @@ function toExtractError(err: Error): ExtractError { return err; } - let type: ExtractErrorType | undefined = void 0; + let type: ExtractErrorType | undefined = undefined; if (/end of central directory record signature not found/.test(err.message)) { type = 'CorruptZip'; @@ -81,13 +80,13 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa let istream: WriteStream; - once(token.onCancellationRequested)(() => { + Event.once(token.onCancellationRequested)(() => { if (istream) { - istream.close(); + istream.destroy(); } }); - return Promise.resolve(mkdirp(targetDirName, void 0, token)).then(() => new Promise((c, e) => { + return Promise.resolve(mkdirp(targetDirName, undefined, token)).then(() => new Promise((c, e) => { if (token.isCancellationRequested) { return; } @@ -104,12 +103,11 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa })); } -function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, logService: ILogService, token: CancellationToken): Promise { +function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, token: CancellationToken): Promise { let last = createCancelablePromise(() => Promise.resolve()); let extractedEntriesCount = 0; - once(token.onCancellationRequested)(() => { - logService.debug(targetPath, 'Cancelled.'); + Event.once(token.onCancellationRequested)(() => { last.cancel(); zipfile.close(); }); @@ -151,11 +149,11 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, log // 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)).then(null, e)); + last = createCancelablePromise(token => mkdirp(targetFileName, undefined, token).then(() => readNextEntry(token)).then(undefined, e)); return; } - const stream = ninvoke(zipfile, zipfile.openReadStream, entry); + const stream = openZipStream(zipfile, entry); const mode = modeFromEntry(entry); last = createCancelablePromise(token => throttler.queue(() => stream.then(stream => extractEntry(stream, fileName, mode, targetPath, options, token).then(() => readNextEntry(token)))).then(null!, e)); @@ -164,8 +162,27 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, log } function openZip(zipFile: string, lazy: boolean = false): Promise { - return nfcall(_openZip, zipFile, lazy ? { lazyEntries: true } : void 0) - .then(null, err => Promise.reject(toExtractError(err))); + return new Promise((resolve, reject) => { + _openZip(zipFile, lazy ? { lazyEntries: true } : undefined, (error?: Error, zipfile?: ZipFile) => { + if (error) { + reject(toExtractError(error)); + } else { + resolve(zipfile); + } + }); + }); +} + +function openZipStream(zipFile: ZipFile, entry: Entry): Promise { + return new Promise((resolve, reject) => { + zipFile.openReadStream(entry, (error?: Error, stream?: Readable) => { + if (error) { + reject(toExtractError(error)); + } else { + resolve(stream); + } + }); + }); } export interface IFile { @@ -195,7 +212,7 @@ export function zip(zipPath: string, files: IFile[]): Promise { }); } -export function extract(zipPath: string, targetPath: string, options: IExtractOptions = {}, logService: ILogService, token: CancellationToken): Promise { +export function extract(zipPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise { const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : ''); let promise = openZip(zipPath, true); @@ -204,7 +221,7 @@ export function extract(zipPath: string, targetPath: string, options: IExtractOp promise = promise.then(zipfile => rimraf(targetPath).then(() => zipfile)); } - return promise.then(zipfile => extractZip(zipfile, targetPath, { sourcePathRegex }, logService, token)); + return promise.then(zipfile => extractZip(zipfile, targetPath, { sourcePathRegex }, token)); } function read(zipPath: string, filePath: string): Promise { @@ -212,7 +229,7 @@ function read(zipPath: string, filePath: string): Promise { return new Promise((c, e) => { zipfile.on('entry', (entry: Entry) => { if (entry.fileName === filePath) { - ninvoke(zipfile, zipfile.openReadStream, entry).then(stream => c(stream), err => e(err)); + openZipStream(zipfile, entry).then(stream => c(stream), err => e(err)); } }); @@ -226,7 +243,7 @@ export function buffer(zipPath: string, filePath: string): Promise { return new Promise((c, e) => { const buffers: Buffer[] = []; stream.once('error', e); - stream.on('data', b => buffers.push(b as Buffer)); + stream.on('data', (b: Buffer) => buffers.push(b)); stream.on('end', () => c(Buffer.concat(buffers))); }); }); diff --git a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts index b4f83b7467c..26212a79fe1 100644 --- a/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-browser/contextmenu.ts @@ -37,7 +37,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions): void } function createItem(item: IContextMenuItem, processedItems: IContextMenuItem[]): ISerializableContextMenuItem { - const serializableItem = { + const serializableItem: ISerializableContextMenuItem = { id: processedItems.length, label: item.label, type: item.type, @@ -45,7 +45,7 @@ function createItem(item: IContextMenuItem, processedItems: IContextMenuItem[]): checked: item.checked, enabled: typeof item.enabled === 'boolean' ? item.enabled : true, visible: typeof item.visible === 'boolean' ? item.visible : true - } as ISerializableContextMenuItem; + }; processedItems.push(item); diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index e2dea11301d..f375750dc20 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -12,11 +12,16 @@ export function registerContextMenuListener(): void { 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, + x: options ? options.x : undefined, + y: options ? options.y : undefined, + positioningItem: options ? options.positioningItem : undefined, callback: () => { - event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL, contextMenuId); + // Workaround for https://github.com/Microsoft/vscode/issues/72447 + // It turns out that the menu gets GC'ed if not referenced anymore + // As such we drag it into this scope so that it is not being GC'ed + if (menu) { + event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL, contextMenuId); + } } }); }); diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts new file mode 100644 index 00000000000..f22f6c0aa73 --- /dev/null +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -0,0 +1,814 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { IMessagePassingProtocol, IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; +import { VSBuffer } from 'vs/base/common/buffer'; +import * as platform from 'vs/base/common/platform'; + +declare var process: any; + +export interface ISocket { + onData(listener: (e: VSBuffer) => void): IDisposable; + onClose(listener: () => void): IDisposable; + onEnd(listener: () => void): IDisposable; + write(buffer: VSBuffer): void; + end(): void; + dispose(): void; +} + +let emptyBuffer: VSBuffer | null = null; +function getEmptyBuffer(): VSBuffer { + if (!emptyBuffer) { + emptyBuffer = VSBuffer.alloc(0); + } + return emptyBuffer; +} + +export class ChunkStream { + + private _chunks: VSBuffer[]; + private _totalLength: number; + + public get byteLength() { + return this._totalLength; + } + + constructor() { + this._chunks = []; + this._totalLength = 0; + } + + public acceptChunk(buff: VSBuffer) { + this._chunks.push(buff); + this._totalLength += buff.byteLength; + } + + public read(byteCount: number): VSBuffer { + return this._read(byteCount, true); + } + + public peek(byteCount: number): VSBuffer { + return this._read(byteCount, false); + } + + private _read(byteCount: number, advance: boolean): VSBuffer { + + if (byteCount === 0) { + return getEmptyBuffer(); + } + + if (byteCount > this._totalLength) { + throw new Error(`Cannot read so many bytes!`); + } + + if (this._chunks[0].byteLength === byteCount) { + // super fast path, precisely first chunk must be returned + const result = this._chunks[0]; + if (advance) { + this._chunks.shift(); + this._totalLength -= byteCount; + } + return result; + } + + if (this._chunks[0].byteLength > byteCount) { + // fast path, the reading is entirely within the first chunk + const result = this._chunks[0].slice(0, byteCount); + if (advance) { + this._chunks[0] = this._chunks[0].slice(byteCount); + this._totalLength -= byteCount; + } + return result; + } + + let result = VSBuffer.alloc(byteCount); + let resultOffset = 0; + let chunkIndex = 0; + while (byteCount > 0) { + const chunk = this._chunks[chunkIndex]; + if (chunk.byteLength > byteCount) { + // this chunk will survive + const chunkPart = chunk.slice(0, byteCount); + result.set(chunkPart, resultOffset); + resultOffset += byteCount; + + if (advance) { + this._chunks[chunkIndex] = chunk.slice(byteCount); + this._totalLength -= byteCount; + } + + byteCount -= byteCount; + } else { + // this chunk will be entirely read + result.set(chunk, resultOffset); + resultOffset += chunk.byteLength; + + if (advance) { + this._chunks.shift(); + this._totalLength -= chunk.byteLength; + } else { + chunkIndex++; + } + + byteCount -= chunk.byteLength; + } + } + return result; + } +} + +const enum ProtocolMessageType { + None = 0, + Regular = 1, + Control = 2, + Ack = 3, + KeepAlive = 4, + Disconnect = 5 +} + +export const enum ProtocolConstants { + HeaderLength = 13, + /** + * Send an Acknowledge message at most 2 seconds later... + */ + AcknowledgeTime = 2000, // 2 seconds + /** + * If there is a message that has been unacknowledged for 10 seconds, consider the connection closed... + */ + AcknowledgeTimeoutTime = 10000, // 10 seconds + /** + * Send at least a message every 5s for keep alive reasons. + */ + KeepAliveTime = 5000, // 5 seconds + /** + * If there is no message received for 10 seconds, consider the connection closed... + */ + KeepAliveTimeoutTime = 10000, // 10 seconds + /** + * If there is no reconnection within this time-frame, consider the connection permanently closed... + */ + ReconnectionGraceTime = 3 * 60 * 60 * 1000, // 3hrs +} + +class ProtocolMessage { + + public writtenTime: number; + + constructor( + public readonly type: ProtocolMessageType, + public readonly id: number, + public readonly ack: number, + public readonly data: VSBuffer + ) { + this.writtenTime = 0; + } + + public get size(): number { + return this.data.byteLength; + } +} + +class ProtocolReader extends Disposable { + + private readonly _socket: ISocket; + private _isDisposed: boolean; + private readonly _incomingData: ChunkStream; + public lastReadTime: number; + + private readonly _onMessage = this._register(new Emitter()); + public readonly onMessage: Event = this._onMessage.event; + + private readonly _state = { + readHead: true, + readLen: ProtocolConstants.HeaderLength, + messageType: ProtocolMessageType.None, + id: 0, + ack: 0 + }; + + constructor(socket: ISocket) { + super(); + this._socket = socket; + this._isDisposed = false; + this._incomingData = new ChunkStream(); + this._register(this._socket.onData(data => this.acceptChunk(data))); + this.lastReadTime = Date.now(); + } + + public acceptChunk(data: VSBuffer | null): void { + if (!data || data.byteLength === 0) { + return; + } + + this.lastReadTime = Date.now(); + + this._incomingData.acceptChunk(data); + + while (this._incomingData.byteLength >= this._state.readLen) { + + const buff = this._incomingData.read(this._state.readLen); + + if (this._state.readHead) { + // buff is the header + + // save new state => next time will read the body + this._state.readHead = false; + this._state.readLen = buff.readUInt32BE(9); + this._state.messageType = buff.readUInt8(0); + this._state.id = buff.readUInt32BE(1); + this._state.ack = buff.readUInt32BE(5); + } else { + // buff is the body + const messageType = this._state.messageType; + const id = this._state.id; + const ack = this._state.ack; + + // save new state => next time will read the header + this._state.readHead = true; + this._state.readLen = ProtocolConstants.HeaderLength; + this._state.messageType = ProtocolMessageType.None; + this._state.id = 0; + this._state.ack = 0; + + this._onMessage.fire(new ProtocolMessage(messageType, id, ack, buff)); + + if (this._isDisposed) { + // check if an event listener lead to our disposal + break; + } + } + } + } + + public readEntireBuffer(): VSBuffer { + return this._incomingData.read(this._incomingData.byteLength); + } + + public dispose(): void { + this._isDisposed = true; + super.dispose(); + } +} + +class ProtocolWriter { + + private _isDisposed: boolean; + private readonly _socket: ISocket; + private _data: VSBuffer[]; + private _totalLength: number; + public lastWriteTime: number; + + constructor(socket: ISocket) { + this._isDisposed = false; + this._socket = socket; + this._data = []; + this._totalLength = 0; + this.lastWriteTime = 0; + } + + public dispose(): void { + this.flush(); + this._isDisposed = true; + } + + public flush(): void { + // flush + this._writeNow(); + } + + public write(msg: ProtocolMessage) { + if (this._isDisposed) { + console.warn(`Cannot write message in a disposed ProtocolWriter`); + console.warn(msg); + return; + } + msg.writtenTime = Date.now(); + this.lastWriteTime = Date.now(); + const header = VSBuffer.alloc(ProtocolConstants.HeaderLength); + header.writeUInt8(msg.type, 0); + header.writeUInt32BE(msg.id, 1); + header.writeUInt32BE(msg.ack, 5); + header.writeUInt32BE(msg.data.byteLength, 9); + this._writeSoon(header, msg.data); + } + + private _bufferAdd(head: VSBuffer, body: VSBuffer): boolean { + const wasEmpty = this._totalLength === 0; + this._data.push(head, body); + this._totalLength += head.byteLength + body.byteLength; + return wasEmpty; + } + + private _bufferTake(): VSBuffer { + const ret = VSBuffer.concat(this._data, this._totalLength); + this._data.length = 0; + this._totalLength = 0; + return ret; + } + + private _writeSoon(header: VSBuffer, data: VSBuffer): void { + if (this._bufferAdd(header, data)) { + platform.setImmediate(() => { + this._writeNow(); + }); + } + } + + private _writeNow(): void { + if (this._totalLength === 0) { + return; + } + this._socket.write(this._bufferTake()); + } +} + +/** + * A message has the following format: + * ``` + * /-------------------------------|------\ + * | HEADER | | + * |-------------------------------| DATA | + * | TYPE | ID | ACK | DATA_LENGTH | | + * \-------------------------------|------/ + * ``` + * The header is 9 bytes and consists of: + * - TYPE is 1 byte (ProtocolMessageType) - the message type + * - ID is 4 bytes (u32be) - the message id (can be 0 to indicate to be ignored) + * - ACK is 4 bytes (u32be) - the acknowledged message id (can be 0 to indicate to be ignored) + * - DATA_LENGTH is 4 bytes (u32be) - the length in bytes of DATA + * + * Only Regular messages are counted, other messages are not counted, nor acknowledged. + */ +export class Protocol extends Disposable implements IMessagePassingProtocol { + + private _socket: ISocket; + private _socketWriter: ProtocolWriter; + private _socketReader: ProtocolReader; + + private _onMessage = new Emitter(); + readonly onMessage: Event = this._onMessage.event; + + private _onClose = new Emitter(); + readonly onClose: Event = this._onClose.event; + + constructor(socket: ISocket) { + super(); + this._socket = socket; + this._socketWriter = this._register(new ProtocolWriter(this._socket)); + this._socketReader = this._register(new ProtocolReader(this._socket)); + + this._register(this._socketReader.onMessage((msg) => { + if (msg.type === ProtocolMessageType.Regular) { + this._onMessage.fire(msg.data); + } + })); + + this._register(this._socket.onClose(() => this._onClose.fire())); + } + + getSocket(): ISocket { + return this._socket; + } + + sendDisconnect(): void { + // Nothing to do... + } + + send(buffer: VSBuffer): void { + this._socketWriter.write(new ProtocolMessage(ProtocolMessageType.Regular, 0, 0, buffer)); + } +} + +export class Client extends IPCClient { + + static fromSocket(socket: ISocket, id: TContext): Client { + return new Client(new Protocol(socket), id); + } + + get onClose(): Event { return this.protocol.onClose; } + + constructor(private protocol: Protocol | PersistentProtocol, id: TContext) { + super(protocol, id); + } + + dispose(): void { + super.dispose(); + const socket = this.protocol.getSocket(); + this.protocol.sendDisconnect(); + this.protocol.dispose(); + socket.end(); + } +} + +/** + * Will ensure no messages are lost if there are no event listeners. + */ +function createBufferedEvent(source: Event): Event { + let emitter: Emitter; + let hasListeners = false; + let isDeliveringMessages = false; + let bufferedMessages: T[] = []; + + const deliverMessages = () => { + if (isDeliveringMessages) { + return; + } + isDeliveringMessages = true; + while (hasListeners && bufferedMessages.length > 0) { + emitter.fire(bufferedMessages.shift()!); + } + isDeliveringMessages = false; + }; + + source((e: T) => { + bufferedMessages.push(e); + deliverMessages(); + }); + + emitter = new Emitter({ + onFirstListenerAdd: () => { + hasListeners = true; + // it is important to deliver these messages after this call, but before + // other messages have a chance to be received (to guarantee in order delivery) + // that's why we're using here nextTick and not other types of timeouts + if (typeof process !== 'undefined') { + process.nextTick(deliverMessages); + } else { + platform.setImmediate(deliverMessages); + } + }, + onLastListenerRemove: () => { + hasListeners = false; + } + }); + + return emitter.event; +} + +class QueueElement { + public readonly data: T; + public next: QueueElement | null; + + constructor(data: T) { + this.data = data; + this.next = null; + } +} + +class Queue { + + private _first: QueueElement | null; + private _last: QueueElement | null; + + constructor() { + this._first = null; + this._last = null; + } + + public peek(): T | null { + if (!this._first) { + return null; + } + return this._first.data; + } + + public toArray(): T[] { + let result: T[] = [], resultLen = 0; + let it = this._first; + while (it) { + result[resultLen++] = it.data; + it = it.next; + } + return result; + } + + public pop(): void { + if (!this._first) { + return; + } + if (this._first === this._last) { + this._first = null; + this._last = null; + return; + } + this._first = this._first.next; + } + + public push(item: T): void { + const element = new QueueElement(item); + if (!this._first) { + this._first = element; + this._last = element; + return; + } + this._last!.next = element; + this._last = element; + } +} + +/** + * Same as Protocol, but will actually track messages and acks. + * Moreover, it will ensure no messages are lost if there are no event listeners. + */ +export class PersistentProtocol { + + private _isReconnecting: boolean; + + private _outgoingUnackMsg: Queue; + private _outgoingMsgId: number; + private _outgoingAckId: number; + private _outgoingAckTimeout: any | null; + + private _incomingMsgId: number; + private _incomingAckId: number; + private _incomingMsgLastTime: number; + private _incomingAckTimeout: any | null; + + private _outgoingKeepAliveTimeout: any | null; + private _incomingKeepAliveTimeout: any | null; + + private _socket: ISocket; + private _socketWriter: ProtocolWriter; + private _socketReader: ProtocolReader; + private _socketDisposables: IDisposable[]; + + private _onControlMessage = new Emitter(); + readonly onControlMessage: Event = createBufferedEvent(this._onControlMessage.event); + + private _onMessage = new Emitter(); + readonly onMessage: Event = createBufferedEvent(this._onMessage.event); + + private _onClose = new Emitter(); + readonly onClose: Event = createBufferedEvent(this._onClose.event); + + private _onSocketClose = new Emitter(); + readonly onSocketClose: Event = createBufferedEvent(this._onSocketClose.event); + + private _onSocketTimeout = new Emitter(); + readonly onSocketTimeout: Event = createBufferedEvent(this._onSocketTimeout.event); + + public get unacknowledgedCount(): number { + return this._outgoingMsgId - this._outgoingAckId; + } + + constructor(socket: ISocket, initialChunk: VSBuffer | null = null) { + this._isReconnecting = false; + this._outgoingUnackMsg = new Queue(); + this._outgoingMsgId = 0; + this._outgoingAckId = 0; + this._outgoingAckTimeout = null; + + this._incomingMsgId = 0; + this._incomingAckId = 0; + this._incomingMsgLastTime = 0; + this._incomingAckTimeout = null; + + this._outgoingKeepAliveTimeout = null; + this._incomingKeepAliveTimeout = null; + + this._socketDisposables = []; + this._socket = socket; + this._socketWriter = new ProtocolWriter(this._socket); + this._socketDisposables.push(this._socketWriter); + this._socketReader = new ProtocolReader(this._socket); + this._socketDisposables.push(this._socketReader); + this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg))); + this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire())); + if (initialChunk) { + this._socketReader.acceptChunk(initialChunk); + } + + this._sendKeepAliveCheck(); + this._recvKeepAliveCheck(); + } + + dispose(): void { + if (this._outgoingAckTimeout) { + clearTimeout(this._outgoingAckTimeout); + this._outgoingAckTimeout = null; + } + if (this._incomingAckTimeout) { + clearTimeout(this._incomingAckTimeout); + this._incomingAckTimeout = null; + } + if (this._outgoingKeepAliveTimeout) { + clearTimeout(this._outgoingKeepAliveTimeout); + this._outgoingKeepAliveTimeout = null; + } + if (this._incomingKeepAliveTimeout) { + clearTimeout(this._incomingKeepAliveTimeout); + this._incomingKeepAliveTimeout = null; + } + this._socketDisposables = dispose(this._socketDisposables); + } + + sendDisconnect(): void { + const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer()); + this._socketWriter.write(msg); + this._socketWriter.flush(); + } + + private _sendKeepAliveCheck(): void { + if (this._outgoingKeepAliveTimeout) { + // there will be a check in the near future + return; + } + + const timeSinceLastOutgoingMsg = Date.now() - this._socketWriter.lastWriteTime; + if (timeSinceLastOutgoingMsg >= ProtocolConstants.KeepAliveTime) { + // sufficient time has passed since last message was written, + // and no message from our side needed to be sent in the meantime, + // so we will send a message containing only a keep alive. + const msg = new ProtocolMessage(ProtocolMessageType.KeepAlive, 0, 0, getEmptyBuffer()); + this._socketWriter.write(msg); + this._sendKeepAliveCheck(); + return; + } + + this._outgoingKeepAliveTimeout = setTimeout(() => { + this._outgoingKeepAliveTimeout = null; + this._sendKeepAliveCheck(); + }, ProtocolConstants.KeepAliveTime - timeSinceLastOutgoingMsg + 5); + } + + private _recvKeepAliveCheck(): void { + if (this._incomingKeepAliveTimeout) { + // there will be a check in the near future + return; + } + + const timeSinceLastIncomingMsg = Date.now() - this._socketReader.lastReadTime; + if (timeSinceLastIncomingMsg >= ProtocolConstants.KeepAliveTimeoutTime) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } + + this._incomingKeepAliveTimeout = setTimeout(() => { + this._incomingKeepAliveTimeout = null; + this._recvKeepAliveCheck(); + }, ProtocolConstants.KeepAliveTimeoutTime - timeSinceLastIncomingMsg + 5); + } + + public getSocket(): ISocket { + return this._socket; + } + + public beginAcceptReconnection(socket: ISocket, initialDataChunk: VSBuffer | null): void { + this._isReconnecting = true; + + this._socketDisposables = dispose(this._socketDisposables); + + this._socket = socket; + this._socketWriter = new ProtocolWriter(this._socket); + this._socketDisposables.push(this._socketWriter); + this._socketReader = new ProtocolReader(this._socket); + this._socketDisposables.push(this._socketReader); + this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg))); + this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire())); + this._socketReader.acceptChunk(initialDataChunk); + } + + public endAcceptReconnection(): void { + this._isReconnecting = false; + + // Send again all unacknowledged messages + const toSend = this._outgoingUnackMsg.toArray(); + for (let i = 0, len = toSend.length; i < len; i++) { + this._socketWriter.write(toSend[i]); + } + this._recvAckCheck(); + + this._sendKeepAliveCheck(); + this._recvKeepAliveCheck(); + } + + public acceptDisconnect(): void { + this._onClose.fire(); + } + + private _receiveMessage(msg: ProtocolMessage): void { + if (msg.ack > this._outgoingAckId) { + this._outgoingAckId = msg.ack; + do { + const first = this._outgoingUnackMsg.peek(); + if (first && first.id <= msg.ack) { + // this message has been confirmed, remove it + this._outgoingUnackMsg.pop(); + } else { + break; + } + } while (true); + } + + if (msg.type === ProtocolMessageType.Regular) { + if (msg.id > this._incomingMsgId) { + if (msg.id !== this._incomingMsgId + 1) { + console.error(`PROTOCOL CORRUPTION, LAST SAW MSG ${this._incomingMsgId} AND HAVE NOW RECEIVED MSG ${msg.id}`); + } + this._incomingMsgId = msg.id; + this._incomingMsgLastTime = Date.now(); + this._sendAckCheck(); + this._onMessage.fire(msg.data); + } + } else if (msg.type === ProtocolMessageType.Control) { + this._onControlMessage.fire(msg.data); + } else if (msg.type === ProtocolMessageType.Disconnect) { + this._onClose.fire(); + } + } + + readEntireBuffer(): VSBuffer { + return this._socketReader.readEntireBuffer(); + } + + flush(): void { + this._socketWriter.flush(); + } + + send(buffer: VSBuffer): void { + const myId = ++this._outgoingMsgId; + this._incomingAckId = this._incomingMsgId; + const msg = new ProtocolMessage(ProtocolMessageType.Regular, myId, this._incomingAckId, buffer); + this._outgoingUnackMsg.push(msg); + if (!this._isReconnecting) { + this._socketWriter.write(msg); + this._recvAckCheck(); + } + } + + /** + * Send a message which will not be part of the regular acknowledge flow. + * Use this for early control messages which are repeated in case of reconnection. + */ + sendControl(buffer: VSBuffer): void { + const msg = new ProtocolMessage(ProtocolMessageType.Control, 0, 0, buffer); + this._socketWriter.write(msg); + } + + private _sendAckCheck(): void { + if (this._incomingMsgId <= this._incomingAckId) { + // nothink to acknowledge + return; + } + + if (this._incomingAckTimeout) { + // there will be a check in the near future + return; + } + + const timeSinceLastIncomingMsg = Date.now() - this._incomingMsgLastTime; + if (timeSinceLastIncomingMsg >= ProtocolConstants.AcknowledgeTime) { + // sufficient time has passed since this message has been received, + // and no message from our side needed to be sent in the meantime, + // so we will send a message containing only an ack. + this._sendAck(); + return; + } + + this._incomingAckTimeout = setTimeout(() => { + this._incomingAckTimeout = null; + this._sendAckCheck(); + }, ProtocolConstants.AcknowledgeTime - timeSinceLastIncomingMsg + 5); + } + + private _recvAckCheck(): void { + if (this._outgoingMsgId <= this._outgoingAckId) { + // everything has been acknowledged + return; + } + + if (this._outgoingAckTimeout) { + // there will be a check in the near future + return; + } + + const oldestUnacknowledgedMsg = this._outgoingUnackMsg.peek()!; + const timeSinceOldestUnacknowledgedMsg = Date.now() - oldestUnacknowledgedMsg.writtenTime; + if (timeSinceOldestUnacknowledgedMsg >= ProtocolConstants.AcknowledgeTimeoutTime) { + // Trash the socket + this._onSocketTimeout.fire(undefined); + return; + } + + this._outgoingAckTimeout = setTimeout(() => { + this._outgoingAckTimeout = null; + this._recvAckCheck(); + }, ProtocolConstants.AcknowledgeTimeoutTime - timeSinceOldestUnacknowledgedMsg + 5); + } + + private _sendAck(): void { + if (this._incomingMsgId <= this._incomingAckId) { + // nothink to acknowledge + return; + } + + this._incomingAckId = this._incomingMsgId; + const msg = new ProtocolMessage(ProtocolMessageType.Ack, 0, this._incomingAckId, getEmptyBuffer()); + this._socketWriter.write(msg); + } +} diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts similarity index 86% rename from src/vs/base/parts/ipc/node/ipc.ts rename to src/vs/base/parts/ipc/common/ipc.ts index 50fc50bbf62..6a2ff55e2d1 100644 --- a/src/vs/base/parts/ipc/node/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -3,11 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event, Emitter, Relay } from 'vs/base/common/event'; import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter, once, toPromise, Relay } from 'vs/base/common/event'; -import { always, CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; +import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { VSBuffer } from 'vs/base/common/buffer'; + +/** + * An `IChannel` is an abstraction over a collection of commands. + * You can `call` several commands on a channel, each taking at + * most one single argument. A `call` always returns a promise + * with at most one single return value. + */ +export interface IChannel { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise; + listen(event: string, arg?: any): Event; +} + +/** + * An `IServerChannel` is the couter part to `IChannel`, + * on the server-side. You should implement this interface + * if you'd like to handle remote promises or events. + */ +export interface IServerChannel { + call(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Promise; + listen(ctx: TContext, event: string, arg?: any): Event; +} + export const enum RequestType { Promise = 100, @@ -42,8 +66,8 @@ interface IHandler { } export interface IMessagePassingProtocol { - send(buffer: Buffer): void; - onMessage: Event; + send(buffer: VSBuffer): void; + onMessage: Event; } enum State { @@ -51,27 +75,6 @@ enum State { Idle } -/** - * An `IChannel` is an abstraction over a collection of commands. - * You can `call` several commands on a channel, each taking at - * most one single argument. A `call` always returns a promise - * with at most one single return value. - */ -export interface IChannel { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable; - listen(event: string, arg?: any): Event; -} - -/** - * An `IServerChannel` is the couter part to `IChannel`, - * on the server-side. You should implement this interface - * if you'd like to handle remote promises or events. - */ -export interface IServerChannel { - call(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable; - listen(ctx: TContext, event: string, arg?: any): Event; -} - /** * An `IChannelServer` hosts a collection of channels. You are * able to register channels onto it, provided a channel name. @@ -103,8 +106,8 @@ export interface IConnectionHub { * channels (each from a separate client) to pick from. */ export interface IClientRouter { - routeCall(hub: IConnectionHub, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable>; - routeEvent(hub: IConnectionHub, event: string, arg?: any): Thenable>; + routeCall(hub: IConnectionHub, command: string, arg?: any, cancellationToken?: CancellationToken): Promise>; + routeEvent(hub: IConnectionHub, event: string, arg?: any): Promise>; } /** @@ -119,35 +122,35 @@ export interface IRoutingChannelClient { } interface IReader { - read(bytes: number): Buffer; + read(bytes: number): VSBuffer; } interface IWriter { - write(buffer: Buffer): void; + write(buffer: VSBuffer): void; } class BufferReader implements IReader { private pos = 0; - constructor(private buffer: Buffer) { } + constructor(private buffer: VSBuffer) { } - read(bytes: number): Buffer { + read(bytes: number): VSBuffer { const result = this.buffer.slice(this.pos, this.pos + bytes); - this.pos += result.length; + this.pos += result.byteLength; return result; } } class BufferWriter implements IWriter { - private buffers: Buffer[] = []; + private buffers: VSBuffer[] = []; - get buffer(): Buffer { - return Buffer.concat(this.buffers); + get buffer(): VSBuffer { + return VSBuffer.concat(this.buffers); } - write(buffer: Buffer): void { + write(buffer: VSBuffer): void { this.buffers.push(buffer); } } @@ -156,12 +159,13 @@ enum DataType { Undefined = 0, String = 1, Buffer = 2, - Array = 3, - Object = 4 + VSBuffer = 3, + Array = 4, + Object = 5 } -function createSizeBuffer(size: number): Buffer { - const result = Buffer.allocUnsafe(4); +function createSizeBuffer(size: number): VSBuffer { + const result = VSBuffer.alloc(4); result.writeUInt32BE(size, 0); return result; } @@ -170,25 +174,40 @@ function readSizeBuffer(reader: IReader): number { return reader.read(4).readUInt32BE(0); } +function createOneByteBuffer(value: number): VSBuffer { + const result = VSBuffer.alloc(1); + result.writeUInt8(value, 0); + return result; +} + const BufferPresets = { - Undefined: Buffer.alloc(1, DataType.Undefined), - String: Buffer.alloc(1, DataType.String), - Buffer: Buffer.alloc(1, DataType.Buffer), - Array: Buffer.alloc(1, DataType.Array), - Object: Buffer.alloc(1, DataType.Object) + Undefined: createOneByteBuffer(DataType.Undefined), + String: createOneByteBuffer(DataType.String), + Buffer: createOneByteBuffer(DataType.Buffer), + VSBuffer: createOneByteBuffer(DataType.VSBuffer), + Array: createOneByteBuffer(DataType.Array), + Object: createOneByteBuffer(DataType.Object), }; +declare var Buffer: any; +const hasBuffer = (typeof Buffer !== 'undefined'); + function serialize(writer: IWriter, data: any): void { if (typeof data === 'undefined') { writer.write(BufferPresets.Undefined); } else if (typeof data === 'string') { - const buffer = Buffer.from(data); + const buffer = VSBuffer.fromString(data); writer.write(BufferPresets.String); - writer.write(createSizeBuffer(buffer.length)); + writer.write(createSizeBuffer(buffer.byteLength)); writer.write(buffer); - } else if (Buffer.isBuffer(data)) { + } else if (hasBuffer && Buffer.isBuffer(data)) { + const buffer = VSBuffer.wrap(data); writer.write(BufferPresets.Buffer); - writer.write(createSizeBuffer(data.length)); + writer.write(createSizeBuffer(buffer.byteLength)); + writer.write(buffer); + } else if (data instanceof VSBuffer) { + writer.write(BufferPresets.VSBuffer); + writer.write(createSizeBuffer(data.byteLength)); writer.write(data); } else if (Array.isArray(data)) { writer.write(BufferPresets.Array); @@ -198,9 +217,9 @@ function serialize(writer: IWriter, data: any): void { serialize(writer, el); } } else { - const buffer = Buffer.from(JSON.stringify(data)); + const buffer = VSBuffer.fromString(JSON.stringify(data)); writer.write(BufferPresets.Object); - writer.write(createSizeBuffer(buffer.length)); + writer.write(createSizeBuffer(buffer.byteLength)); writer.write(buffer); } } @@ -211,7 +230,8 @@ function deserialize(reader: IReader): any { switch (type) { case DataType.Undefined: return undefined; case DataType.String: return reader.read(readSizeBuffer(reader)).toString(); - case DataType.Buffer: return reader.read(readSizeBuffer(reader)); + case DataType.Buffer: return reader.read(readSizeBuffer(reader)).buffer; + case DataType.VSBuffer: return reader.read(readSizeBuffer(reader)); case DataType.Array: { const length = readSizeBuffer(reader); const result: any[] = []; @@ -261,7 +281,7 @@ export class ChannelServer implements IChannelServer implements IChannelServer implements IChannelServer; + let promise: Promise; try { promise = channel.call(this.ctx, request.name, request.arg, cancellationTokenSource.token); @@ -309,7 +332,7 @@ export class ChannelServer implements IChannelServer implements IChannelServer { + private requestPromise(channelName: string, name: string, arg?: any, cancellationToken = CancellationToken.None): Promise { const id = this.lastRequestId++; const type = RequestType.Promise; const request: IRawRequest = { id, type, channelName, name, arg }; @@ -442,9 +468,7 @@ export class ChannelClient implements IChannelClient, IDisposable { this.activeRequests.add(disposable); }); - always(result, () => this.activeRequests.delete(disposable)); - - return result; + return result.finally(() => this.activeRequests.delete(disposable)); } private requestEvent(channelName: string, name: string, arg?: any): Event { @@ -499,7 +523,7 @@ export class ChannelClient implements IChannelClient, IDisposable { this.sendBuffer(writer.buffer); } - private sendBuffer(message: Buffer): void { + private sendBuffer(message: VSBuffer): void { try { this.protocol.send(message); } catch (err) { @@ -507,7 +531,7 @@ export class ChannelClient implements IChannelClient, IDisposable { } } - private onBuffer(message: Buffer): void { + private onBuffer(message: VSBuffer): void { const reader = new BufferReader(message); const header = deserialize(reader); const body = deserialize(reader); @@ -539,11 +563,11 @@ export class ChannelClient implements IChannelClient, IDisposable { } } - private whenInitialized(): Thenable { + private whenInitialized(): Promise { if (this.state === State.Idle) { return Promise.resolve(); } else { - return toPromise(this.onDidInitialize); + return Event.toPromise(this.onDidInitialize); } } @@ -590,7 +614,7 @@ export class IPCServer implements IChannelServer, I constructor(onDidClientConnect: Event) { onDidClientConnect(({ protocol, onDidClientDisconnect }) => { - const onFirstMessage = once(protocol.onMessage); + const onFirstMessage = Event.once(protocol.onMessage); onFirstMessage(msg => { const reader = new BufferReader(msg); @@ -681,10 +705,10 @@ export class IPCClient implements IChannelClient, IChannelSer } } -export function getDelayedChannel(promise: Thenable): T { +export function getDelayedChannel(promise: Promise): T { return { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { - return promise.then(c => c.call(command, arg, cancellationToken)); + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { + return promise.then(c => c.call(command, arg, cancellationToken)); }, listen(event: string, arg?: any): Event { @@ -699,7 +723,7 @@ export function getNextTickChannel(channel: T): T { let didTick = false; return { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { if (didTick) { return channel.call(command, arg, cancellationToken); } @@ -726,13 +750,13 @@ export function getNextTickChannel(channel: T): T { export class StaticRouter implements IClientRouter { - constructor(private fn: (ctx: TContext) => boolean | Thenable) { } + constructor(private fn: (ctx: TContext) => boolean | Promise) { } - routeCall(hub: IConnectionHub): Thenable> { + routeCall(hub: IConnectionHub): Promise> { return this.route(hub); } - routeEvent(hub: IConnectionHub): Thenable> { + routeEvent(hub: IConnectionHub): Promise> { return this.route(hub); } @@ -743,7 +767,7 @@ export class StaticRouter implements IClientRouter } } - await toPromise(hub.onDidChangeConnections); + await Event.toPromise(hub.onDidChangeConnections); return await this.route(hub); } -} \ 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 88b51024314..2690300b83a 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 @@ -3,18 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { fromNodeEventEmitter } from 'vs/base/common/event'; -import { IPCClient } from 'vs/base/parts/ipc/node/ipc'; +import { Event } from 'vs/base/common/event'; +import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcRenderer } from 'electron'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { VSBuffer } from 'vs/base/common/buffer'; export class Client extends IPCClient implements IDisposable { private protocol: Protocol; private static createProtocol(): Protocol { - const onMessage = fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: string) => message); + const onMessage = Event.fromNodeEventEmitter(ipcRenderer, 'ipc:message', (_, message: Buffer) => VSBuffer.wrap(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 eb9cef3e413..c5fbbb4e65b 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 @@ -3,30 +3,44 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, filterEvent, mapEvent, fromNodeEventEmitter, signalEvent } from 'vs/base/common/event'; -import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/node/ipc'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcMain } from 'electron'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { VSBuffer } from 'vs/base/common/buffer'; interface IIPCEvent { event: { sender: Electron.WebContents; }; - message: string; + message: Buffer | null; } -function createScopedOnMessageEvent(senderId: number, eventName: string): Event { - const onMessage = fromNodeEventEmitter(ipcMain, eventName, (event, message: string) => ({ event, message })); - const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.id === senderId); - return mapEvent(onMessageFromSender, ({ message }) => message); +function createScopedOnMessageEvent(senderId: number, eventName: string): Event { + const onMessage = Event.fromNodeEventEmitter(ipcMain, eventName, (event, message) => ({ event, message })); + const onMessageFromSender = Event.filter(onMessage, ({ event }) => event.sender.id === senderId); + return Event.map(onMessageFromSender, ({ message }) => message ? VSBuffer.wrap(message) : message); } export class Server extends IPCServer { - private static getOnDidClientConnect(): Event { - const onHello = fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + private static Clients = new Map(); - return mapEvent(onHello, webContents => { - const onMessage = createScopedOnMessageEvent(webContents.id, 'ipc:message'); - const onDidClientDisconnect = signalEvent(createScopedOnMessageEvent(webContents.id, 'ipc:disconnect')); + private static getOnDidClientConnect(): Event { + const onHello = Event.fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + + return Event.map(onHello, webContents => { + const id = webContents.id; + const client = Server.Clients.get(id); + + if (client) { + client.dispose(); + } + + const onDidClientReconnect = new Emitter(); + Server.Clients.set(id, toDisposable(() => onDidClientReconnect.fire())); + + const onMessage = createScopedOnMessageEvent(id, 'ipc:message') as Event; + const onDidClientDisconnect = Event.any(Event.signal(createScopedOnMessageEvent(id, 'ipc:disconnect')), onDidClientReconnect.event); const protocol = new Protocol(webContents, onMessage); return { protocol, onDidClientDisconnect }; diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 678415bd34a..9104deb503c 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -5,14 +5,15 @@ import { ChildProcess, fork, ForkOptions } from 'child_process'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Delayer, always, createCancelablePromise } from 'vs/base/common/async'; +import { Delayer, createCancelablePromise } from 'vs/base/common/async'; import { deepClone, assign } from 'vs/base/common/objects'; -import { Emitter, fromNodeEventEmitter, Event } from 'vs/base/common/event'; +import { Emitter, 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/node/ipc'; -import { isRemoteConsoleLog, log } from 'vs/base/node/console'; +import { IChannel, ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient } from 'vs/base/parts/ipc/common/ipc'; +import { isRemoteConsoleLog, log } from 'vs/base/common/console'; import { CancellationToken } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; +import { VSBuffer } from 'vs/base/common/buffer'; /** * This implementation doesn't perform well since it uses base64 encoding for buffers. @@ -25,11 +26,11 @@ export class Server extends IPCServer { send: r => { try { if (process.send) { - process.send(r.toString('base64')); + process.send((r.buffer).toString('base64')); } } catch (e) { /* not much to do */ } }, - onMessage: fromNodeEventEmitter(process, 'message', msg => Buffer.from(msg, 'base64')) + onMessage: Event.fromNodeEventEmitter(process, 'message', msg => VSBuffer.wrap(Buffer.from(msg, 'base64'))) }, ctx); process.once('disconnect', () => this.dispose()); @@ -105,7 +106,7 @@ export class Client implements IChannelClient, IDisposable { const that = this; return { - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { return that.requestPromise(channelName, command, arg, cancellationToken); }, listen(event: string, arg?: any) { @@ -114,7 +115,7 @@ export class Client implements IChannelClient, IDisposable { } as T; } - protected requestPromise(channelName: string, name: string, arg?: any, cancellationToken = CancellationToken.None): Thenable { + protected requestPromise(channelName: string, name: string, arg?: any, cancellationToken = CancellationToken.None): Promise { if (!this.disposeDelayer) { return Promise.reject(new Error('disposed')); } @@ -132,7 +133,7 @@ export class Client implements IChannelClient, IDisposable { const disposable = toDisposable(() => result.cancel()); this.activeRequests.add(disposable); - always(result, () => { + result.finally(() => { cancellationTokenListener.dispose(); this.activeRequests.delete(disposable); @@ -198,8 +199,8 @@ export class Client implements IChannelClient, IDisposable { this.child = fork(this.modulePath, args, forkOpts); - const onMessageEmitter = new Emitter(); - const onRawMessage = fromNodeEventEmitter(this.child, 'message', msg => msg); + const onMessageEmitter = new Emitter(); + const onRawMessage = Event.fromNodeEventEmitter(this.child, 'message', msg => msg); onRawMessage(msg => { @@ -210,11 +211,11 @@ export class Client implements IChannelClient, IDisposable { } // Anything else goes to the outside - onMessageEmitter.fire(Buffer.from(msg, 'base64')); + onMessageEmitter.fire(VSBuffer.wrap(Buffer.from(msg, 'base64'))); }); const sender = this.options.useQueue ? createQueuedSender(this.child) : this.child; - const send = (r: Buffer) => this.child && this.child.connected && sender.send(r.toString('base64')); + const send = (r: VSBuffer) => this.child && this.child.connected && sender.send((r.buffer).toString('base64')); const onMessage = onMessageEmitter.event; const protocol = { send, onMessage }; diff --git a/src/vs/base/parts/ipc/node/ipc.electron.ts b/src/vs/base/parts/ipc/node/ipc.electron.ts index 831a27377c1..09c97ba47b1 100644 --- a/src/vs/base/parts/ipc/node/ipc.electron.ts +++ b/src/vs/base/parts/ipc/node/ipc.electron.ts @@ -3,33 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -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 - */ +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { Event } from 'vs/base/common/event'; +import { VSBuffer } from 'vs/base/common/buffer'; export interface Sender { - send(channel: string, msg: string | null): void; + send(channel: string, msg: Buffer | null): void; } export class Protocol implements IMessagePassingProtocol { - private listener: IDisposable; + constructor(private sender: Sender, readonly onMessage: Event) { } - private _onMessage = new Emitter(); - get onMessage(): Event { return this._onMessage.event; } - - constructor(private sender: Sender, onMessageEvent: Event) { - onMessageEvent(msg => this._onMessage.fire(Buffer.from(msg, 'base64'))); - } - - send(message: Buffer): void { + send(message: VSBuffer): void { try { - this.sender.send('ipc:message', message.toString('base64')); + this.sender.send('ipc:message', (message.buffer)); } catch (e) { // systems are going down } @@ -37,6 +25,5 @@ export class Protocol implements IMessagePassingProtocol { dispose(): void { this.sender.send('ipc:disconnect', null); - this.listener = dispose(this.listener); } } \ No newline at end of file diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index ec7cb77243a..2dbefa2c42a 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -4,13 +4,66 @@ *--------------------------------------------------------------------------------------------*/ import { Socket, Server as NetServer, createConnection, createServer } from 'net'; -import { Event, Emitter, once, mapEvent, fromNodeEventEmitter } from 'vs/base/common/event'; -import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } from 'vs/base/parts/ipc/node/ipc'; -import { join } from 'path'; +import { Event } from 'vs/base/common/event'; +import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc'; +import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; import { generateUuid } from 'vs/base/common/uuid'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { TimeoutTimer } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { ISocket, Protocol, Client } from 'vs/base/parts/ipc/common/ipc.net'; + +export class NodeSocket implements ISocket { + public readonly socket: Socket; + + constructor(socket: Socket) { + this.socket = socket; + } + + public dispose(): void { + this.socket.destroy(); + } + + public onData(_listener: (e: VSBuffer) => void): IDisposable { + const listener = (buff: Buffer) => _listener(VSBuffer.wrap(buff)); + this.socket.on('data', listener); + return { + dispose: () => this.socket.off('data', listener) + }; + } + + public onClose(listener: () => void): IDisposable { + this.socket.on('close', listener); + return { + dispose: () => this.socket.off('close', listener) + }; + } + + public onEnd(listener: () => void): IDisposable { + this.socket.on('end', listener); + return { + dispose: () => this.socket.off('end', listener) + }; + } + + public write(buffer: VSBuffer): void { + // return early if socket has been destroyed in the meantime + if (this.socket.destroyed) { + return; + } + + // we ignore the returned value from `write` because we would have to cached the data + // anyways and nodejs is already doing that for us: + // > https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback + // > However, the false return value is only advisory and the writable stream will unconditionally + // > accept and buffer chunk even if it has not not been allowed to drain. + this.socket.write(buffer.buffer); + } + + public end(): void { + this.socket.end(); + } +} export function generateRandomPipeName(): string { const randomSuffix = generateUuid(); @@ -22,196 +75,14 @@ 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 = 4; - - private _isDisposed: boolean; - private _chunks: Buffer[]; - - private _firstChunkTimer: TimeoutTimer; - private _socketDataListener: (data: Buffer) => void; - private _socketEndListener: () => void; - private _socketCloseListener: () => void; - - private _onMessage = new Emitter(); - readonly onMessage: Event = this._onMessage.event; - - private _onClose = new Emitter(); - readonly onClose: Event = this._onClose.event; - - constructor(private _socket: Socket, firstDataChunk?: Buffer) { - this._isDisposed = false; - this._chunks = []; - - let totalLength = 0; - - const state = { - readHead: true, - bodyLen: -1, - }; - - const acceptChunk = (data: Buffer) => { - - this._chunks.push(data); - totalLength += data.length; - - while (totalLength > 0) { - - if (state.readHead) { - // expecting header -> read 5bytes for header - // information: `bodyIsJson` and `bodyLen` - if (totalLength >= Protocol._headerLen) { - const all = Buffer.concat(this._chunks); - - state.bodyLen = all.readUInt32BE(0); - state.readHead = false; - - const rest = all.slice(Protocol._headerLen); - totalLength = rest.length; - this._chunks = [rest]; - - } else { - break; - } - } - - if (!state.readHead) { - // expecting body -> read bodyLen-bytes for - // the actual message or wait for more data - if (totalLength >= state.bodyLen) { - - const all = Buffer.concat(this._chunks); - const buffer = all.slice(0, state.bodyLen); - - // 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.bodyLen = -1; - state.readHead = true; - - this._onMessage.fire(buffer); - - if (this._isDisposed) { - // check if an event listener lead to our disposal - break; - } - } else { - break; - } - } - } - }; - - const acceptFirstDataChunk = () => { - if (firstDataChunk && firstDataChunk.length > 0) { - let tmp = firstDataChunk; - firstDataChunk = undefined; - acceptChunk(tmp); - } - }; - - // Make sure to always handle the firstDataChunk if no more `data` event comes in - this._firstChunkTimer = new TimeoutTimer(); - this._firstChunkTimer.setIfNotSet(() => { - acceptFirstDataChunk(); - }, 0); - - this._socketDataListener = (data: Buffer) => { - acceptFirstDataChunk(); - acceptChunk(data); - }; - _socket.on('data', this._socketDataListener); - - this._socketEndListener = () => { - acceptFirstDataChunk(); - }; - _socket.on('end', this._socketEndListener); - - this._socketCloseListener = () => { - this._onClose.fire(); - }; - _socket.once('close', this._socketCloseListener); - } - - dispose(): void { - this._isDisposed = true; - this._firstChunkTimer.dispose(); - this._socket.removeListener('data', this._socketDataListener); - this._socket.removeListener('end', this._socketEndListener); - this._socket.removeListener('close', this._socketCloseListener); - } - - end(): void { - this._socket.end(); - } - - getBuffer(): Buffer { - return Buffer.concat(this._chunks); - } - - send(buffer: Buffer): void { - const header = Buffer.allocUnsafe(Protocol._headerLen); - header.writeUInt32BE(buffer.length, 0, true); - this._writeSoon(header, buffer); - } - - private _writeBuffer = new class { - - private _data: Buffer[] = []; - private _totalLength = 0; - - add(head: Buffer, body: Buffer): boolean { - const wasEmpty = this._totalLength === 0; - this._data.push(head, body); - this._totalLength += head.length + body.length; - return wasEmpty; - } - - take(): Buffer { - const ret = Buffer.concat(this._data, this._totalLength); - this._data.length = 0; - this._totalLength = 0; - return ret; - } - }; - - private _writeSoon(header: Buffer, data: Buffer): void { - if (this._writeBuffer.add(header, data)) { - setImmediate(() => { - // return early if socket has been destroyed in the meantime - if (this._socket.destroyed) { - return; - } - // we ignore the returned value from `write` because we would have to cached the data - // anyways and nodejs is already doing that for us: - // > https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback - // > However, the false return value is only advisory and the writable stream will unconditionally - // > accept and buffer chunk even if it has not not been allowed to drain. - this._socket.write(this._writeBuffer.take()); - }); - } - } -} - export class Server extends IPCServer { private static toClientConnectionEvent(server: NetServer): Event { - const onConnection = fromNodeEventEmitter(server, 'connection'); + const onConnection = Event.fromNodeEventEmitter(server, 'connection'); - return mapEvent(onConnection, socket => ({ - protocol: new Protocol(socket), - onDidClientDisconnect: once(fromNodeEventEmitter(socket, 'close')) + return Event.map(onConnection, socket => ({ + protocol: new Protocol(new NodeSocket(socket)), + onDidClientDisconnect: Event.once(Event.fromNodeEventEmitter(socket, 'close')) })); } @@ -231,27 +102,9 @@ export class Server extends IPCServer { } } -export class Client extends IPCClient { - - static fromSocket(socket: Socket, id: TContext): Client { - return new Client(new Protocol(socket), id); - } - - get onClose(): Event { return this.protocol.onClose; } - - constructor(private protocol: Protocol, id: TContext) { - super(protocol, id); - } - - dispose(): void { - super.dispose(); - this.protocol.end(); - } -} - -export function serve(port: number): Thenable; -export function serve(namedPipe: string): Thenable; -export function serve(hook: any): Thenable { +export function serve(port: number): Promise; +export function serve(namedPipe: string): Promise; +export function serve(hook: any): Promise { return new Promise((c, e) => { const server = createServer(); @@ -263,14 +116,14 @@ export function serve(hook: any): Thenable { }); } -export function connect(options: { host: string, port: number }, clientId: string): Thenable; -export function connect(port: number, clientId: string): Thenable; -export function connect(namedPipe: string, clientId: string): Thenable; -export function connect(hook: any, clientId: string): Thenable { +export function connect(options: { host: string, port: number }, clientId: string): Promise; +export function connect(port: number, clientId: string): Promise; +export function connect(namedPipe: string, clientId: string): Promise; +export function connect(hook: any, clientId: string): Promise { return new Promise((c, e) => { const socket = createConnection(hook, () => { socket.removeListener('error', e); - c(Client.fromSocket(socket, clientId)); + c(Client.fromSocket(new NodeSocket(socket), clientId)); }); socket.once('error', e); diff --git a/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts b/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts index 9f5e08b10d1..147a2be5131 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts @@ -5,7 +5,6 @@ import * as assert from 'assert'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import { always } from 'vs/base/common/async'; import { TestServiceClient } from './testService'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -27,7 +26,7 @@ suite('IPC, Child Process', () => { assert.equal(r.outgoing, 'pong'); }); - return always(result, () => client.dispose()); + return result.finally(() => client.dispose()); }); test('events', () => { @@ -39,7 +38,7 @@ suite('IPC, Child Process', () => { service.onMarco(({ answer }) => { try { assert.equal(answer, 'polo'); - c(null); + c(undefined); } catch (err) { e(err); } @@ -49,7 +48,7 @@ suite('IPC, Child Process', () => { const request = service.marco(); const result = Promise.all([request, event]); - return always(result, () => client.dispose()); + return result.finally(() => client.dispose()); }); test('event dispose', () => { @@ -74,6 +73,6 @@ suite('IPC, Child Process', () => { assert.equal(count, 2); }); - return always(result, () => client.dispose()); + return result.finally(() => client.dispose()); }); }); 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 c094414be18..957329d032b 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 @@ -6,68 +6,148 @@ import * as assert from 'assert'; import { Socket } from 'net'; import { EventEmitter } from 'events'; -import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; +import { Protocol, PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; +import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { VSBuffer } from 'vs/base/common/buffer'; -class MockDuplex extends EventEmitter { +class MessageStream { - private _cache: Buffer[] = []; + private _currentComplete: ((data: VSBuffer) => void) | null; + private _messages: VSBuffer[]; - readonly destroyed = false; + constructor(x: Protocol | PersistentProtocol) { + this._currentComplete = null; + this._messages = []; + x.onMessage(data => { + this._messages.push(data); + this._trigger(); + }); + } - private _deliver(): void { - if (this._cache.length) { - const data = Buffer.concat(this._cache); - this._cache.length = 0; - this.emit('data', data); + private _trigger(): void { + if (!this._currentComplete) { + return; } + if (this._messages.length === 0) { + return; + } + const complete = this._currentComplete; + const msg = this._messages.shift()!; + + this._currentComplete = null; + complete(msg); + } + + public waitForOne(): Promise { + return new Promise((complete) => { + this._currentComplete = complete; + this._trigger(); + }); + } +} + +class EtherStream extends EventEmitter { + constructor( + private readonly _ether: Ether, + private readonly _name: 'a' | 'b' + ) { + super(); } write(data: Buffer, cb?: Function): boolean { - this._cache.push(data); - setImmediate(() => this._deliver()); + if (!Buffer.isBuffer(data)) { + throw new Error(`Invalid data`); + } + this._ether.write(this._name, data); return true; } } +class Ether { + + private readonly _a: EtherStream; + private readonly _b: EtherStream; + + private _ab: Buffer[]; + private _ba: Buffer[]; + + public get a(): Socket { + return this._a; + } + + public get b(): Socket { + return this._b; + } + + constructor() { + this._a = new EtherStream(this, 'a'); + this._b = new EtherStream(this, 'b'); + this._ab = []; + this._ba = []; + } + + public write(from: 'a' | 'b', data: Buffer): void { + if (from === 'a') { + this._ab.push(data); + } else { + this._ba.push(data); + } + + setImmediate(() => this._deliver()); + } + + private _deliver(): void { + + if (this._ab.length > 0) { + const data = Buffer.concat(this._ab); + this._ab.length = 0; + this._b.emit('data', data); + setImmediate(() => this._deliver()); + return; + } + + if (this._ba.length > 0) { + const data = Buffer.concat(this._ba); + this._ba.length = 0; + this._a.emit('data', data); + setImmediate(() => this._deliver()); + return; + } + + } +} suite('IPC, Socket Protocol', () => { - let stream: Socket; + let ether: Ether; setup(() => { - stream = new MockDuplex(); + ether = new Ether(); }); test('read/write', async () => { - const a = new Protocol(stream); - const b = new Protocol(stream); + const a = new Protocol(new NodeSocket(ether.a)); + const b = new Protocol(new NodeSocket(ether.b)); + const bMessages = new MessageStream(b); - await new Promise(resolve => { - const sub = b.onMessage(data => { - sub.dispose(); - assert.equal(data.toString(), 'foobarfarboo'); - resolve(null); - }); - a.send(Buffer.from('foobarfarboo')); - }); - return new Promise(resolve => { - const sub_1 = b.onMessage(data => { - sub_1.dispose(); - assert.equal(data.readInt8(0), 123); - resolve(null); - }); - const buffer = Buffer.allocUnsafe(1); - buffer.writeInt8(123, 0); - a.send(buffer); - }); + a.send(VSBuffer.fromString('foobarfarboo')); + const msg1 = await bMessages.waitForOne(); + assert.equal(msg1.toString(), 'foobarfarboo'); + + const buffer = VSBuffer.alloc(1); + buffer.writeUInt8(123, 0); + a.send(buffer); + const msg2 = await bMessages.waitForOne(); + assert.equal(msg2.readUInt8(0), 123); }); - test('read/write, object data', () => { + test('read/write, object data', async () => { - const a = new Protocol(stream); - const b = new Protocol(stream); + const a = new Protocol(new NodeSocket(ether.a)); + const b = new Protocol(new NodeSocket(ether.b)); + const bMessages = new MessageStream(b); const data = { pi: Math.PI, @@ -76,48 +156,69 @@ suite('IPC, Socket Protocol', () => { data: 'Hello World'.split('') }; - a.send(Buffer.from(JSON.stringify(data))); - - return new Promise(resolve => { - b.onMessage(msg => { - assert.deepEqual(JSON.parse(msg.toString()), data); - resolve(null); - }); - }); + a.send(VSBuffer.fromString(JSON.stringify(data))); + const msg = await bMessages.waitForOne(); + assert.deepEqual(JSON.parse(msg.toString()), data); }); - test('can devolve to a socket and evolve again without losing data', () => { - let resolve: (v: void) => void; - let result = new Promise((_resolve, _reject) => { - resolve = _resolve; - }); - const sender = new Protocol(stream); - const receiver1 = new Protocol(stream); +}); - assert.equal(stream.listenerCount('data'), 2); - assert.equal(stream.listenerCount('end'), 2); +suite('PersistentProtocol reconnection', () => { + let ether: Ether; - receiver1.onMessage((msg) => { - assert.equal(JSON.parse(msg.toString()).value, 1); + setup(() => { + ether = new Ether(); + }); - let buffer = receiver1.getBuffer(); - receiver1.dispose(); + test('acks get piggybacked with messages', async () => { + const a = new PersistentProtocol(new NodeSocket(ether.a)); + const aMessages = new MessageStream(a); + const b = new PersistentProtocol(new NodeSocket(ether.b)); + const bMessages = new MessageStream(b); - assert.equal(stream.listenerCount('data'), 1); - assert.equal(stream.listenerCount('end'), 1); + a.send(VSBuffer.fromString('a1')); + assert.equal(a.unacknowledgedCount, 1); + assert.equal(b.unacknowledgedCount, 0); - const receiver2 = new Protocol(stream, buffer); - receiver2.onMessage((msg) => { - assert.equal(JSON.parse(msg.toString()).value, 2); - resolve(void 0); - }); - }); + a.send(VSBuffer.fromString('a2')); + assert.equal(a.unacknowledgedCount, 2); + assert.equal(b.unacknowledgedCount, 0); - const msg1 = { value: 1 }; - const msg2 = { value: 2 }; - sender.send(Buffer.from(JSON.stringify(msg1))); - sender.send(Buffer.from(JSON.stringify(msg2))); + a.send(VSBuffer.fromString('a3')); + assert.equal(a.unacknowledgedCount, 3); + assert.equal(b.unacknowledgedCount, 0); - return result; + const a1 = await bMessages.waitForOne(); + assert.equal(a1.toString(), 'a1'); + assert.equal(a.unacknowledgedCount, 3); + assert.equal(b.unacknowledgedCount, 0); + + const a2 = await bMessages.waitForOne(); + assert.equal(a2.toString(), 'a2'); + assert.equal(a.unacknowledgedCount, 3); + assert.equal(b.unacknowledgedCount, 0); + + const a3 = await bMessages.waitForOne(); + assert.equal(a3.toString(), 'a3'); + assert.equal(a.unacknowledgedCount, 3); + assert.equal(b.unacknowledgedCount, 0); + + b.send(VSBuffer.fromString('b1')); + assert.equal(a.unacknowledgedCount, 3); + assert.equal(b.unacknowledgedCount, 1); + + const b1 = await aMessages.waitForOne(); + assert.equal(b1.toString(), 'b1'); + assert.equal(a.unacknowledgedCount, 0); + assert.equal(b.unacknowledgedCount, 1); + + a.send(VSBuffer.fromString('a4')); + assert.equal(a.unacknowledgedCount, 1); + assert.equal(b.unacknowledgedCount, 1); + + const b2 = await bMessages.waitForOne(); + assert.equal(b2.toString(), 'a4'); + assert.equal(a.unacknowledgedCount, 1); + assert.equal(b.unacknowledgedCount, 0); }); }); diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/node/ipc.test.ts index 03810849fc7..14f059f5e08 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.test.ts @@ -4,18 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; -import { Emitter, toPromise, Event } from 'vs/base/common/event'; +import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { Emitter, Event } from 'vs/base/common/event'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; class QueueProtocol implements IMessagePassingProtocol { private buffering = true; - private buffers: Buffer[] = []; + private buffers: VSBuffer[] = []; - private _onMessage = new Emitter({ + private _onMessage = new Emitter({ onFirstListenerDidAdd: () => { for (const buffer of this.buffers) { this._onMessage.fire(buffer); @@ -32,11 +33,11 @@ class QueueProtocol implements IMessagePassingProtocol { readonly onMessage = this._onMessage.event; other: QueueProtocol; - send(buffer: Buffer): void { + send(buffer: VSBuffer): void { this.other.receive(buffer); } - protected receive(buffer: Buffer): void { + protected receive(buffer: VSBuffer): void { if (this.buffering) { this.buffers.push(buffer); } else { @@ -71,7 +72,7 @@ class TestIPCClient extends IPCClient { class TestIPCServer extends IPCServer { - private onDidClientConnect: Emitter; + private readonly onDidClientConnect: Emitter; constructor() { const onDidClientConnect = new Emitter(); @@ -95,11 +96,11 @@ class TestIPCServer extends IPCServer { const TestChannelId = 'testchannel'; interface ITestService { - marco(): Thenable; - error(message: string): Thenable; - neverComplete(): Thenable; - neverCompleteCT(cancellationToken: CancellationToken): Thenable; - buffersLength(buffers: Buffer[]): Thenable; + marco(): Promise; + error(message: string): Promise; + neverComplete(): Promise; + neverCompleteCT(cancellationToken: CancellationToken): Promise; + buffersLength(buffers: Buffer[]): Promise; pong: Event; } @@ -109,19 +110,19 @@ class TestService implements ITestService { private _pong = new Emitter(); readonly pong = this._pong.event; - marco(): Thenable { + marco(): Promise { return Promise.resolve('polo'); } - error(message: string): Thenable { + error(message: string): Promise { return Promise.reject(new Error(message)); } - neverComplete(): Thenable { + neverComplete(): Promise { return new Promise(_ => { }); } - neverCompleteCT(cancellationToken: CancellationToken): Thenable { + neverCompleteCT(cancellationToken: CancellationToken): Promise { if (cancellationToken.isCancellationRequested) { return Promise.reject(canceled()); } @@ -129,7 +130,7 @@ class TestService implements ITestService { return new Promise((_, e) => cancellationToken.onCancellationRequested(() => e(canceled()))); } - buffersLength(buffers: Buffer[]): Thenable { + buffersLength(buffers: Buffer[]): Promise { return Promise.resolve(buffers.reduce((r, b) => r + b.length, 0)); } @@ -142,7 +143,7 @@ class TestChannel implements IServerChannel { constructor(private service: ITestService) { } - call(_, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(_: unknown, command: string, arg: any, cancellationToken: CancellationToken): Promise { switch (command) { case 'marco': return this.service.marco(); case 'error': return this.service.error(arg); @@ -153,7 +154,7 @@ class TestChannel implements IServerChannel { } } - listen(_, event: string, arg?: any): Event { + listen(_: unknown, event: string, arg?: any): Event { switch (event) { case 'pong': return this.service.pong; default: throw new Error('not implemented'); @@ -169,23 +170,23 @@ class TestChannelClient implements ITestService { constructor(private channel: IChannel) { } - marco(): Thenable { + marco(): Promise { return this.channel.call('marco'); } - error(message: string): Thenable { + error(message: string): Promise { return this.channel.call('error', message); } - neverComplete(): Thenable { + neverComplete(): Promise { return this.channel.call('neverComplete'); } - neverCompleteCT(cancellationToken: CancellationToken): Thenable { + neverCompleteCT(cancellationToken: CancellationToken): Promise { return this.channel.call('neverCompleteCT', undefined, cancellationToken); } - buffersLength(buffers: Buffer[]): Thenable { + buffersLength(buffers: Buffer[]): Promise { return this.channel.call('buffersLength', buffers); } } @@ -195,14 +196,14 @@ suite('Base IPC', function () { test('createProtocolPair', async function () { const [clientProtocol, serverProtocol] = createProtocolPair(); - const b1 = Buffer.alloc(0); + const b1 = VSBuffer.alloc(0); clientProtocol.send(b1); - const b3 = Buffer.alloc(0); + const b3 = VSBuffer.alloc(0); serverProtocol.send(b3); - const b2 = await toPromise(serverProtocol.onMessage); - const b4 = await toPromise(clientProtocol.onMessage); + const b2 = await Event.toPromise(serverProtocol.onMessage); + const b4 = await Event.toPromise(clientProtocol.onMessage); assert.strictEqual(b1, b2); assert.strictEqual(b3, b4); diff --git a/src/vs/base/parts/ipc/test/node/testService.ts b/src/vs/base/parts/ipc/test/node/testService.ts index 241402397d4..6784bdfc60d 100644 --- a/src/vs/base/parts/ipc/test/node/testService.ts +++ b/src/vs/base/parts/ipc/test/node/testService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; @@ -13,9 +13,9 @@ export interface IMarcoPoloEvent { export interface ITestService { onMarco: Event; - marco(): Thenable; - pong(ping: string): Thenable<{ incoming: string, outgoing: string }>; - cancelMe(): Thenable; + marco(): Promise; + pong(ping: string): Promise<{ incoming: string, outgoing: string }>; + cancelMe(): Promise; } export class TestService implements ITestService { @@ -23,16 +23,16 @@ export class TestService implements ITestService { private _onMarco = new Emitter(); onMarco: Event = this._onMarco.event; - marco(): Thenable { + marco(): Promise { this._onMarco.fire({ answer: 'polo' }); return Promise.resolve('polo'); } - pong(ping: string): Thenable<{ incoming: string, outgoing: string }> { + pong(ping: string): Promise<{ incoming: string, outgoing: string }> { return Promise.resolve({ incoming: ping, outgoing: 'pong' }); } - cancelMe(): Thenable { + cancelMe(): Promise { return Promise.resolve(timeout(100)).then(() => true); } } @@ -41,7 +41,7 @@ export class TestChannel implements IServerChannel { constructor(private testService: ITestService) { } - listen(_, event: string): Event { + listen(_: unknown, event: string): Event { switch (event) { case 'marco': return this.testService.onMarco; } @@ -49,7 +49,7 @@ export class TestChannel implements IServerChannel { throw new Error('Event not found'); } - call(_, command: string, ...args: any[]): Thenable { + call(_: unknown, command: string, ...args: any[]): Promise { switch (command) { case 'pong': return this.testService.pong(args[0]); case 'cancelMe': return this.testService.cancelMe(); @@ -65,15 +65,15 @@ export class TestServiceClient implements ITestService { constructor(private channel: IChannel) { } - marco(): Thenable { + marco(): Promise { return this.channel.call('marco'); } - pong(ping: string): Thenable<{ incoming: string, outgoing: string }> { + pong(ping: string): Promise<{ incoming: string, outgoing: string }> { return this.channel.call('pong', ping); } - cancelMe(): Thenable { + cancelMe(): Promise { return this.channel.call('cancelMe'); } } \ 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 0fb0bae29c0..165db403b5d 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -8,10 +8,10 @@ import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; -import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; +import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; import { compareAnything } from 'vs/base/common/comparers'; -import { ActionBar, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import * as DOM from 'vs/base/browser/dom'; import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; @@ -19,6 +19,7 @@ import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLa import { OS } from 'vs/base/common/platform'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { IItemAccessor } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { coalesce } from 'vs/base/common/arrays'; export interface IContext { event: any; @@ -34,18 +35,18 @@ let IDS = 0; export class QuickOpenItemAccessorClass implements IItemAccessor { - getItemLabel(entry: QuickOpenEntry): string { - return entry.getLabel(); + getItemLabel(entry: QuickOpenEntry): string | null { + return types.withUndefinedAsNull(entry.getLabel()); } - getItemDescription(entry: QuickOpenEntry): string { - return entry.getDescription(); + getItemDescription(entry: QuickOpenEntry): string | null { + return types.withUndefinedAsNull(entry.getDescription()); } - getItemPath(entry: QuickOpenEntry): string { + getItemPath(entry: QuickOpenEntry): string | undefined { const resource = entry.getResource(); - return resource ? resource.fsPath : void 0; + return resource ? resource.fsPath : undefined; } } @@ -54,8 +55,8 @@ export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass(); export class QuickOpenEntry { private id: string; private labelHighlights: IHighlight[]; - private descriptionHighlights: IHighlight[]; - private detailHighlights: IHighlight[]; + private descriptionHighlights?: IHighlight[]; + private detailHighlights?: IHighlight[]; private hidden: boolean; constructor(highlights: IHighlight[] = []) { @@ -74,74 +75,73 @@ export class QuickOpenEntry { /** * The label of the entry to identify it from others in the list */ - getLabel(): string { - return null; + getLabel(): string | undefined { + return undefined; } /** * The options for the label to use for this entry */ - getLabelOptions(): IIconLabelValueOptions { - return null; + getLabelOptions(): IIconLabelValueOptions | undefined { + return undefined; } /** * The label of the entry to use when a screen reader wants to read about the entry */ getAriaLabel(): string { - return [this.getLabel(), this.getDescription(), this.getDetail()] - .filter(s => !!s) + return coalesce([this.getLabel(), this.getDescription(), this.getDetail()]) .join(', '); } /** * Detail information about the entry that is optional and can be shown below the label */ - getDetail(): string { - return null; + getDetail(): string | undefined { + return undefined; } /** * The icon of the entry to identify it from others in the list */ - getIcon(): string { - return null; + getIcon(): string | undefined { + return undefined; } /** * A secondary description that is optional and can be shown right to the label */ - getDescription(): string { - return null; + getDescription(): string | undefined { + return undefined; } /** * A tooltip to show when hovering over the entry. */ - getTooltip(): string { - return null; + getTooltip(): string | undefined { + return undefined; } /** * A tooltip to show when hovering over the description portion of the entry. */ - getDescriptionTooltip(): string { - return null; + getDescriptionTooltip(): string | undefined { + return undefined; } /** * An optional keybinding to show for an entry. */ - getKeybinding(): ResolvedKeybinding { - return null; + getKeybinding(): ResolvedKeybinding | undefined { + return undefined; } /** * A resource for this entry. Resource URIs can be used to compare different kinds of entries and group * them together. */ - getResource(): URI { - return null; + getResource(): URI | undefined { + return undefined; } /** @@ -170,7 +170,7 @@ export class QuickOpenEntry { /** * Allows to return highlight ranges that should show up for the entry label and description. */ - getHighlights(): [IHighlight[] /* Label */, IHighlight[] /* Description */, IHighlight[] /* Detail */] { + getHighlights(): [IHighlight[] /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { return [this.labelHighlights, this.descriptionHighlights, this.detailHighlights]; } @@ -180,7 +180,7 @@ export class QuickOpenEntry { * * The context parameter provides additional context information how the run was triggered. */ - run(mode: Mode, context: IContext): boolean { + run(mode: Mode, context: IEntryRunContext): boolean { return false; } @@ -195,9 +195,9 @@ export class QuickOpenEntry { } export class QuickOpenEntryGroup extends QuickOpenEntry { - private entry: QuickOpenEntry; - private groupLabel: string; - private withBorder: boolean; + private entry?: QuickOpenEntry; + private groupLabel?: string; + private withBorder?: boolean; constructor(entry?: QuickOpenEntry, groupLabel?: string, withBorder?: boolean) { super(); @@ -210,11 +210,11 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { /** * The label of the group or null if none. */ - getGroupLabel(): string { + getGroupLabel(): string | undefined { return this.groupLabel; } - setGroupLabel(groupLabel: string): void { + setGroupLabel(groupLabel: string | undefined): void { this.groupLabel = groupLabel; } @@ -222,18 +222,18 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { * Whether to show a border on top of the group entry or not. */ showBorder(): boolean { - return this.withBorder; + return !!this.withBorder; } setShowBorder(showBorder: boolean): void { this.withBorder = showBorder; } - getLabel(): string { + getLabel(): string | undefined { return this.entry ? this.entry.getLabel() : super.getLabel(); } - getLabelOptions(): IIconLabelValueOptions { + getLabelOptions(): IIconLabelValueOptions | undefined { return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions(); } @@ -241,27 +241,27 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.getAriaLabel() : super.getAriaLabel(); } - getDetail(): string { + getDetail(): string | undefined { return this.entry ? this.entry.getDetail() : super.getDetail(); } - getResource(): URI { + getResource(): URI | undefined { return this.entry ? this.entry.getResource() : super.getResource(); } - getIcon(): string { + getIcon(): string | undefined { return this.entry ? this.entry.getIcon() : super.getIcon(); } - getDescription(): string { + getDescription(): string | undefined { return this.entry ? this.entry.getDescription() : super.getDescription(); } - getEntry(): QuickOpenEntry { + getEntry(): QuickOpenEntry | undefined { return this.entry; } - getHighlights(): [IHighlight[], IHighlight[], IHighlight[]] { + getHighlights(): [IHighlight[], IHighlight[] | undefined, IHighlight[] | undefined] { return this.entry ? this.entry.getHighlights() : super.getHighlights(); } @@ -277,7 +277,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { this.entry ? this.entry.setHidden(hidden) : super.setHidden(hidden); } - run(mode: Mode, context: IContext): boolean { + run(mode: Mode, context: IEntryRunContext): boolean { return this.entry ? this.entry.run(mode, context) : super.run(mode, context); } } @@ -288,19 +288,7 @@ class NoActionProvider implements IActionProvider { return false; } - getActions(tree: ITree, element: any): IAction[] { - return null; - } - - hasSecondaryActions(tree: ITree, element: any): boolean { - return false; - } - - getSecondaryActions(tree: ITree, element: any): IAction[] { - return null; - } - - getActionItem(tree: ITree, element: any, action: Action): IActionItem { + getActions(tree: ITree, element: any): IAction[] | null { return null; } } @@ -316,7 +304,7 @@ export interface IQuickOpenEntryTemplateData { } export interface IQuickOpenEntryGroupTemplateData extends IQuickOpenEntryTemplateData { - group: HTMLDivElement; + group?: HTMLDivElement; } const templateEntry = 'quickOpenEntry'; @@ -325,9 +313,9 @@ const templateEntryGroup = 'quickOpenEntryGroup'; class Renderer implements IRenderer { private actionProvider: IActionProvider; - private actionRunner: IActionRunner; + private actionRunner?: IActionRunner; - constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner: IActionRunner | null = null) { + constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner?: IActionRunner) { this.actionProvider = actionProvider; this.actionRunner = actionRunner; } @@ -356,7 +344,7 @@ class Renderer implements IRenderer { // Entry const row1 = DOM.$('.quick-open-row'); const row2 = DOM.$('.quick-open-row'); - const entry = DOM.$('.quick-open-entry', null, row1, row2); + const entry = DOM.$('.quick-open-entry', undefined, row1, row2); entryContainer.appendChild(entry); // Icon @@ -379,7 +367,7 @@ class Renderer implements IRenderer { const detail = new HighlightedLabel(detailContainer, true); // Entry Group - let group: HTMLDivElement; + let group: HTMLDivElement | undefined; if (templateId === templateEntryGroup) { group = document.createElement('div'); DOM.addClass(group, 'results-group'); @@ -442,7 +430,9 @@ class Renderer implements IRenderer { // Border if (group.showBorder()) { DOM.addClass(groupData.container, 'results-group-separator'); - groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString(); + if (styles.pickerGroupBorder) { + groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString(); + } } else { DOM.removeClass(groupData.container, 'results-group-separator'); groupData.container.style.borderTopColor = null; @@ -450,8 +440,12 @@ class Renderer implements IRenderer { // Group Label const groupLabel = group.getGroupLabel() || ''; - groupData.group.textContent = groupLabel; - groupData.group.style.color = styles.pickerGroupForeground.toString(); + if (groupData.group) { + groupData.group.textContent = groupLabel; + if (styles.pickerGroupForeground) { + groupData.group.style.color = styles.pickerGroupForeground.toString(); + } + } } // Normal Entry @@ -468,30 +462,27 @@ class Renderer implements IRenderer { options.title = entry.getTooltip(); options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow options.descriptionMatches = descriptionHighlights || []; - data.label.setValue(entry.getLabel(), entry.getDescription(), options); + data.label.setLabel(types.withNullAsUndefined(entry.getLabel()), entry.getDescription(), options); // Meta data.detail.set(entry.getDetail(), detailHighlights); // Keybinding - data.keybinding.set(entry.getKeybinding(), null); + data.keybinding.set(entry.getKeybinding()!); } } disposeTemplate(templateId: string, templateData: IQuickOpenEntryGroupTemplateData): void { - const data = templateData as IQuickOpenEntryGroupTemplateData; - data.actionBar.dispose(); - data.actionBar = null; - data.container = null; - data.entry = null; - data.keybinding.dispose(); - data.keybinding = null; - data.detail.dispose(); - data.detail = null; - data.group = null; - data.icon = null; - data.label.dispose(); - data.label = null; + templateData.actionBar.dispose(); + templateData.actionBar = null!; + templateData.container = null!; + templateData.entry = null!; + templateData.keybinding = null!; + templateData.detail = null!; + templateData.group = null!; + templateData.icon = null!; + templateData.label.dispose(); + templateData.label = null!; } } @@ -564,8 +555,8 @@ export class QuickOpenModel implements return entry.getId(); } - getLabel(entry: QuickOpenEntry): string { - return entry.getLabel(); + getLabel(entry: QuickOpenEntry): string | null { + return types.withUndefinedAsNull(entry.getLabel()); } getAriaLabel(entry: QuickOpenEntry): string { @@ -581,7 +572,7 @@ export class QuickOpenModel implements return !entry.isHidden(); } - run(entry: QuickOpenEntry, mode: Mode, context: IContext): boolean { + run(entry: QuickOpenEntry, mode: Mode, context: IEntryRunContext): boolean { return entry.run(mode, context); } } @@ -605,8 +596,8 @@ export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntr } // Fallback to the full path if labels are identical and we have associated resources - let nameA = elementA.getLabel(); - let nameB = elementB.getLabel(); + let nameA = elementA.getLabel()!; + let nameB = elementB.getLabel()!; if (nameA === nameB) { const resourceA = elementA.getResource(); const resourceB = elementB.getResource(); diff --git a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts b/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts index 58f0f68b304..a87b30dbadc 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { isFunction } from 'vs/base/common/types'; import { ITree, IRenderer, IFilter, IDataSource, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree'; import { IModel } from 'vs/base/parts/quickopen/common/quickOpen'; @@ -25,7 +24,7 @@ export class DataSource implements IDataSource { getId(tree: ITree, element: any): string { if (!element) { - return null; + return null!; } const model = this.modelProvider.getModel(); @@ -34,26 +33,26 @@ export class DataSource implements IDataSource { hasChildren(tree: ITree, element: any): boolean { const model = this.modelProvider.getModel(); - return model && model === element && model.entries.length > 0; + return !!(model && model === element && model.entries.length > 0); } - getChildren(tree: ITree, element: any): TPromise { + getChildren(tree: ITree, element: any): Promise { const model = this.modelProvider.getModel(); - return TPromise.as(model === element ? model.entries : []); + return Promise.resolve(model === element ? model.entries : []); } - getParent(tree: ITree, element: any): TPromise { - return TPromise.as(null); + getParent(tree: ITree, element: any): Promise { + return Promise.resolve(null); } } export class AccessibilityProvider implements IAccessibilityProvider { constructor(private modelProvider: IModelProvider) { } - getAriaLabel(tree: ITree, element: any): string { + getAriaLabel(tree: ITree, element: any): string | null { const model = this.modelProvider.getModel(); - return model.accessibilityProvider && model.accessibilityProvider.getAriaLabel(element); + return model.accessibilityProvider ? model.accessibilityProvider.getAriaLabel(element) : null; } getPosInSet(tree: ITree, element: any): string { @@ -140,4 +139,4 @@ export class Renderer implements IRenderer { const model = this.modelProvider.getModel(); model.renderer.disposeTemplate(templateId, templateData); } -} \ No newline at end of file +} diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index c761e286019..74148e4a0fd 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -36,7 +36,7 @@ export interface IQuickOpenCallbacks { export interface IQuickOpenOptions extends IQuickOpenStyles { minItemsToShow?: number; maxItemsToShow?: number; - inputPlaceHolder: string; + inputPlaceHolder?: string; inputAriaLabel?: string; actionProvider?: IActionProvider; keyboardSupport?: boolean; @@ -110,12 +110,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { private visible: boolean; private isLoosingFocus: boolean; private callbacks: IQuickOpenCallbacks; - private quickNavigateConfiguration: IQuickNavigateConfiguration; + private quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; private container: HTMLElement; private treeElement: HTMLElement; private inputElement: HTMLElement; private layoutDimensions: DOM.Dimension; - private model: IModel; + private model: IModel | null; private inputChangingTimeoutHandle: any; private styles: IQuickOpenStyles; private renderer: Renderer; @@ -137,7 +137,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } getModel(): IModel { - return this.model; + return this.model!; } setCallbacks(callbacks: IQuickOpenCallbacks): void { @@ -155,13 +155,13 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { this._register(DOM.addDisposableListener(this.element, DOM.EventType.FOCUS, e => this.gainingFocus(), true)); this._register(DOM.addDisposableListener(this.element, DOM.EventType.BLUR, e => this.loosingFocus(e), true)); this._register(DOM.addDisposableListener(this.element, DOM.EventType.KEY_DOWN, e => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e as KeyboardEvent); + const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); if (keyboardEvent.keyCode === KeyCode.Escape) { DOM.EventHelper.stop(e, true); this.hide(HideReason.CANCELED); } else if (keyboardEvent.keyCode === KeyCode.Tab && !keyboardEvent.altKey && !keyboardEvent.ctrlKey && !keyboardEvent.metaKey) { - const stops = e.currentTarget.querySelectorAll('input, .monaco-tree, .monaco-tree-row.focused .action-label.icon'); + const stops = (e.currentTarget as HTMLElement).querySelectorAll('input, .monaco-tree, .monaco-tree-row.focused .action-label.icon') as NodeListOf; if (keyboardEvent.shiftKey && keyboardEvent.target === stops[0]) { DOM.EventHelper.stop(e, true); stops[stops.length - 1].focus(); @@ -181,7 +181,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { DOM.addClass(this.inputContainer, 'quick-open-input'); this.element.appendChild(this.inputContainer); - this.inputBox = this._register(new InputBox(this.inputContainer, null, { + this.inputBox = this._register(new InputBox(this.inputContainer, undefined, { placeholder: this.options.inputPlaceHolder || '', ariaLabel: DEFAULT_INPUT_ARIA_LABEL, inputBackground: this.styles.inputBackground, @@ -277,7 +277,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { this._register(this.tree.onDidChangeSelection(event => { if (event.selection && event.selection.length > 0) { - const mouseEvent: StandardMouseEvent = event.payload && event.payload.originalEvent instanceof StandardMouseEvent ? event.payload.originalEvent : void 0; + const mouseEvent: StandardMouseEvent = event.payload && event.payload.originalEvent instanceof StandardMouseEvent ? event.payload.originalEvent : undefined; const shouldOpenInBackground = mouseEvent ? this.shouldOpenInBackground(mouseEvent) : false; this.elementSelected(event.selection[0], event, shouldOpenInBackground ? Mode.OPEN_IN_BACKGROUND : Mode.OPEN); @@ -285,7 +285,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { })); this._register(DOM.addDisposableListener(this.treeContainer, DOM.EventType.KEY_DOWN, e => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e as KeyboardEvent); + const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); // Only handle when in quick navigation mode if (!this.quickNavigateConfiguration) { @@ -301,7 +301,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { })); this._register(DOM.addDisposableListener(this.treeContainer, DOM.EventType.KEY_UP, e => { - const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e as KeyboardEvent); + const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); const keyCode = keyboardEvent.keyCode; // Only handle when in quick navigation mode @@ -538,10 +538,15 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } // ARIA - this.inputElement.setAttribute('aria-activedescendant', this.treeElement.getAttribute('aria-activedescendant')); + const arivaActiveDescendant = this.treeElement.getAttribute('aria-activedescendant'); + if (arivaActiveDescendant) { + this.inputElement.setAttribute('aria-activedescendant', arivaActiveDescendant); + } else { + this.inputElement.removeAttribute('aria-activedescendant'); + } const context: IEntryRunContext = { event: event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration }; - this.model.runner.run(value, Mode.PREVIEW, context); + this.model!.runner.run(value, Mode.PREVIEW, context); } private elementSelected(value: any, event?: any, preferredMode?: Mode): void { @@ -553,7 +558,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { const context: IEntryRunContext = { event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration }; - hide = this.model.runner.run(value, mode, context); + hide = this.model!.runner.run(value, mode, context); } // Hide if command was run successfully @@ -574,7 +579,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { show(param: any, options?: IShowOptions): void { this.visible = true; this.isLoosingFocus = false; - this.quickNavigateConfiguration = options ? options.quickNavigateConfiguration : void 0; + this.quickNavigateConfiguration = options ? options.quickNavigateConfiguration : undefined; // Adjust UI for quick navigate mode if (this.quickNavigateConfiguration) { @@ -603,7 +608,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { if (types.isString(param)) { this.doShowWithPrefix(param); } else { - if (options.value) { + if (options && options.value) { this.restoreLastInput(options.value); } this.doShowWithInput(param, options && options.autoFocus ? options.autoFocus : {}); @@ -634,7 +639,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { this.setInput(input, autoFocus); } - private setInputAndLayout(input: IModel, autoFocus: IAutoFocus): void { + private setInputAndLayout(input: IModel, autoFocus?: IAutoFocus): void { this.treeContainer.style.height = `${this.getHeight(input)}px`; this.tree.setInput(null).then(() => { @@ -675,9 +680,8 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { let caseInsensitiveMatch: any; const prefix = autoFocus.autoFocusPrefixMatch; const lowerCasePrefix = prefix.toLowerCase(); - for (let i = 0; i < entries.length; i++) { - const entry = entries[i]; - const label = input.dataSource.getLabel(entry); + for (const entry of entries) { + const label = input.dataSource.getLabel(entry) || ''; if (!caseSensitiveMatch && label.indexOf(prefix) === 0) { caseSensitiveMatch = entry; @@ -748,13 +752,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)) : []; + const entries = input ? input.entries!.filter(e => this.isElementVisible(input!, e)) : []; this.updateResultCount(entries.length); // Handle auto focus if (autoFocus) { if (entries.length) { - this.autoFocus(input, entries, autoFocus); + this.autoFocus(input!, entries, autoFocus); } } }); @@ -771,9 +775,9 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { let height = 0; - let preferredItemsHeight: number; + let preferredItemsHeight: number | undefined; if (this.layoutDimensions && this.layoutDimensions.height) { - preferredItemsHeight = (this.layoutDimensions.height - 50 /* subtract height of input field (30px) and some spacing (drop shadow) to fit */) * 0.40 /* max 40% of screen */; + preferredItemsHeight = (this.layoutDimensions.height - 50 /* subtract height of input field (30px) and some spacing (drop shadow) to fit */) * 0.4 /* max 40% of screen */; } if (!preferredItemsHeight || preferredItemsHeight > QuickOpenWidget.MAX_ITEMS_HEIGHT) { @@ -835,12 +839,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } if (this.callbacks.onHide) { - this.callbacks.onHide(reason); + this.callbacks.onHide(reason!); } } getQuickNavigateConfiguration(): IQuickNavigateConfiguration { - return this.quickNavigateConfiguration; + return this.quickNavigateConfiguration!; } setPlaceHolder(placeHolder: string): void { @@ -869,7 +873,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } } - setInput(input: IModel, autoFocus: IAutoFocus, ariaLabel?: string): void { + setInput(input: IModel, autoFocus?: IAutoFocus, ariaLabel?: string): void { if (!this.isVisible()) { return; } @@ -941,7 +945,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { return this.inputBox; } - setExtraClass(clazz: string): void { + setExtraClass(clazz: string | null): void { const previousClass = this.element.getAttribute('quick-open-extra-class'); if (previousClass) { DOM.removeClasses(this.element, previousClass); diff --git a/src/vs/base/parts/quickopen/common/quickOpen.ts b/src/vs/base/parts/quickopen/common/quickOpen.ts index e6d87a0b0e7..dd39a215ef1 100644 --- a/src/vs/base/parts/quickopen/common/quickOpen.ts +++ b/src/vs/base/parts/quickopen/common/quickOpen.ts @@ -49,7 +49,7 @@ export const enum Mode { export interface IEntryRunContext { event: any; keymods: IKeyMods; - quickNavigateConfiguration: IQuickNavigateConfiguration; + quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; } export interface IKeyMods { @@ -59,7 +59,7 @@ export interface IKeyMods { export interface IDataSource { getId(entry: T): string; - getLabel(entry: T): string; + getLabel(entry: T): string | null; } /** diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts index bb2efd7ad41..21cb08caace 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { compareAnything } from 'vs/base/common/comparers'; -import { matchesPrefix, IMatch, createMatches, matchesCamelCase, isUpper } from 'vs/base/common/filters'; -import { nativeSep } from 'vs/base/common/paths'; +import { matchesPrefix, IMatch, matchesCamelCase, isUpper } from 'vs/base/common/filters'; +import { sep } from 'vs/base/common/path'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; @@ -60,7 +60,7 @@ export function score(target: string, query: string, queryLower: string, fuzzy: return res; } -function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] { +function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): Score { const scores: number[] = []; const matches: number[] = []; @@ -80,15 +80,25 @@ function doScore(query: string, queryLower: string, queryLength: number, target: // y // for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) { + const queryIndexOffset = queryIndex * targetLength; + const queryIndexPreviousOffset = queryIndexOffset - targetLength; + + const queryIndexGtNull = queryIndex > 0; + + const queryCharAtIndex = query[queryIndex]; + const queryLowerCharAtIndex = queryLower[queryIndex]; + for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) { - const currentIndex = queryIndex * targetLength + targetIndex; + const targetIndexGtNull = targetIndex > 0; + + const currentIndex = queryIndexOffset + targetIndex; const leftIndex = currentIndex - 1; - const diagIndex = (queryIndex - 1) * targetLength + targetIndex - 1; + const diagIndex = queryIndexPreviousOffset + targetIndex - 1; - const leftScore: number = targetIndex > 0 ? scores[leftIndex] : 0; - const diagScore: number = queryIndex > 0 && targetIndex > 0 ? scores[diagIndex] : 0; + const leftScore = targetIndexGtNull ? scores[leftIndex] : 0; + const diagScore = queryIndexGtNull && targetIndexGtNull ? scores[diagIndex] : 0; - const matchesSequenceLength: number = queryIndex > 0 && targetIndex > 0 ? matches[diagIndex] : 0; + const matchesSequenceLength = queryIndexGtNull && targetIndexGtNull ? 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). @@ -96,10 +106,10 @@ function doScore(query: string, queryLower: string, queryLength: number, target: // 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) { + if (!diagScore && queryIndexGtNull) { score = 0; } else { - score = computeCharScore(query, queryLower, queryIndex, target, targetLower, targetIndex, matchesSequenceLength); + score = computeCharScore(queryCharAtIndex, queryLowerCharAtIndex, target, targetLower, targetIndex, matchesSequenceLength); } // We have a score and its equal or larger than the left score @@ -146,10 +156,10 @@ function doScore(query: string, queryLower: string, queryLength: number, target: return [scores[queryLength * targetLength - 1], positions.reverse()]; } -function computeCharScore(query: string, queryLower: string, queryIndex: number, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { +function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: string, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { let score = 0; - if (queryLower[queryIndex] !== targetLower[targetIndex]) { + if (queryLowerCharAtIndex !== targetLower[targetIndex]) { return score; // no match of characters } @@ -170,7 +180,7 @@ function computeCharScore(query: string, queryLower: string, queryIndex: number, } // Same case bonus - if (query[queryIndex] === target[targetIndex]) { + if (queryCharAtIndex === target[targetIndex]) { score += 1; // if (DEBUG) { @@ -275,17 +285,17 @@ export interface IItemAccessor { /** * Just the label of the item to score on. */ - getItemLabel(item: T): string; + getItemLabel(item: T): string | null; /** * The optional description of the item to score on. Can be null. */ - getItemDescription(item: T): string; + getItemDescription(item: T): string | null; /** * If the item is a file, the path of the file to score on. Can be null. */ - getItemPath(file: T): string; + getItemPath(file: T): string | undefined; } const PATH_IDENTITY_SCORE = 1 << 18; @@ -310,11 +320,11 @@ export function prepareQuery(original: string): IPreparedQuery { let value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace if (isWindows) { - value = value.replace(/\//g, nativeSep); // Help Windows users to search for paths when using slash + value = value.replace(/\//g, sep); // Help Windows users to search for paths when using slash } const lowercase = value.toLowerCase(); - const containsPathSeparator = value.indexOf(nativeSep) >= 0; + const containsPathSeparator = value.indexOf(sep) >= 0; return { original, value, lowercase, containsPathSeparator }; } @@ -349,11 +359,28 @@ export function scoreItem(item: T, query: IPreparedQuery, fuzzy: boolean, acc return itemScore; } -function doScoreItem(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore { +function createMatches(offsets: undefined | number[]): IMatch[] { + let ret: IMatch[] = []; + if (!offsets) { + return ret; + } + let last: IMatch | undefined; + for (const pos of offsets) { + if (last && last.end === pos) { + last.end += 1; + } else { + last = { start: pos, end: pos + 1 }; + ret.push(last); + } + } + return ret; +} + +function doScoreItem(label: string, description: string | null, path: string | undefined, 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 }; + 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 }] : undefined }; } // We only consider label matches if the query is not including file path separators @@ -383,7 +410,7 @@ function doScoreItem(label: string, description: string, path: string, query: IP if (description) { let descriptionPrefix = description; if (!!path) { - descriptionPrefix = `${description}${nativeSep}`; // assume this is a file path + descriptionPrefix = `${description}${sep}`; // assume this is a file path } const descriptionPrefixLength = descriptionPrefix.length; @@ -442,8 +469,8 @@ export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery return scoreA === LABEL_PREFIX_SCORE ? -1 : 1; } - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; // prefer shorter names when both match on label prefix if (labelA.length !== labelB.length) { @@ -457,8 +484,8 @@ export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1; } - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); + 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); @@ -565,8 +592,8 @@ function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { // check for label + description length and prefer shorter - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; const descriptionA = accessor.getItemDescription(itemA); const descriptionB = accessor.getItemDescription(itemB); @@ -605,4 +632,4 @@ export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, ac // equal return 0; -} \ No newline at end of file +} diff --git a/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts b/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts index f42a2f73e9b..f53d0b4e1ed 100644 --- a/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts +++ b/src/vs/base/parts/quickopen/test/browser/quickopen.test.ts @@ -38,11 +38,11 @@ suite('QuickOpen', () => { model.addEntries([entry1, entry2, entry3]); const ds = new DataSource(model); - assert.equal(entry1.getId(), ds.getId(null, entry1)); - assert.equal(true, ds.hasChildren(null, model)); - assert.equal(false, ds.hasChildren(null, entry1)); + assert.equal(entry1.getId(), ds.getId(null!, entry1)); + assert.equal(true, ds.hasChildren(null!, model)); + assert.equal(false, ds.hasChildren(null!, entry1)); - ds.getChildren(null, model).then((children: any[]) => { + ds.getChildren(null!, model).then((children: any[]) => { assert.equal(3, children.length); }); }); diff --git a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts index 57fb24a9d13..47c51b87902 100644 --- a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts +++ b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import * as scorer from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { URI } from 'vs/base/common/uri'; -import { basename, dirname, nativeSep } from 'vs/base/common/paths'; +import { basename, dirname, sep } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; class ResourceAccessorClass implements scorer.IItemAccessor { @@ -29,15 +29,15 @@ const ResourceAccessor = new ResourceAccessorClass(); class NullAccessorClass implements scorer.IItemAccessor { getItemLabel(resource: URI): string { - return void 0; + return undefined!; } getItemDescription(resource: URI): string { - return void 0; + return undefined!; } getItemPath(resource: URI): string { - return void 0; + return undefined!; } } @@ -120,52 +120,52 @@ suite('Quick Open Scorer', () => { // Path Identity const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); assert.ok(identityRes.score); - assert.equal(identityRes.descriptionMatch.length, 1); - assert.equal(identityRes.labelMatch.length, 1); - assert.equal(identityRes.descriptionMatch[0].start, 0); - assert.equal(identityRes.descriptionMatch[0].end, ResourceAccessor.getItemDescription(resource).length); - assert.equal(identityRes.labelMatch[0].start, 0); - assert.equal(identityRes.labelMatch[0].end, ResourceAccessor.getItemLabel(resource).length); + assert.equal(identityRes.descriptionMatch!.length, 1); + assert.equal(identityRes.labelMatch!.length, 1); + assert.equal(identityRes.descriptionMatch![0].start, 0); + assert.equal(identityRes.descriptionMatch![0].end, ResourceAccessor.getItemDescription(resource).length); + assert.equal(identityRes.labelMatch![0].start, 0); + assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length); // Basename Prefix const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache); assert.ok(basenamePrefixRes.score); assert.ok(!basenamePrefixRes.descriptionMatch); - assert.equal(basenamePrefixRes.labelMatch.length, 1); - assert.equal(basenamePrefixRes.labelMatch[0].start, 0); - assert.equal(basenamePrefixRes.labelMatch[0].end, 'som'.length); + assert.equal(basenamePrefixRes.labelMatch!.length, 1); + assert.equal(basenamePrefixRes.labelMatch![0].start, 0); + assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length); // Basename Camelcase const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache); assert.ok(basenameCamelcaseRes.score); assert.ok(!basenameCamelcaseRes.descriptionMatch); - assert.equal(basenameCamelcaseRes.labelMatch.length, 2); - assert.equal(basenameCamelcaseRes.labelMatch[0].start, 0); - assert.equal(basenameCamelcaseRes.labelMatch[0].end, 1); - assert.equal(basenameCamelcaseRes.labelMatch[1].start, 4); - assert.equal(basenameCamelcaseRes.labelMatch[1].end, 5); + assert.equal(basenameCamelcaseRes.labelMatch!.length, 2); + assert.equal(basenameCamelcaseRes.labelMatch![0].start, 0); + assert.equal(basenameCamelcaseRes.labelMatch![0].end, 1); + assert.equal(basenameCamelcaseRes.labelMatch![1].start, 4); + assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5); // Basename Match const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache); assert.ok(basenameRes.score); assert.ok(!basenameRes.descriptionMatch); - assert.equal(basenameRes.labelMatch.length, 2); - assert.equal(basenameRes.labelMatch[0].start, 1); - assert.equal(basenameRes.labelMatch[0].end, 2); - assert.equal(basenameRes.labelMatch[1].start, 4); - assert.equal(basenameRes.labelMatch[1].end, 5); + assert.equal(basenameRes.labelMatch!.length, 2); + assert.equal(basenameRes.labelMatch![0].start, 1); + assert.equal(basenameRes.labelMatch![0].end, 2); + assert.equal(basenameRes.labelMatch![1].start, 4); + assert.equal(basenameRes.labelMatch![1].end, 5); // Path Match const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 8); - assert.equal(pathRes.labelMatch[0].end, 11); - assert.equal(pathRes.descriptionMatch.length, 1); - assert.equal(pathRes.descriptionMatch[0].start, 1); - assert.equal(pathRes.descriptionMatch[0].end, 4); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 8); + assert.equal(pathRes.labelMatch![0].end, 11); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 1); + assert.equal(pathRes.descriptionMatch![0].end, 4); // No Match const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache); @@ -182,7 +182,7 @@ suite('Quick Open Scorer', () => { test('scoreItem - invalid input', function () { - let res = scoreItem(null, null, true, ResourceAccessor, cache); + let res = scoreItem(null, null!, true, ResourceAccessor, cache); assert.equal(res.score, 0); res = scoreItem(null, 'null', true, ResourceAccessor, cache); @@ -199,12 +199,12 @@ suite('Quick Open Scorer', () => { assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 0); - assert.equal(pathRes.labelMatch[0].end, 7); - assert.equal(pathRes.descriptionMatch.length, 1); - assert.equal(pathRes.descriptionMatch[0].start, 23); - assert.equal(pathRes.descriptionMatch[0].end, 26); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 7); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 23); + assert.equal(pathRes.descriptionMatch![0].end, 26); }); test('scoreItem - avoid match scattering (bug #36119)', function () { @@ -214,9 +214,9 @@ suite('Quick Open Scorer', () => { assert.ok(pathRes.score); assert.ok(pathRes.descriptionMatch); assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch.length, 1); - assert.equal(pathRes.labelMatch[0].start, 0); - assert.equal(pathRes.labelMatch[0].end, 9); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 9); }); test('scoreItem - prefers more compact matches', function () { @@ -227,12 +227,12 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'ad', true, ResourceAccessor, cache); assert.ok(res.score); assert.ok(res.descriptionMatch); - assert.ok(!res.labelMatch.length); - assert.equal(res.descriptionMatch.length, 2); - assert.equal(res.descriptionMatch[0].start, 11); - assert.equal(res.descriptionMatch[0].end, 12); - assert.equal(res.descriptionMatch[1].start, 13); - assert.equal(res.descriptionMatch[1].end, 14); + assert.ok(!res.labelMatch!.length); + assert.equal(res.descriptionMatch!.length, 2); + assert.equal(res.descriptionMatch![0].start, 11); + assert.equal(res.descriptionMatch![0].end, 12); + assert.equal(res.descriptionMatch![1].start, 13); + assert.equal(res.descriptionMatch![1].end, 14); }); test('scoreItem - proper target offset', function () { @@ -247,9 +247,9 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'de', true, ResourceAccessor, cache); - assert.equal(res.labelMatch.length, 1); - assert.equal(res.labelMatch[0].start, 1); - assert.equal(res.labelMatch[0].end, 3); + assert.equal(res.labelMatch!.length, 1); + assert.equal(res.labelMatch![0].start, 1); + assert.equal(res.labelMatch![0].end, 3); }); test('scoreItem - proper target offset #3', function () { @@ -257,19 +257,19 @@ suite('Quick Open Scorer', () => { const res = scoreItem(resource, 'debug', true, ResourceAccessor, cache); - assert.equal(res.descriptionMatch.length, 3); - assert.equal(res.descriptionMatch[0].start, 9); - assert.equal(res.descriptionMatch[0].end, 10); - assert.equal(res.descriptionMatch[1].start, 36); - assert.equal(res.descriptionMatch[1].end, 37); - assert.equal(res.descriptionMatch[2].start, 40); - assert.equal(res.descriptionMatch[2].end, 41); + assert.equal(res.descriptionMatch!.length, 3); + assert.equal(res.descriptionMatch![0].start, 9); + assert.equal(res.descriptionMatch![0].end, 10); + assert.equal(res.descriptionMatch![1].start, 36); + assert.equal(res.descriptionMatch![1].end, 37); + assert.equal(res.descriptionMatch![2].start, 40); + assert.equal(res.descriptionMatch![2].end, 41); - assert.equal(res.labelMatch.length, 2); - assert.equal(res.labelMatch[0].start, 9); - assert.equal(res.labelMatch[0].end, 10); - assert.equal(res.labelMatch[1].start, 20); - assert.equal(res.labelMatch[1].end, 21); + assert.equal(res.labelMatch!.length, 2); + assert.equal(res.labelMatch![0].start, 9); + assert.equal(res.labelMatch![0].end, 10); + assert.equal(res.labelMatch![1].start, 20); + assert.equal(res.labelMatch![1].end, 21); }); test('scoreItem - no match unless query contained in sequence', function () { @@ -797,9 +797,9 @@ suite('Quick Open Scorer', () => { }); test('compareFilesByScore - avoid match scattering (bug #12095)', function () { - const resourceA = URI.file('src/vs/workbench/parts/files/common/explorerViewModel.ts'); - const resourceB = URI.file('src/vs/workbench/parts/files/browser/views/explorerView.ts'); - const resourceC = URI.file('src/vs/workbench/parts/files/browser/views/explorerViewer.ts'); + const resourceA = URI.file('src/vs/workbench/contrib/files/common/explorerViewModel.ts'); + const resourceB = URI.file('src/vs/workbench/contrib/files/browser/views/explorerView.ts'); + const resourceC = URI.file('src/vs/workbench/contrib/files/browser/views/explorerViewer.ts'); let query = 'filesexplorerview.ts'; @@ -815,6 +815,6 @@ suite('Quick Open Scorer', () => { assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts'); assert.equal(scorer.prepareQuery('Model Tester.ts').lowercase, 'modeltester.ts'); assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false); - assert.equal(scorer.prepareQuery('Model' + nativeSep + 'Tester.ts').containsPathSeparator, true); + assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true); }); }); \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index 307b19f9584..970e05e7579 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as WinJS from 'vs/base/common/winjs.base'; import * as Touch from 'vs/base/browser/touch'; import * as Mouse from 'vs/base/browser/mouseEvent'; import * as Keyboard from 'vs/base/browser/keyboardEvent'; import { INavigator } from 'vs/base/common/iterator'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { Event } from 'vs/base/common/event'; -import { IAction, IActionItem } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { Color } from 'vs/base/common/color'; import { IItemCollapseEvent, IItemExpandEvent } from 'vs/base/parts/tree/browser/treeModel'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface ITree { @@ -50,7 +50,7 @@ export interface ITree { /** * Sets the input of the tree. */ - setInput(element: any): WinJS.Promise; + setInput(element: any): Promise; /** * Returns the tree's input. @@ -76,179 +76,69 @@ export interface ITree { * Refreshes an element. * Provide no arguments and it will refresh the input element. */ - refresh(element?: any, recursive?: boolean): WinJS.Promise; - - /** - * Updates an element's width. - */ - updateWidth(element: any): void; + refresh(element?: any, recursive?: boolean): Promise; /** * Expands an element. * The returned promise returns a boolean for whether the element was expanded or not. */ - expand(element: any): WinJS.Promise; + expand(element: any): Promise; /** * Expands several elements. * The returned promise returns a boolean array for whether the elements were expanded or not. */ - expandAll(elements?: any[]): WinJS.Promise; + expandAll(elements?: any[]): Promise; /** * Collapses an element. * The returned promise returns a boolean for whether the element was collapsed or not. */ - collapse(element: any, recursive?: boolean): WinJS.Promise; + collapse(element: any, recursive?: boolean): Promise; /** * Collapses several elements. * Provide no arguments and it will recursively collapse all elements in the tree * The returned promise returns a boolean for whether the elements were collapsed or not. */ - collapseAll(elements?: any[], recursive?: boolean): WinJS.Promise; + collapseAll(elements?: any[], recursive?: boolean): Promise; /** * Toggles an element's expansion state. */ - toggleExpansion(element: any, recursive?: boolean): WinJS.Promise; - - /** - * Toggles several element's expansion state. - */ - toggleExpansionAll(elements: any[]): WinJS.Promise; + toggleExpansion(element: any, recursive?: boolean): Promise; /** * Returns whether an element is expanded or not. */ isExpanded(element: any): boolean; - /** - * Returns a list of the currently expanded elements. - */ - getExpandedElements(): any[]; - /** * Reveals an element in the tree. The relativeTop is a value between 0 and 1. The closer to 0 the more the * element will scroll up to the top. */ - reveal(element: any, relativeTop?: number): WinJS.Promise; - - /** - * Returns the relative top position of any given element, if visible. - * If not visible, returns a negative number or a number > 1. - * Useful when calling `reveal(element, relativeTop)`. - */ - getRelativeTop(element: any): number; - - /** - * Returns the top-most visible element. - */ - getFirstVisibleElement(): any; - - /** - * Returns a number between 0 and 1 representing how much the tree is scroll down. 0 means all the way - * to the top; 1 means all the way down. - */ - getScrollPosition(): number; - - /** - * Sets the scroll position with a number between 0 and 1 representing how much the tree is scroll down. 0 means all the way - * to the top; 1 means all the way down. - */ - setScrollPosition(pos: number): void; - - /** - * Returns the total height of the tree's content. - */ - getContentHeight(): number; - - /** - * Sets the tree's highlight to be the given element. - * Provide no arguments and it clears the tree's highlight. - */ - setHighlight(element?: any, eventPayload?: any): void; + reveal(element: any, relativeTop?: number): Promise; /** * Returns the currently highlighted element. */ getHighlight(includeHidden?: boolean): any; - /** - * Returns whether an element is highlighted or not. - */ - isHighlighted(element: any): boolean; - /** * Clears the highlight. */ clearHighlight(eventPayload?: any): void; - /** - * Selects an element. - */ - select(element: any, eventPayload?: any): void; - - /** - * Selects a range of elements. - */ - selectRange(fromElement: any, toElement: any, eventPayload?: any): void; - - /** - * Deselects a range of elements. - */ - deselectRange(fromElement: any, toElement: any, eventPayload?: any): void; - - /** - * Selects several elements. - */ - selectAll(elements: any[], eventPayload?: any): void; - - /** - * Deselects an element. - */ - deselect(element: any, eventPayload?: any): void; - - /** - * Deselects several elements. - */ - deselectAll(elements: any[], eventPayload?: any): void; - /** * Replaces the current selection with the given elements. */ setSelection(elements: any[], eventPayload?: any): void; - /** - * Toggles the element's selection. - */ - toggleSelection(element: any, eventPayload?: any): void; - /** * Returns the currently selected elements. */ getSelection(includeHidden?: boolean): any[]; - /** - * Returns whether an element is selected or not. - */ - isSelected(element: any): boolean; - - /** - * Selects the next `count`-nth element, in visible order. - */ - selectNext(count?: number, clearSelection?: boolean, eventPayload?: any): void; - - /** - * Selects the previous `count`-nth element, in visible order. - */ - selectPrevious(count?: number, clearSelection?: boolean, eventPayload?: any): void; - - /** - * Selects the currently selected element's parent. - */ - selectParent(clearSelection?: boolean, eventPayload?: any): void; - /** * Clears the selection. */ @@ -259,11 +149,6 @@ export interface ITree { */ setFocus(element?: any, eventPayload?: any): void; - /** - * Returns whether an element is focused or not. - */ - isFocused(element: any): boolean; - /** * Returns focused element. */ @@ -321,26 +206,6 @@ export interface ITree { */ clearFocus(eventPayload?: any): void; - /** - * Adds the trait to elements. - */ - addTraits(trait: string, elements: any[]): void; - - /** - * Removes the trait from elements. - */ - removeTraits(trait: string, elements: any[]): void; - - /** - * Toggles the element's trait. - */ - toggleTrait(trait: string, element: any): void; - - /** - * Returns whether the element has the trait or not. - */ - hasTrait(trait: string, element: any): boolean; - /** * Returns a navigator which allows to discover the visible and * expanded elements in the tree. @@ -378,12 +243,12 @@ export interface IDataSource { /** * Returns the element's children as an array in a promise. */ - getChildren(tree: ITree, element: any): WinJS.Promise; + getChildren(tree: ITree, element: any): Promise; /** * Returns the element's parent in a promise. */ - getParent(tree: ITree, element: any): WinJS.Promise; + getParent(tree: ITree, element: any): Promise; /** * Returns whether an element should be expanded when first added to the tree. @@ -443,7 +308,7 @@ export interface IAccessibilityProvider { * * See also: https://www.w3.org/TR/wai-aria/states_and_properties#aria-label */ - getAriaLabel(tree: ITree, element: any): string; + getAriaLabel(tree: ITree, element: any): string | null; /** * Given an element in the tree return its aria-posinset. Should be between 1 and aria-setsize @@ -587,25 +452,13 @@ export interface IDragOverReaction { autoExpand?: boolean; } -export const DRAG_OVER_REJECT: IDragOverReaction = { accept: false }; -export const DRAG_OVER_ACCEPT: IDragOverReaction = { accept: true }; -export const DRAG_OVER_ACCEPT_BUBBLE_UP: IDragOverReaction = { accept: true, bubble: DragOverBubble.BUBBLE_UP }; -export const DRAG_OVER_ACCEPT_BUBBLE_DOWN = (autoExpand = false) => ({ accept: true, bubble: DragOverBubble.BUBBLE_DOWN, autoExpand }); -export const DRAG_OVER_ACCEPT_BUBBLE_UP_COPY: IDragOverReaction = { accept: true, bubble: DragOverBubble.BUBBLE_UP, effect: DragOverEffect.COPY }; -export const DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY = (autoExpand = false) => ({ accept: true, bubble: DragOverBubble.BUBBLE_DOWN, effect: DragOverEffect.COPY, autoExpand }); - -export interface IDragAndDropData { - update(event: Mouse.DragMouseEvent): void; - getData(): any; -} - export interface IDragAndDrop { /** * Returns a uri if the given element should be allowed to drag. * Returns null, otherwise. */ - getDragURI(tree: ITree, element: any): string; + getDragURI(tree: ITree, element: any): string | null; /** * Returns a label to display when dragging the element. @@ -621,7 +474,7 @@ export interface IDragAndDrop { * Returns a DragOverReaction indicating whether sources can be * dropped into target or some parent of the target. */ - onDragOver(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: Mouse.DragMouseEvent): IDragOverReaction; + onDragOver(tree: ITree, data: IDragAndDropData, targetElement: any, originalEvent: Mouse.DragMouseEvent): IDragOverReaction | null; /** * Handles the action of dropping sources into target. @@ -637,12 +490,6 @@ export interface IFilter { isVisible(tree: ITree, element: any): boolean; } -export interface IElementCallback { - (tree: ITree, element: any): void; -} - -export type ICallback = () => void; - export interface ISorter { /** @@ -726,25 +573,10 @@ export interface IActionProvider { /** * Returns whether or not the element has actions. These show up in place right to the element in the tree. */ - hasActions(tree: ITree, element: any): boolean; + hasActions(tree: ITree | null, element: any): boolean; /** * Returns a promise of an array with the actions of the element that should show up in place right to the element in the tree. */ - getActions(tree: ITree, element: any): IAction[]; - - /** - * Returns whether or not the element has secondary actions. These show up once the user has expanded the element's action bar. - */ - hasSecondaryActions(tree: ITree, element: any): boolean; - - /** - * Returns a promise of an array with the secondary actions of the element that should show up once the user has expanded the element's action bar. - */ - getSecondaryActions(tree: ITree, element: any): IAction[]; - - /** - * Returns an action item to render an action. - */ - getActionItem(tree: ITree, element: any, action: IAction): IActionItem; + getActions(tree: ITree | null, element: any): IAction[] | null; } diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts index 80723066e4b..f91ca2bcf84 100644 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ b/src/vs/base/parts/tree/browser/treeDefaults.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import * as platform from 'vs/base/common/platform'; import * as touch from 'vs/base/browser/touch'; @@ -13,7 +12,8 @@ import * as dom from 'vs/base/browser/dom'; import * as mouse from 'vs/base/browser/mouseEvent'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as _ from 'vs/base/parts/tree/browser/tree'; -import { KeyCode, KeyMod, Keybinding, createKeybinding, SimpleKeybinding, createSimpleKeybinding } from 'vs/base/common/keyCodes'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { KeyCode, KeyMod, Keybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; export interface IKeyBindingCallback { (tree: _.ITree, event: IKeyboardEvent): void; @@ -49,7 +49,7 @@ export interface IControllerOptions { } interface IKeybindingDispatcherItem { - keybinding: Keybinding; + keybinding: Keybinding | null; callback: IKeyBindingCallback; } @@ -62,27 +62,29 @@ export class KeybindingDispatcher { } public has(keybinding: KeyCode): boolean { - let target = createSimpleKeybinding(keybinding, platform.OS); - for (const a of this._arr) { - if (target.equals(a.keybinding)) { - return true; + let target = createKeybinding(keybinding, platform.OS); + if (target !== null) { + for (const a of this._arr) { + if (target.equals(a.keybinding)) { + return true; + } } } return false; } - public set(keybinding: KeyCode, callback: IKeyBindingCallback) { + public set(keybinding: number, callback: IKeyBindingCallback) { this._arr.push({ keybinding: createKeybinding(keybinding, platform.OS), callback: callback }); } - public dispatch(keybinding: SimpleKeybinding): IKeyBindingCallback { + public dispatch(keybinding: SimpleKeybinding): IKeyBindingCallback | null { // Loop from the last to the first to handle overwrites for (let i = this._arr.length - 1; i >= 0; i--) { let item = this._arr[i]; - if (keybinding.equals(item.keybinding)) { + if (keybinding.toChord().equals(item.keybinding)) { return item.callback; } } @@ -190,9 +192,9 @@ export class DefaultController implements _.IController { if (this.shouldToggleExpansion(element, event, origin)) { if (tree.isExpanded(element)) { - tree.collapse(element).then(null, errors.onUnexpectedError); + tree.collapse(element).then(undefined, errors.onUnexpectedError); } else { - tree.expand(element).then(null, errors.onUnexpectedError); + tree.expand(element).then(undefined, errors.onUnexpectedError); } } } @@ -226,7 +228,7 @@ export class DefaultController implements _.IController { return false; } - const twistieWidth = parseInt(twistieStyle.width) + parseInt(twistieStyle.paddingRight); + const twistieWidth = parseInt(twistieStyle.width!) + parseInt(twistieStyle.paddingRight!); return event.browserEvent.offsetX <= twistieWidth; } @@ -282,7 +284,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusPrevious(1, payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -294,7 +296,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusPreviousPage(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -306,7 +308,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusNext(1, payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -318,7 +320,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusNextPage(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -330,7 +332,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusFirst(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -342,7 +344,7 @@ export class DefaultController implements _.IController { tree.clearHighlight(payload); } else { tree.focusLast(payload); - tree.reveal(tree.getFocus()).then(null, errors.onUnexpectedError); + tree.reveal(tree.getFocus()).then(undefined, errors.onUnexpectedError); } return true; } @@ -360,7 +362,7 @@ export class DefaultController implements _.IController { return tree.reveal(tree.getFocus()); } return undefined; - }).then(null, errors.onUnexpectedError); + }).then(undefined, errors.onUnexpectedError); } return true; } @@ -378,7 +380,7 @@ export class DefaultController implements _.IController { return tree.reveal(tree.getFocus()); } return undefined; - }).then(null, errors.onUnexpectedError); + }).then(undefined, errors.onUnexpectedError); } return true; } @@ -431,19 +433,19 @@ export class DefaultController implements _.IController { export class DefaultDragAndDrop implements _.IDragAndDrop { - public getDragURI(tree: _.ITree, element: any): string { + public getDragURI(tree: _.ITree, element: any): string | null { return null; } - public onDragStart(tree: _.ITree, data: _.IDragAndDropData, originalEvent: mouse.DragMouseEvent): void { + public onDragStart(tree: _.ITree, data: IDragAndDropData, originalEvent: mouse.DragMouseEvent): void { return; } - public onDragOver(tree: _.ITree, data: _.IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): _.IDragOverReaction { + public onDragOver(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): _.IDragOverReaction | null { return null; } - public drop(tree: _.ITree, data: _.IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): void { + public drop(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): void { return; } } @@ -464,7 +466,7 @@ export class DefaultSorter implements _.ISorter { export class DefaultAccessibilityProvider implements _.IAccessibilityProvider { - getAriaLabel(tree: _.ITree, element: any): string { + getAriaLabel(tree: _.ITree, element: any): string | null { return null; } } @@ -553,12 +555,12 @@ export class DefaultTreestyler implements _.ITreeStyler { export class CollapseAllAction extends Action { constructor(private viewer: _.ITree, enabled: boolean) { - super('vs.tree.collapse', nls.localize('collapse', "Collapse"), 'monaco-tree-action collapse-all', enabled); + super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled); } - public run(context?: any): TPromise { + public run(context?: any): Promise { if (this.viewer.getHighlight()) { - return TPromise.as(null); // Global action disabled if user is in edit mode from another action + return Promise.resolve(); // Global action disabled if user is in edit mode from another action } this.viewer.collapseAll(); @@ -567,6 +569,6 @@ export class CollapseAllAction extends Action { this.viewer.domFocus(); this.viewer.focusFirst(); - return TPromise.as(null); + return Promise.resolve(); } } diff --git a/src/vs/base/parts/tree/browser/treeDnd.ts b/src/vs/base/parts/tree/browser/treeDnd.ts index 5ba6467296b..810e6ce98ee 100644 --- a/src/vs/base/parts/tree/browser/treeDnd.ts +++ b/src/vs/base/parts/tree/browser/treeDnd.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as _ from 'vs/base/parts/tree/browser/tree'; -import * as Mouse from 'vs/base/browser/mouseEvent'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; -export class ElementsDragAndDropData implements _.IDragAndDropData { +export class ElementsDragAndDropData implements IDragAndDropData { private elements: any[]; @@ -14,7 +14,7 @@ export class ElementsDragAndDropData implements _.IDragAndDropData { this.elements = elements; } - public update(event: Mouse.DragMouseEvent): void { + public update(dataTransfer: DataTransfer): void { // no-op } @@ -23,7 +23,7 @@ export class ElementsDragAndDropData implements _.IDragAndDropData { } } -export class ExternalElementsDragAndDropData implements _.IDragAndDropData { +export class ExternalElementsDragAndDropData implements IDragAndDropData { private elements: any[]; @@ -31,7 +31,7 @@ export class ExternalElementsDragAndDropData implements _.IDragAndDropData { this.elements = elements; } - public update(event: Mouse.DragMouseEvent): void { + public update(dataTransfer: DataTransfer): void { // no-op } @@ -40,7 +40,7 @@ export class ExternalElementsDragAndDropData implements _.IDragAndDropData { } } -export class DesktopDragAndDropData implements _.IDragAndDropData { +export class DesktopDragAndDropData implements IDragAndDropData { private types: any[]; private files: any[]; @@ -50,15 +50,15 @@ export class DesktopDragAndDropData implements _.IDragAndDropData { this.files = []; } - public update(event: Mouse.DragMouseEvent): void { - if (event.dataTransfer.types) { + public update(dataTransfer: DataTransfer): void { + if (dataTransfer.types) { this.types = []; - Array.prototype.push.apply(this.types, event.dataTransfer.types); + Array.prototype.push.apply(this.types, dataTransfer.types as any); } - if (event.dataTransfer.files) { + if (dataTransfer.files) { this.files = []; - Array.prototype.push.apply(this.files, event.dataTransfer.files); + Array.prototype.push.apply(this.files, dataTransfer.files as any); this.files = this.files.filter(f => f.size || f.type); } diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index 63f217de9ec..3f30ea46037 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./tree'; -import * as WinJS from 'vs/base/common/winjs.base'; import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; import * as Model from 'vs/base/parts/tree/browser/treeModel'; import * as View from './treeView'; @@ -21,13 +20,13 @@ export class TreeContext implements _.ITreeContext { public options: _.ITreeOptions; public dataSource: _.IDataSource; - public renderer: _.IRenderer; + public renderer?: _.IRenderer; public controller: _.IController; public dnd: _.IDragAndDrop; public filter: _.IFilter; - public sorter: _.ISorter; + public sorter?: _.ISorter; public accessibilityProvider: _.IAccessibilityProvider; - public styler: _.ITreeStyler; + public styler?: _.ITreeStyler; constructor(tree: _.ITree, configuration: _.ITreeConfiguration, options: _.ITreeOptions = {}) { this.tree = tree; @@ -43,9 +42,9 @@ export class TreeContext implements _.ITreeContext { this.controller = configuration.controller || new TreeDefaults.DefaultController({ clickBehavior: TreeDefaults.ClickBehavior.ON_MOUSE_UP, keyboardSupport: typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport }); this.dnd = configuration.dnd || new TreeDefaults.DefaultDragAndDrop(); this.filter = configuration.filter || new TreeDefaults.DefaultFilter(); - this.sorter = configuration.sorter || null; + this.sorter = configuration.sorter; this.accessibilityProvider = configuration.accessibilityProvider || new TreeDefaults.DefaultAccessibilityProvider(); - this.styler = configuration.styler || null; + this.styler = configuration.styler; } } @@ -150,7 +149,7 @@ export class Tree implements _.ITree { this.view.onHidden(); } - public setInput(element: any): WinJS.Promise { + public setInput(element: any): Promise { return this.model.setInput(element); } @@ -158,128 +157,50 @@ export class Tree implements _.ITree { return this.model.getInput(); } - public refresh(element: any = null, recursive = true): WinJS.Promise { + public refresh(element: any = null, recursive = true): Promise { return this.model.refresh(element, recursive); } - public updateWidth(element: any): void { - let item = this.model.getItem(element); - return this.view.updateWidth(item); - } - - public expand(element: any): WinJS.Promise { + public expand(element: any): Promise { return this.model.expand(element); } - public expandAll(elements: any[]): WinJS.Promise { + public expandAll(elements: any[]): Promise { return this.model.expandAll(elements); } - public collapse(element: any, recursive: boolean = false): WinJS.Promise { + public collapse(element: any, recursive: boolean = false): Promise { return this.model.collapse(element, recursive); } - public collapseAll(elements: any[] | null = null, recursive: boolean = false): WinJS.Promise { + public collapseAll(elements: any[] | null = null, recursive: boolean = false): Promise { return this.model.collapseAll(elements, recursive); } - public toggleExpansion(element: any, recursive: boolean = false): WinJS.Promise { + public toggleExpansion(element: any, recursive: boolean = false): Promise { return this.model.toggleExpansion(element, recursive); } - public toggleExpansionAll(elements: any[]): WinJS.Promise { - return this.model.toggleExpansionAll(elements); - } - public isExpanded(element: any): boolean { return this.model.isExpanded(element); } - public getExpandedElements(): any[] { - return this.model.getExpandedElements(); - } - - public reveal(element: any, relativeTop: number | null = null): WinJS.Promise { + public reveal(element: any, relativeTop: number | null = null): Promise { return this.model.reveal(element, relativeTop); } - public getRelativeTop(element: any): number { - let item = this.model.getItem(element); - return this.view.getRelativeTop(item); - } - - public getFirstVisibleElement(): any { - return this.view.getFirstVisibleElement(); - } - - public getLastVisibleElement(): any { - return this.view.getLastVisibleElement(); - } - - public getScrollPosition(): number { - return this.view.getScrollPosition(); - } - - public setScrollPosition(pos: number): void { - this.view.setScrollPosition(pos); - } - - getContentHeight(): number { - return this.view.getContentHeight(); - } - - public setHighlight(element?: any, eventPayload?: any): void { - this.model.setHighlight(element, eventPayload); - } - public getHighlight(): any { return this.model.getHighlight(); } - public isHighlighted(element: any): boolean { - return this.model.isFocused(element); - } - public clearHighlight(eventPayload?: any): void { this.model.setHighlight(null, eventPayload); } - public select(element: any, eventPayload?: any): void { - this.model.select(element, eventPayload); - } - - public selectRange(fromElement: any, toElement: any, eventPayload?: any): void { - this.model.selectRange(fromElement, toElement, eventPayload); - } - - public deselectRange(fromElement: any, toElement: any, eventPayload?: any): void { - this.model.deselectRange(fromElement, toElement, eventPayload); - } - - public selectAll(elements: any[], eventPayload?: any): void { - this.model.selectAll(elements, eventPayload); - } - - public deselect(element: any, eventPayload?: any): void { - this.model.deselect(element, eventPayload); - } - - public deselectAll(elements: any[], eventPayload?: any): void { - this.model.deselectAll(elements, eventPayload); - } - public setSelection(elements: any[], eventPayload?: any): void { this.model.setSelection(elements, eventPayload); } - public toggleSelection(element: any, eventPayload?: any): void { - this.model.toggleSelection(element, eventPayload); - } - - public isSelected(element: any): boolean { - return this.model.isSelected(element); - } - public getSelection(): any[] { return this.model.getSelection(); } @@ -288,26 +209,10 @@ export class Tree implements _.ITree { this.model.setSelection([], eventPayload); } - public selectNext(count?: number, clearSelection?: boolean, eventPayload?: any): void { - this.model.selectNext(count, clearSelection, eventPayload); - } - - public selectPrevious(count?: number, clearSelection?: boolean, eventPayload?: any): void { - this.model.selectPrevious(count, clearSelection, eventPayload); - } - - public selectParent(clearSelection?: boolean, eventPayload?: any): void { - this.model.selectParent(clearSelection, eventPayload); - } - public setFocus(element?: any, eventPayload?: any): void { this.model.setFocus(element, eventPayload); } - public isFocused(element: any): boolean { - return this.model.isFocused(element); - } - public getFocus(): any { return this.model.getFocus(); } @@ -352,23 +257,6 @@ export class Tree implements _.ITree { this.model.setFocus(null, eventPayload); } - public addTraits(trait: string, elements: any[]): void { - this.model.addTraits(trait, elements); - } - - public removeTraits(trait: string, elements: any[]): void { - this.model.removeTraits(trait, elements); - } - - public toggleTrait(trait: string, element: any): void { - this.model.hasTrait(trait, element) ? this.model.removeTraits(trait, [element]) - : this.model.addTraits(trait, [element]); - } - - public hasTrait(trait: string, element: any): boolean { - return this.model.hasTrait(trait, element); - } - getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator { return new MappedNavigator(this.model.getNavigator(fromElement, subTreeOnly), i => i && i.getElement()); } @@ -378,11 +266,11 @@ export class Tree implements _.ITree { if (this.model !== null) { this.model.dispose(); - this.model = null; + this.model = null!; // StrictNullOverride Nulling out ok in dispose } if (this.view !== null) { this.view.dispose(); - this.view = null; + this.view = null!; // StrictNullOverride Nulling out ok in dispose } this._onDidChangeFocus.dispose(); diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts index ecfbe852b10..1ddbe4cb462 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -6,10 +6,9 @@ import * as Assert from 'vs/base/common/assert'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import * as arrays from 'vs/base/common/arrays'; import { INavigator } from 'vs/base/common/iterator'; import * as _ from './tree'; -import { Event, Emitter, once, EventMultiplexer, Relay } from 'vs/base/common/event'; +import { Event, Emitter, EventMultiplexer, Relay } from 'vs/base/common/event'; interface IMap { [id: string]: T; } interface IItemMap extends IMap { } @@ -18,8 +17,8 @@ interface ITraitMap extends IMap { } export class LockData { private _item: Item; - private _onDispose = new Emitter(); - readonly onDispose: Event = this._onDispose.event; + private _onDispose?= new Emitter(); + readonly onDispose: Event = this._onDispose!.event; constructor(item: Item) { this._item = item; @@ -33,7 +32,7 @@ export class LockData { if (this._onDispose) { this._onDispose.fire(); this._onDispose.dispose(); - this._onDispose = null; + this._onDispose = undefined; } } } @@ -78,18 +77,18 @@ export class Lock { return !!this.locks[item.id]; } - public run(item: Item, fn: () => Thenable): Thenable { - var lock = this.getLock(item); + public run(item: Item, fn: () => Promise): Promise { + const lock = this.getLock(item); if (lock) { return new Promise((c, e) => { - once(lock.onDispose)(() => { + Event.once(lock.onDispose)(() => { return this.run(item, fn).then(c, e); }); }); } - var result: Thenable; + let result: Promise; return new Promise((c, e) => { @@ -97,7 +96,7 @@ export class Lock { return e(new Error('Item is disposed.')); } - var lock = this.locks[item.id] = new LockData(item); + let lock = this.locks[item.id] = new LockData(item); result = fn().then((r) => { delete this.locks[item.id]; @@ -110,11 +109,11 @@ export class Lock { }); } - private getLock(item: Item): LockData { - var key: string; + private getLock(item: Item): LockData | null { + let key: string; for (key in this.locks) { - var lock = this.locks[key]; + let lock = this.locks[key]; if (item.intersects(lock.item)) { return lock; @@ -187,13 +186,13 @@ export class ItemRegistry { return this.items.hasOwnProperty(id); } - public getItem(id: string): Item { + public getItem(id: string): Item | null { const result = this.items[id]; return result ? result.item : null; } public dispose(): void { - this.items = null; + this.items = null!; // StrictNullOverride: nulling out ok in dispose this._onDidRevealItem.dispose(); this._onExpandItem.dispose(); @@ -227,7 +226,7 @@ export interface IItemTraitEvent extends IBaseItemEvent { } export interface IItemRevealEvent extends IBaseItemEvent { - relativeTop: number; + relativeTop: number | null; } export interface IItemChildrenRefreshEvent extends IBaseItemEvent { @@ -246,11 +245,11 @@ export class Item { private needsChildrenRefresh: boolean; private doesHaveChildren: boolean; - public parent: Item; - public previous: Item; - public next: Item; - public firstChild: Item; - public lastChild: Item; + public parent: Item | null; + public previous: Item | null; + public next: Item | null; + public firstChild: Item | null; + public lastChild: Item | null; private height: number; private depth: number; @@ -307,7 +306,7 @@ export class Item { this.traits = {}; this.depth = 0; - this.expanded = this.context.dataSource.shouldAutoexpand && this.context.dataSource.shouldAutoexpand(this.context.tree, element); + this.expanded = !!(this.context.dataSource.shouldAutoexpand && this.context.dataSource.shouldAutoexpand(this.context.tree, element)); this._onDidCreate.fire(this); @@ -346,22 +345,22 @@ export class Item { } public reveal(relativeTop: number | null = null): void { - var eventData: IItemRevealEvent = { item: this, relativeTop: relativeTop }; + let eventData: IItemRevealEvent = { item: this, relativeTop: relativeTop }; this._onDidReveal.fire(eventData); } - public expand(): Thenable { + public expand(): Promise { if (this.isExpanded() || !this.doesHaveChildren || this.lock.isLocked(this)) { return Promise.resolve(false); } - var result = this.lock.run(this, () => { + let result = this.lock.run(this, () => { if (this.isExpanded() || !this.doesHaveChildren) { return Promise.resolve(false); } - var eventData: IItemExpandEvent = { item: this }; - var result: Thenable; + let eventData: IItemExpandEvent = { item: this }; + let result: Promise; this._onExpand.fire(eventData); if (this.needsChildrenRefresh) { @@ -391,9 +390,9 @@ export class Item { }); } - public collapse(recursive: boolean = false): Thenable { + public collapse(recursive: boolean = false): Promise { if (recursive) { - var collapseChildrenPromise = Promise.resolve(null); + let collapseChildrenPromise = Promise.resolve(null); this.forEachChild((child) => { collapseChildrenPromise = collapseChildrenPromise.then(() => child.collapse(true)); }); @@ -406,7 +405,7 @@ export class Item { } return this.lock.run(this, () => { - var eventData: IItemCollapseEvent = { item: this }; + let eventData: IItemCollapseEvent = { item: this }; this._onCollapse.fire(eventData); this._setExpanded(false); this._onDidCollapse.fire(eventData); @@ -417,13 +416,13 @@ export class Item { } public addTrait(trait: string): void { - var eventData: IItemTraitEvent = { item: this, trait: trait }; + let eventData: IItemTraitEvent = { item: this, trait: trait }; this.traits[trait] = true; this._onDidAddTrait.fire(eventData); } public removeTrait(trait: string): void { - var eventData: IItemTraitEvent = { item: this, trait: trait }; + let eventData: IItemTraitEvent = { item: this, trait: trait }; delete this.traits[trait]; this._onDidRemoveTrait.fire(eventData); } @@ -433,8 +432,8 @@ export class Item { } public getAllTraits(): string[] { - var result: string[] = []; - var trait: string; + let result: string[] = []; + let trait: string; for (trait in this.traits) { if (this.traits.hasOwnProperty(trait) && this.traits[trait]) { result.push(trait); @@ -447,7 +446,7 @@ export class Item { return this.height; } - private refreshChildren(recursive: boolean, safe: boolean = false, force: boolean = false): Thenable { + private refreshChildren(recursive: boolean, safe: boolean = false, force: boolean = false): Promise { if (!force && !this.isExpanded()) { const setNeedsChildrenRefresh = (item: Item) => { item.needsChildrenRefresh = true; @@ -461,11 +460,11 @@ export class Item { this.needsChildrenRefresh = false; - var doRefresh = () => { - var eventData: IItemChildrenRefreshEvent = { item: this, isNested: safe }; + let doRefresh = () => { + let eventData: IItemChildrenRefreshEvent = { item: this, isNested: safe }; this._onRefreshChildren.fire(eventData); - var childrenPromise: Thenable; + let childrenPromise: Promise; if (this.doesHaveChildren) { childrenPromise = this.context.dataSource.getChildren(this.context.tree, this.element); } else { @@ -484,16 +483,16 @@ export class Item { elements = !elements ? [] : elements.slice(0); elements = this.sort(elements); - var staleItems: IItemMap = {}; + let staleItems: IItemMap = {}; while (this.firstChild !== null) { staleItems[this.firstChild.id] = this.firstChild; this.removeChild(this.firstChild); } - for (var i = 0, len = elements.length; i < len; i++) { - var element = elements[i]; - var id = this.context.dataSource.getId(this.context.tree, element); - var item = staleItems[id] || new Item(id, this.registry, this.context, this.lock, element); + for (let i = 0, len = elements.length; i < len; i++) { + let element = elements[i]; + let id = this.context.dataSource.getId(this.context.tree, element); + let item = staleItems[id] || new Item(id, this.registry, this.context, this.lock, element); item.element = element; if (recursive) { item.needsChildrenRefresh = recursive; @@ -502,7 +501,7 @@ export class Item { this.addChild(item); } - for (var staleItemId in staleItems) { + for (let staleItemId in staleItems) { if (staleItems.hasOwnProperty(staleItemId)) { staleItems[staleItemId].dispose(); } @@ -525,14 +524,14 @@ export class Item { }); return result - .then(null, onUnexpectedError) + .then(undefined, onUnexpectedError) .then(() => this._onDidRefreshChildren.fire(eventData)); }; return safe ? doRefresh() : this.lock.run(this, doRefresh); } - private doRefresh(recursive: boolean, safe: boolean = false): Thenable { + private doRefresh(recursive: boolean, safe: boolean = false): Promise { this.doesHaveChildren = this.context.dataSource.hasChildren(this.context.tree, this.element); this.height = this._getHeight(); this.updateVisibility(); @@ -546,7 +545,7 @@ export class Item { this.setVisible(this._isVisible()); } - public refresh(recursive: boolean): Thenable { + public refresh(recursive: boolean): Promise { return this.doRefresh(recursive); } @@ -558,20 +557,8 @@ export class Item { return this.isAncestorOf(other) || other.isAncestorOf(this); } - public getHierarchy(): Item[] { - var result: Item[] = []; - var node: Item = this; - - do { - result.push(node); - node = node.parent; - } while (node); - - result.reverse(); - return result; - } - - private isAncestorOf(item: Item): boolean { + private isAncestorOf(startItem: Item): boolean { + let item: Item | null = startItem; while (item) { if (item.id === this.id) { return true; @@ -581,27 +568,39 @@ export class Item { return false; } - private addChild(item: Item, afterItem: Item = this.lastChild): void { - var isEmpty = this.firstChild === null; - var atHead = afterItem === null; - var atTail = afterItem === this.lastChild; + private addChild(item: Item, afterItem: Item | null = this.lastChild): void { + let isEmpty = this.firstChild === null; + let atHead = afterItem === null; + let atTail = afterItem === this.lastChild; if (isEmpty) { this.firstChild = this.lastChild = item; item.next = item.previous = null; } else if (atHead) { + if (!this.firstChild) { + throw new Error('Invalid tree state'); + } this.firstChild.previous = item; item.next = this.firstChild; item.previous = null; this.firstChild = item; } else if (atTail) { + if (!this.lastChild) { + throw new Error('Invalid tree state'); + } this.lastChild.next = item; item.next = null; item.previous = this.lastChild; this.lastChild = item; } else { item.previous = afterItem; + if (!afterItem) { + throw new Error('Invalid tree state'); + } item.next = afterItem.next; + if (!afterItem.next) { + throw new Error('Invalid tree state'); + } afterItem.next.previous = item; afterItem.next = item; } @@ -611,28 +610,41 @@ export class Item { } private removeChild(item: Item): void { - var isFirstChild = this.firstChild === item; - var isLastChild = this.lastChild === item; + let isFirstChild = this.firstChild === item; + let isLastChild = this.lastChild === item; if (isFirstChild && isLastChild) { this.firstChild = this.lastChild = null; } else if (isFirstChild) { + if (!item.next) { + throw new Error('Invalid tree state'); + } item.next.previous = null; this.firstChild = item.next; } else if (isLastChild) { + if (!item.previous) { + throw new Error('Invalid tree state'); + } item.previous.next = null; this.lastChild = item.previous; } else { + if (!item.next) { + throw new Error('Invalid tree state'); + } item.next.previous = item.previous; + if (!item.previous) { + throw new Error('Invalid tree state'); + } item.previous.next = item.next; } item.parent = null; - item.depth = null; + item.depth = NaN; } private forEachChild(fn: (child: Item) => void): void { - var child = this.firstChild, next: Item; + let child = this.firstChild; + let next: Item | null; while (child) { next = child.next; fn(child); @@ -641,7 +653,7 @@ export class Item { } private mapEachChild(fn: (child: Item) => T): T[] { - var result: T[] = []; + let result: T[] = []; this.forEachChild((child) => { result.push(fn(child)); }); @@ -649,9 +661,10 @@ export class Item { } private sort(elements: any[]): any[] { - if (this.context.sorter) { + const sorter = this.context.sorter; + if (sorter) { return elements.sort((element, otherElement) => { - return this.context.sorter.compare(this.context.tree, element, otherElement); + return sorter.compare(this.context.tree, element, otherElement); }); } @@ -659,10 +672,16 @@ export class Item { } /* protected */ public _getHeight(): number { + if (!this.context.renderer) { + return 0; + } return this.context.renderer.getHeight(this.context.tree, this.element); } /* protected */ public _isVisible(): boolean { + if (!this.context.filter) { + return false; + } return this.context.filter.isVisible(this.context.tree, this.element); } @@ -737,10 +756,10 @@ class RootItem extends Item { export class TreeNavigator implements INavigator { - private start: Item; - private item: Item; + private start: Item | null; + private item: Item | null; - static lastDescendantOf(item: Item): Item { + static lastDescendantOf(item: Item | null): Item | null { if (!item) { return null; } @@ -760,16 +779,16 @@ export class TreeNavigator implements INavigator { return TreeNavigator.lastDescendantOf(item.lastChild); } - constructor(item: Item, subTreeOnly: boolean = true) { + constructor(item: Item | null, subTreeOnly: boolean = true) { this.item = item; this.start = subTreeOnly ? item : null; } - public current(): Item { + public current(): Item | null { return this.item || null; } - public next(): Item { + public next(): Item | null { if (this.item) { do { if ((this.item instanceof RootItem || (this.item.isVisible() && this.item.isExpanded())) && this.item.firstChild) { @@ -791,10 +810,10 @@ export class TreeNavigator implements INavigator { return this.item || null; } - public previous(): Item { + public previous(): Item | null { if (this.item) { do { - var previous = TreeNavigator.lastDescendantOf(this.item.previous); + let previous = TreeNavigator.lastDescendantOf(this.item.previous); if (previous) { this.item = previous; } else if (this.item.parent && this.item.parent !== this.start && this.item.parent.isVisible()) { @@ -807,9 +826,9 @@ export class TreeNavigator implements INavigator { return this.item || null; } - public parent(): Item { + public parent(): Item | null { if (this.item) { - var parent = this.item.parent; + let parent = this.item.parent; if (parent && parent !== this.start && parent.isVisible()) { this.item = parent; } else { @@ -819,55 +838,19 @@ export class TreeNavigator implements INavigator { return this.item || null; } - public first(): Item { + public first(): Item | null { this.item = this.start; this.next(); return this.item || null; } - public last(): Item { + public last(): Item | null { return TreeNavigator.lastDescendantOf(this.start); } } -function getRange(one: Item, other: Item): Item[] { - var oneHierarchy = one.getHierarchy(); - var otherHierarchy = other.getHierarchy(); - var length = arrays.commonPrefixLength(oneHierarchy, otherHierarchy); - var item = oneHierarchy[length - 1]; - var nav = item.getNavigator(); - - var oneIndex: number | null = null; - var otherIndex: number | null = null; - - var index = 0; - var result: Item[] = []; - - while (item && (oneIndex === null || otherIndex === null)) { - result.push(item); - - if (item === one) { - oneIndex = index; - } - if (item === other) { - otherIndex = index; - } - - index++; - item = nav.next(); - } - - if (oneIndex === null || otherIndex === null) { - return []; - } - - var min = Math.min(oneIndex, otherIndex); - var max = Math.max(oneIndex, otherIndex); - return result.slice(min, max + 1); -} - export interface IBaseEvent { - item: Item; + item: Item | null; } export interface IInputEvent extends IBaseEvent { } @@ -880,7 +863,7 @@ export class TreeModel { private context: _.ITreeContext; private lock: Lock; - private input: Item; + private input: Item | null; private registry: ItemRegistry; private registryDisposable: IDisposable; private traitsToItems: ITraitMap; @@ -929,8 +912,8 @@ export class TreeModel { this.traitsToItems = {}; } - public setInput(element: any): Thenable { - var eventData: IInputEvent = { item: this.input }; + public setInput(element: any): Promise { + let eventData: IInputEvent = { item: this.input }; this._onSetInput.fire(eventData); this.setSelection([]); @@ -965,7 +948,7 @@ export class TreeModel { this.registryDisposable = this.registry .onDidDisposeItem(item => item.getAllTraits().forEach(trait => delete this.traitsToItems[trait][item.id])); - var id = this.context.dataSource.getId(this.context.tree, element); + let id = this.context.dataSource.getId(this.context.tree, element); this.input = new RootItem(id, this.registry, this.context, this.lock, element); eventData = { item: this.input }; this._onDidSetInput.fire(eventData); @@ -976,22 +959,22 @@ export class TreeModel { return this.input ? this.input.getElement() : null; } - public refresh(element: any = null, recursive: boolean = true): Thenable { - var item = this.getItem(element); + public refresh(element: any = null, recursive: boolean = true): Promise { + let item = this.getItem(element); if (!item) { return Promise.resolve(null); } - var eventData: IRefreshEvent = { item: item, recursive: recursive }; + let eventData: IRefreshEvent = { item: item, recursive: recursive }; this._onRefresh.fire(eventData); return item.refresh(recursive).then(() => { this._onDidRefresh.fire(eventData); }); } - public expand(element: any): Thenable { - var item = this.getItem(element); + public expand(element: any): Promise { + let item = this.getItem(element); if (!item) { return Promise.resolve(false); @@ -1000,12 +983,12 @@ export class TreeModel { return item.expand(); } - public expandAll(elements?: any[]): Thenable { + public expandAll(elements?: any[]): Promise { if (!elements) { elements = []; - var item: Item; - var nav = this.getNavigator(); + let item: Item | null; + let nav = this.getNavigator(); while (item = nav.next()) { elements.push(item); @@ -1015,7 +998,7 @@ export class TreeModel { return this._expandAll(elements); } - private _expandAll(elements: any[]): Thenable { + private _expandAll(elements: any[]): Promise { if (elements.length === 0) { return Promise.resolve(null); } @@ -1024,7 +1007,7 @@ export class TreeModel { const elementsToDelay: any[] = []; for (const element of elements) { - var item = this.getItem(element); + let item = this.getItem(element); if (item) { elementsToExpand.push(element); @@ -1041,16 +1024,16 @@ export class TreeModel { .then(() => this._expandAll(elementsToDelay)); } - private __expandAll(elements: any[]): Thenable { - var promises = []; - for (var i = 0, len = elements.length; i < len; i++) { + private __expandAll(elements: any[]): Promise { + const promises: Array> = []; + for (let i = 0, len = elements.length; i < len; i++) { promises.push(this.expand(elements[i])); } return Promise.all(promises); } - public collapse(element: any, recursive: boolean = false): Thenable { - var item = this.getItem(element); + public collapse(element: any, recursive: boolean = false): Promise { + const item = this.getItem(element); if (!item) { return Promise.resolve(false); @@ -1059,32 +1042,32 @@ export class TreeModel { return item.collapse(recursive); } - public collapseAll(elements: any[] | null = null, recursive: boolean = false): Thenable { + public collapseAll(elements: any[] | null = null, recursive: boolean = false): Promise { if (!elements) { elements = [this.input]; recursive = true; } - var promises = []; - for (var i = 0, len = elements.length; i < len; i++) { + let promises: Array> = []; + for (let i = 0, len = elements.length; i < len; i++) { promises.push(this.collapse(elements[i], recursive)); } return Promise.all(promises); } - public toggleExpansion(element: any, recursive: boolean = false): Thenable { + public toggleExpansion(element: any, recursive: boolean = false): Promise { return this.isExpanded(element) ? this.collapse(element, recursive) : this.expand(element); } - public toggleExpansionAll(elements: any[]): Thenable { - var promises = []; - for (var i = 0, len = elements.length; i < len; i++) { + public toggleExpansionAll(elements: any[]): Promise { + let promises: Array> = []; + for (let i = 0, len = elements.length; i < len; i++) { promises.push(this.toggleExpansion(elements[i])); } return Promise.all(promises); } public isExpanded(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1094,9 +1077,9 @@ export class TreeModel { } public getExpandedElements(): any[] { - var result: any[] = []; - var item: Item; - var nav = this.getNavigator(); + let result: any[] = []; + let item: Item | null; + let nav = this.getNavigator(); while (item = nav.next()) { if (item.isExpanded()) { @@ -1107,9 +1090,9 @@ export class TreeModel { return result; } - public reveal(element: any, relativeTop: number | null = null): Thenable { + public reveal(element: any, relativeTop: number | null = null): Promise { return this.resolveUnknownParentChain(element).then((chain: any[]) => { - var result = Promise.resolve(null); + let result = Promise.resolve(null); chain.forEach((e) => { result = result.then(() => this.expand(e)); @@ -1117,7 +1100,7 @@ export class TreeModel { return result; }).then(() => { - var item = this.getItem(element); + let item = this.getItem(element); if (item) { return item.reveal(relativeTop); @@ -1125,7 +1108,7 @@ export class TreeModel { }); } - private resolveUnknownParentChain(element: any): Thenable { + private resolveUnknownParentChain(element: any): Promise { return this.context.dataSource.getParent(this.context.tree, element).then((parent) => { if (!parent) { return Promise.resolve([]); @@ -1140,17 +1123,17 @@ export class TreeModel { public setHighlight(element?: any, eventPayload?: any): void { this.setTraits('highlighted', element ? [element] : []); - var eventData: _.IHighlightEvent = { highlight: this.getHighlight(), payload: eventPayload }; + let eventData: _.IHighlightEvent = { highlight: this.getHighlight(), payload: eventPayload }; this._onDidHighlight.fire(eventData); } - public getHighlight(includeHidden?: boolean): any { - var result = this.getElementsWithTrait('highlighted', includeHidden); + public getHighlight(includeHidden: boolean = false): any { + let result = this.getElementsWithTrait('highlighted', includeHidden); return result.length === 0 ? null : result[0]; } public isHighlighted(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1163,31 +1146,9 @@ export class TreeModel { this.selectAll([element], eventPayload); } - public selectRange(fromElement: any, toElement: any, eventPayload?: any): void { - var fromItem = this.getItem(fromElement); - var toItem = this.getItem(toElement); - - if (!fromItem || !toItem) { - return; - } - - this.selectAll(getRange(fromItem, toItem), eventPayload); - } - - public deselectRange(fromElement: any, toElement: any, eventPayload?: any): void { - var fromItem = this.getItem(fromElement); - var toItem = this.getItem(toElement); - - if (!fromItem || !toItem) { - return; - } - - this.deselectAll(getRange(fromItem, toItem), eventPayload); - } - public selectAll(elements: any[], eventPayload?: any): void { this.addTraits('selected', elements); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; + let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; this._onDidSelect.fire(eventData); } @@ -1197,24 +1158,18 @@ export class TreeModel { public deselectAll(elements: any[], eventPayload?: any): void { this.removeTraits('selected', elements); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; + let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; this._onDidSelect.fire(eventData); } public setSelection(elements: any[], eventPayload?: any): void { this.setTraits('selected', elements); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; - this._onDidSelect.fire(eventData); - } - - public toggleSelection(element: any, eventPayload?: any): void { - this.toggleTrait('selected', element); - var eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; + let eventData: _.ISelectionEvent = { selection: this.getSelection(), payload: eventPayload }; this._onDidSelect.fire(eventData); } public isSelected(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1223,17 +1178,17 @@ export class TreeModel { return item.hasTrait('selected'); } - public getSelection(includeHidden?: boolean): any[] { + public getSelection(includeHidden: boolean = false): any[] { return this.getElementsWithTrait('selected', includeHidden); } public selectNext(count: number = 1, clearSelection: boolean = true, eventPayload?: any): void { - var selection = this.getSelection(); - var item: Item = selection.length > 0 ? selection[0] : this.input; - var nextItem: Item; - var nav = this.getNavigator(item, false); + let selection = this.getSelection(); + let item: Item = selection.length > 0 ? selection[0] : this.input; + let nextItem: Item | null; + let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { nextItem = nav.next(); if (!nextItem) { break; @@ -1249,7 +1204,7 @@ export class TreeModel { } public selectPrevious(count: number = 1, clearSelection: boolean = true, eventPayload?: any): void { - var selection = this.getSelection(), + let selection = this.getSelection(), item: Item | null = null, previousItem: Item | null = null; @@ -1266,7 +1221,7 @@ export class TreeModel { item = selection[0]; let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { previousItem = nav.previous(); if (!previousItem) { break; @@ -1282,29 +1237,14 @@ export class TreeModel { } } - public selectParent(eventPayload?: any, clearSelection: boolean = true): void { - var selection = this.getSelection(); - var item: Item = selection.length > 0 ? selection[0] : this.input; - var nav = this.getNavigator(item, false); - var parent = nav.parent(); - - if (parent) { - if (clearSelection) { - this.setSelection([parent], eventPayload); - } else { - this.select(parent, eventPayload); - } - } - } - public setFocus(element?: any, eventPayload?: any): void { this.setTraits('focused', element ? [element] : []); - var eventData: _.IFocusEvent = { focus: this.getFocus(), payload: eventPayload }; + let eventData: _.IFocusEvent = { focus: this.getFocus(), payload: eventPayload }; this._onDidFocus.fire(eventData); } public isFocused(element: any): boolean { - var item = this.getItem(element); + let item = this.getItem(element); if (!item) { return false; @@ -1313,17 +1253,17 @@ export class TreeModel { return item.hasTrait('focused'); } - public getFocus(includeHidden?: boolean): any { - var result = this.getElementsWithTrait('focused', includeHidden); + public getFocus(includeHidden: boolean = false): any { + let result = this.getElementsWithTrait('focused', includeHidden); return result.length === 0 ? null : result[0]; } public focusNext(count: number = 1, eventPayload?: any): void { - var item: Item = this.getFocus() || this.input; - var nextItem: Item; - var nav = this.getNavigator(item, false); + let item: Item = this.getFocus() || this.input; + let nextItem: Item | null; + let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { nextItem = nav.next(); if (!nextItem) { break; @@ -1335,11 +1275,11 @@ export class TreeModel { } public focusPrevious(count: number = 1, eventPayload?: any): void { - var item: Item = this.getFocus() || this.input; - var previousItem: Item; - var nav = this.getNavigator(item, false); + let item: Item = this.getFocus() || this.input; + let previousItem: Item | null; + let nav = this.getNavigator(item, false); - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { previousItem = nav.previous(); if (!previousItem) { break; @@ -1351,9 +1291,9 @@ export class TreeModel { } public focusParent(eventPayload?: any): void { - var item: Item = this.getFocus() || this.input; - var nav = this.getNavigator(item, false); - var parent = nav.parent(); + let item: Item = this.getFocus() || this.input; + let nav = this.getNavigator(item, false); + let parent = nav.parent(); if (parent) { this.setFocus(parent, eventPayload); @@ -1376,10 +1316,10 @@ export class TreeModel { } public focusNth(index: number, eventPayload?: any, from?: any): void { - var navItem = this.getParent(from); - var nav = this.getNavigator(navItem); - var item = nav.first(); - for (var i = 0; i < index; i++) { + let navItem = this.getParent(from); + let nav = this.getNavigator(navItem); + let item = nav.first(); + for (let i = 0; i < index; i++) { item = nav.next(); } @@ -1389,12 +1329,12 @@ export class TreeModel { } public focusLast(eventPayload?: any, from?: any): void { - var navItem = this.getParent(from); - var item: Item; - if (from) { + const navItem = this.getParent(from); + let item: Item | null; + if (from && navItem) { item = navItem.lastChild; } else { - var nav = this.getNavigator(navItem); + const nav = this.getNavigator(navItem); item = nav.last(); } @@ -1403,9 +1343,9 @@ export class TreeModel { } } - private getParent(from?: any): Item { + private getParent(from?: any): Item | null { if (from) { - var fromItem = this.getItem(from); + const fromItem = this.getItem(from); if (fromItem && fromItem.parent) { return fromItem.parent; } @@ -1418,7 +1358,7 @@ export class TreeModel { return new TreeNavigator(this.getItem(element), subTreeOnly); } - public getItem(element: any = null): Item { + public getItem(element: any = null): Item | null { if (element === null) { return this.input; } else if (element instanceof Item) { @@ -1431,9 +1371,9 @@ export class TreeModel { } public addTraits(trait: string, elements: any[]): void { - var items: IItemMap = this.traitsToItems[trait] || {}; - var item: Item; - for (var i = 0, len = elements.length; i < len; i++) { + let items: IItemMap = this.traitsToItems[trait] || {}; + let item: Item | null; + for (let i = 0, len = elements.length; i < len; i++) { item = this.getItem(elements[i]); if (item) { @@ -1445,9 +1385,9 @@ export class TreeModel { } public removeTraits(trait: string, elements: any[]): void { - var items: IItemMap = this.traitsToItems[trait] || {}; - var item: Item; - var id: string; + let items: IItemMap = this.traitsToItems[trait] || {}; + let item: Item | null; + let id: string; if (elements.length === 0) { for (id in items) { @@ -1460,7 +1400,7 @@ export class TreeModel { delete this.traitsToItems[trait]; } else { - for (var i = 0, len = elements.length; i < len; i++) { + for (let i = 0, len = elements.length; i < len; i++) { item = this.getItem(elements[i]); if (item) { @@ -1471,31 +1411,12 @@ export class TreeModel { } } - public hasTrait(trait: string, element: any): boolean { - var item = this.getItem(element); - return item && item.hasTrait(trait); - } - - private toggleTrait(trait: string, element: any): void { - var item = this.getItem(element); - - if (!item) { - return; - } - - if (item.hasTrait(trait)) { - this.removeTraits(trait, [element]); - } else { - this.addTraits(trait, [element]); - } - } - private setTraits(trait: string, elements: any[]): void { if (elements.length === 0) { this.removeTraits(trait, elements); } else { - var items: { [id: string]: Item; } = {}; - var item: Item; + let items: { [id: string]: Item; } = {}; + let item: Item | null; for (let i = 0, len = elements.length; i < len; i++) { item = this.getItem(elements[i]); @@ -1505,9 +1426,9 @@ export class TreeModel { } } - var traitItems: IItemMap = this.traitsToItems[trait] || {}; - var itemsToRemoveTrait: Item[] = []; - var id: string; + let traitItems: IItemMap = this.traitsToItems[trait] || {}; + let itemsToRemoveTrait: Item[] = []; + let id: string; for (id in traitItems) { if (traitItems.hasOwnProperty(id)) { @@ -1538,9 +1459,9 @@ export class TreeModel { } private getElementsWithTrait(trait: string, includeHidden: boolean): any[] { - var elements = []; - var items = this.traitsToItems[trait] || {}; - var id: string; + let elements: any[] = []; + let items = this.traitsToItems[trait] || {}; + let id: string; for (id in items) { if (items.hasOwnProperty(id) && (items[id].isVisible() || includeHidden)) { elements.push(items[id].getElement()); @@ -1552,7 +1473,7 @@ export class TreeModel { public dispose(): void { if (this.registry) { this.registry.dispose(); - this.registry = null; + this.registry = null!; // StrictNullOverride: nulling out ok in dispose } this._onSetInput.dispose(); diff --git a/src/vs/base/parts/tree/browser/treeUtils.ts b/src/vs/base/parts/tree/browser/treeUtils.ts index 00f2ef0f37c..6d536a7fa5a 100644 --- a/src/vs/base/parts/tree/browser/treeUtils.ts +++ b/src/vs/base/parts/tree/browser/treeUtils.ts @@ -5,16 +5,6 @@ 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); @@ -26,11 +16,3 @@ export function isEqualOrParent(tree: _.ITree, element: any, candidateParent: an 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 c85df8c07ac..6852c0c727e 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -21,19 +21,19 @@ import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; import * as _ from 'vs/base/parts/tree/browser/tree'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Event, Emitter } from 'vs/base/common/event'; -import { DataTransfers } from 'vs/base/browser/dnd'; +import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; import { DefaultTreestyler } from './treeDefaults'; import { Delayer, timeout } from 'vs/base/common/async'; export interface IRow { - element: HTMLElement; + element: HTMLElement | null; templateId: string; templateData: any; } function removeFromParent(element: HTMLElement): void { try { - element.parentElement.removeChild(element); + element.parentElement!.removeChild(element); } catch (e) { // this will throw if this happens due to a blur event, nasty business } @@ -41,26 +41,26 @@ function removeFromParent(element: HTMLElement): void { export class RowCache implements Lifecycle.IDisposable { - private _cache: { [templateId: string]: IRow[]; }; + private _cache: { [templateId: string]: IRow[]; } | null; constructor(private context: _.ITreeContext) { this._cache = { '': [] }; } public alloc(templateId: string): IRow { - var result = this.cache(templateId).pop(); + let result = this.cache(templateId).pop(); if (!result) { - var content = document.createElement('div'); + let content = document.createElement('div'); content.className = 'content'; - var row = document.createElement('div'); + let row = document.createElement('div'); row.appendChild(content); let templateData: any = null; try { - templateData = this.context.renderer.renderTemplate(this.context.tree, templateId, content); + templateData = this.context.renderer!.renderTemplate(this.context.tree, templateId, content); } catch (err) { console.error('Tree usage error: exception while rendering template'); console.error(err); @@ -77,24 +77,24 @@ export class RowCache implements Lifecycle.IDisposable { } public release(templateId: string, row: IRow): void { - removeFromParent(row.element); + removeFromParent(row.element!); this.cache(templateId).push(row); } private cache(templateId: string): IRow[] { - return this._cache[templateId] || (this._cache[templateId] = []); + return this._cache![templateId] || (this._cache![templateId] = []); } public garbageCollect(): void { if (this._cache) { Object.keys(this._cache).forEach(templateId => { - this._cache[templateId].forEach(cachedRow => { - this.context.renderer.disposeTemplate(this.context.tree, templateId, cachedRow.templateData); + this._cache![templateId].forEach(cachedRow => { + this.context.renderer!.disposeTemplate(this.context.tree, templateId, cachedRow.templateData); cachedRow.element = null; cachedRow.templateData = null; }); - delete this._cache[templateId]; + delete this._cache![templateId]; }); } } @@ -102,7 +102,6 @@ export class RowCache implements Lifecycle.IDisposable { public dispose(): void { this.garbageCollect(); this._cache = null; - this.context = null; } } @@ -117,7 +116,7 @@ export class ViewItem implements IViewItem { public model: Model.Item; public id: string; - protected row: IRow; + protected row: IRow | null; public top: number; public height: number; @@ -125,8 +124,8 @@ export class ViewItem implements IViewItem { public onDragStart: (e: DragEvent) => void; public needsRender: boolean; - public uri: string; - public unbindDragStart: Lifecycle.IDisposable; + public uri: string | null; + public unbindDragStart: Lifecycle.IDisposable = Lifecycle.Disposable.None; public loadingTimer: any; public _styles: any; @@ -172,12 +171,12 @@ export class ViewItem implements IViewItem { } public get element(): HTMLElement { - return this.row && this.row.element; + return (this.row && this.row.element)!; } private _templateId: string; private get templateId(): string { - return this._templateId || (this._templateId = (this.context.renderer.getTemplateId && this.context.renderer.getTemplateId(this.context.tree, this.model.getElement()))); + return this._templateId || (this._templateId = (this.context.renderer!.getTemplateId && this.context.renderer!.getTemplateId(this.context.tree, this.model.getElement()))); } public addClass(name: string): void { @@ -195,7 +194,7 @@ export class ViewItem implements IViewItem { return; } - var classes = ['monaco-tree-row']; + let classes = ['monaco-tree-row']; classes.push.apply(classes, Object.keys(this._styles)); if (this.model.hasChildren()) { @@ -208,7 +207,7 @@ export class ViewItem implements IViewItem { // ARIA this.element.setAttribute('role', 'treeitem'); - const accessibility = this.context.accessibilityProvider; + const accessibility = this.context.accessibilityProvider!; const ariaLabel = accessibility.getAriaLabel(this.context.tree, this.model.getElement()); if (ariaLabel) { this.element.setAttribute('aria-label', ariaLabel); @@ -234,18 +233,17 @@ export class ViewItem implements IViewItem { this.element.setAttribute('aria-level', String(this.model.getDepth())); if (this.context.options.paddingOnRow) { - this.element.style.paddingLeft = this.context.options.twistiePixels + ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px'; + this.element.style.paddingLeft = this.context.options.twistiePixels! + ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; } else { - this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px'; - (this.row.element.firstElementChild).style.paddingLeft = this.context.options.twistiePixels + 'px'; + this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; + (this.row!.element!.firstElementChild).style.paddingLeft = this.context.options.twistiePixels + 'px'; } - var uri = this.context.dnd.getDragURI(this.context.tree, this.model.getElement()); + let uri = this.context.dnd!.getDragURI(this.context.tree, this.model.getElement()); if (uri !== this.uri) { if (this.unbindDragStart) { this.unbindDragStart.dispose(); - this.unbindDragStart = null; } if (uri) { @@ -260,15 +258,18 @@ export class ViewItem implements IViewItem { } if (!skipUserRender && this.element) { - const style = window.getComputedStyle(this.element); - const paddingLeft = parseFloat(style.paddingLeft); + let paddingLeft: number = 0; + if (this.context.horizontalScrolling) { + const style = window.getComputedStyle(this.element); + paddingLeft = parseFloat(style.paddingLeft!); + } if (this.context.horizontalScrolling) { this.element.style.width = 'fit-content'; } try { - this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData); + 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); @@ -287,13 +288,13 @@ export class ViewItem implements IViewItem { } const style = window.getComputedStyle(this.element); - const paddingLeft = parseFloat(style.paddingLeft); + const paddingLeft = parseFloat(style.paddingLeft!); this.element.style.width = 'fit-content'; this.width = DOM.getContentWidth(this.element) + paddingLeft; this.element.style.width = ''; } - public insertInDOM(container: HTMLElement, afterElement: HTMLElement): void { + public insertInDOM(container: HTMLElement, afterElement: HTMLElement | null): void { if (!this.row) { this.row = this.context.cache.alloc(this.templateId); @@ -324,10 +325,7 @@ export class ViewItem implements IViewItem { return; } - if (this.unbindDragStart) { - this.unbindDragStart.dispose(); - this.unbindDragStart = null; - } + this.unbindDragStart.dispose(); this.uri = null; @@ -338,7 +336,6 @@ export class ViewItem implements IViewItem { public dispose(): void { this.row = null; - this.model = null; } } @@ -350,7 +347,7 @@ class RootViewItem extends ViewItem { this.row = { element: wrapper, templateData: null, - templateId: null + templateId: null! }; } @@ -359,7 +356,7 @@ class RootViewItem extends ViewItem { return; } - var classes = ['monaco-tree-wrapper']; + let classes = ['monaco-tree-wrapper']; classes.push.apply(classes, Object.keys(this._styles)); if (this.model.hasChildren()) { @@ -383,7 +380,7 @@ interface IThrottledGestureEvent { translationY: number; } -function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction): boolean { +function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction | null): boolean { if (!one && !other) { return true; } else if (!one || !other) { @@ -407,11 +404,9 @@ export class TreeView extends HeightMap { private static counter: number = 0; private instance: number; - private static currentExternalDragAndDropData: _.IDragAndDropData = null; - private context: IViewContext; private modelListeners: Lifecycle.IDisposable[]; - private model: Model.TreeModel; + private model: Model.TreeModel | null = null; private viewListeners: Lifecycle.IDisposable[]; private domNode: HTMLElement; @@ -435,29 +430,29 @@ export class TreeView extends HeightMap { private isRefreshing = false; private refreshingPreviousChildrenIds: { [id: string]: string[] } = {}; - private currentDragAndDropData: _.IDragAndDropData; + private currentDragAndDropData: IDragAndDropData | null = null; private currentDropElement: any; private currentDropElementReaction: _.IDragOverReaction; - private currentDropTarget: ViewItem; + private currentDropTarget: ViewItem | null = null; private shouldInvalidateDropReaction: boolean; - private currentDropTargets: ViewItem[]; + private currentDropTargets: ViewItem[] | null = null; private currentDropDisposable: Lifecycle.IDisposable = Lifecycle.Disposable.None; - private dragAndDropScrollInterval: number; - private dragAndDropScrollTimeout: number; - private dragAndDropMouseY: number; + private dragAndDropScrollInterval: number | null = null; + private dragAndDropScrollTimeout: number | null = null; + private dragAndDropMouseY: number | null = null; private didJustPressContextMenuKey: boolean; private highlightedItemWasDraggable: boolean; - private onHiddenScrollTop: number; + private onHiddenScrollTop: number | null = null; - private readonly _onDOMFocus: Emitter = new Emitter(); + private readonly _onDOMFocus = new Emitter(); get onDOMFocus(): Event { return this._onDOMFocus.event; } - private readonly _onDOMBlur: Emitter = new Emitter(); + private readonly _onDOMBlur = new Emitter(); get onDOMBlur(): Event { return this._onDOMBlur.event; } - private readonly _onDidScroll: Emitter = new Emitter(); + private readonly _onDidScroll = new Emitter(); get onDidScroll(): Event { return this._onDidScroll.event; } constructor(context: _.ITreeContext, container: HTMLElement) { @@ -486,7 +481,6 @@ export class TreeView extends HeightMap { this.modelListeners = []; this.viewListeners = []; - this.model = null; this.items = {}; this.domNode = document.createElement('div'); @@ -496,10 +490,7 @@ export class TreeView extends HeightMap { this.styleElement = DOM.createStyleSheet(this.domNode); - this.treeStyler = context.styler; - if (!this.treeStyler) { - this.treeStyler = new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`); - } + this.treeStyler = context.styler || new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`); // ARIA this.domNode.setAttribute('role', 'tree'); @@ -541,7 +532,7 @@ export class TreeView extends HeightMap { this.rowsContainer.className += ' show-twisties'; } - var focusTracker = DOM.trackFocus(this.domNode); + let focusTracker = DOM.trackFocus(this.domNode); this.viewListeners.push(focusTracker.onDidFocus(() => this.onFocus())); this.viewListeners.push(focusTracker.onDidBlur(() => this.onBlur())); this.viewListeners.push(focusTracker); @@ -569,7 +560,7 @@ export class TreeView extends HeightMap { event.stopPropagation(); event.preventDefault(); - var result = { translationY: event.translationY, translationX: event.translationX }; + let result = { translationY: event.translationY, translationX: event.translationX }; if (lastEvent) { result.translationY += lastEvent.translationY; @@ -601,8 +592,6 @@ export class TreeView extends HeightMap { this.dragAndDropScrollInterval = null; this.dragAndDropScrollTimeout = null; - this.onHiddenScrollTop = null; - this.onRowsChanged(); this.layout(); @@ -636,7 +625,7 @@ export class TreeView extends HeightMap { } public onVisible(): void { - this.scrollTop = this.onHiddenScrollTop; + this.scrollTop = this.onHiddenScrollTop!; this.onHiddenScrollTop = null; this.setupMSGesture(); } @@ -669,34 +658,13 @@ export class TreeView extends HeightMap { } } - public getFirstVisibleElement(): any { - const firstIndex = this.indexAt(this.lastRenderTop); - let item = this.itemAtIndex(firstIndex); - if (!item) { - return item; - } - - const itemMidpoint = item.top + item.height / 2; - if (itemMidpoint < this.scrollTop) { - const nextItem = this.itemAtIndex(firstIndex + 1); - item = nextItem || item; - } - - return 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; + let i: number; + let stop: number; - var renderTop = scrollTop; - var renderBottom = scrollTop + viewHeight; - var thisRenderBottom = this.lastRenderTop + this.lastRenderHeight; + let renderTop = scrollTop; + let renderBottom = scrollTop + viewHeight; + let thisRenderBottom = this.lastRenderTop + this.lastRenderHeight; // when view scrolls down, start rendering from the renderBottom for (i = this.indexAfter(renderBottom) - 1, stop = this.indexAt(Math.max(thisRenderBottom, renderTop)); i >= stop; i--) { @@ -718,7 +686,7 @@ export class TreeView extends HeightMap { this.removeItemFromDOM(this.itemAtIndex(i)); } - var topItem = this.itemAtIndex(this.indexAt(renderTop)); + let topItem = this.itemAtIndex(this.indexAt(renderTop)); if (topItem) { this.rowsContainer.style.top = (topItem.top - renderTop) + 'px'; @@ -790,15 +758,15 @@ export class TreeView extends HeightMap { } public focusNextPage(eventPayload?: any): void { - var lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight); + let lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; - var lastPageElement = this.itemAtIndex(lastPageIndex).model.getElement(); - var currentlyFocusedElement = this.model.getFocus(); + let lastPageElement = this.itemAtIndex(lastPageIndex).model.getElement(); + let currentlyFocusedElement = this.model!.getFocus(); if (currentlyFocusedElement !== lastPageElement) { - this.model.setFocus(lastPageElement, eventPayload); + this.model!.setFocus(lastPageElement, eventPayload); } else { - var previousScrollTop = this.scrollTop; + let previousScrollTop = this.scrollTop; this.scrollTop += this.viewHeight; if (this.scrollTop !== previousScrollTop) { @@ -812,7 +780,7 @@ export class TreeView extends HeightMap { } public focusPreviousPage(eventPayload?: any): void { - var firstPageIndex: number; + let firstPageIndex: number; if (this.scrollTop === 0) { firstPageIndex = this.indexAt(this.scrollTop); @@ -820,13 +788,13 @@ export class TreeView extends HeightMap { firstPageIndex = this.indexAfter(this.scrollTop - 1); } - var firstPageElement = this.itemAtIndex(firstPageIndex).model.getElement(); - var currentlyFocusedElement = this.model.getFocus(); + let firstPageElement = this.itemAtIndex(firstPageIndex).model.getElement(); + let currentlyFocusedElement = this.model!.getFocus(); if (currentlyFocusedElement !== firstPageElement) { - this.model.setFocus(firstPageElement, eventPayload); + this.model!.setFocus(firstPageElement, eventPayload); } else { - var previousScrollTop = this.scrollTop; + let previousScrollTop = this.scrollTop; this.scrollTop -= this.viewHeight; if (this.scrollTop !== previousScrollTop) { @@ -890,7 +858,7 @@ export class TreeView extends HeightMap { // Events private onClearingInput(e: Model.IInputEvent): void { - var item = e.item; + let item = e.item; if (item) { this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); this.onRowsChanged(); @@ -903,8 +871,8 @@ export class TreeView extends HeightMap { } private onItemChildrenRefreshing(e: Model.IItemChildrenRefreshEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem && this.context.options.showLoading) { viewItem.loadingTimer = setTimeout(() => { @@ -914,9 +882,9 @@ export class TreeView extends HeightMap { } if (!e.isNested) { - var childrenIds: string[] = []; - var navigator = item.getNavigator(); - var childItem: Model.Item; + let childrenIds: string[] = []; + let navigator = item.getNavigator(); + let childItem: Model.Item | null; while (childItem = navigator.next()) { childrenIds.push(childItem.id); @@ -927,8 +895,8 @@ export class TreeView extends HeightMap { } private onItemChildrenRefreshed(e: Model.IItemChildrenRefreshEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem) { if (viewItem.loadingTimer) { @@ -940,18 +908,18 @@ export class TreeView extends HeightMap { } if (!e.isNested) { - var previousChildrenIds = this.refreshingPreviousChildrenIds[item.id]; - var afterModelItems: Model.Item[] = []; - var navigator = item.getNavigator(); - var childItem: Model.Item; + let previousChildrenIds = this.refreshingPreviousChildrenIds[item.id]; + let afterModelItems: Model.Item[] = []; + let navigator = item.getNavigator(); + let childItem: Model.Item | null; while (childItem = navigator.next()) { afterModelItems.push(childItem); } let skipDiff = Math.abs(previousChildrenIds.length - afterModelItems.length) > 1000; - let diff: Diff.IDiffChange[]; - let doToInsertItemsAlreadyExist: boolean; + let diff: Diff.IDiffChange[] = []; + let doToInsertItemsAlreadyExist: boolean = false; if (!skipDiff) { const lcs = new Diff.LcsDiff( @@ -973,7 +941,7 @@ export class TreeView extends HeightMap { // of the elements has changed doToInsertItemsAlreadyExist = diff.some(d => { if (d.modifiedLength > 0) { - for (var i = d.modifiedStart, len = d.modifiedStart + d.modifiedLength; i < len; i++) { + for (let i = d.modifiedStart, len = d.modifiedStart + d.modifiedLength; i < len; i++) { if (this.items.hasOwnProperty(afterModelItems[i].id)) { return true; } @@ -986,15 +954,14 @@ export class TreeView extends HeightMap { // 50 is an optimization number, at some point we're better off // just replacing everything if (!skipDiff && !doToInsertItemsAlreadyExist && diff.length < 50) { - for (let i = 0, len = diff.length; i < len; i++) { - const diffChange = diff[i]; + for (const diffChange of diff) { if (diffChange.originalLength > 0) { this.onRemoveItems(new ArrayIterator(previousChildrenIds, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength)); } if (diffChange.modifiedLength > 0) { - let beforeItem = afterModelItems[diffChange.modifiedStart - 1] || item; + let beforeItem: Model.Item | null = afterModelItems[diffChange.modifiedStart - 1] || item; beforeItem = beforeItem.getDepth() > 0 ? beforeItem : null; this.onInsertItems(new ArrayIterator(afterModelItems, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength), beforeItem ? beforeItem.id : null); @@ -1022,20 +989,20 @@ export class TreeView extends HeightMap { } private onItemExpanding(e: Model.IItemExpandEvent): void { - var viewItem = this.items[e.item.id]; + let viewItem = this.items[e.item.id]; if (viewItem) { viewItem.expanded = true; } } private onItemExpanded(e: Model.IItemExpandEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.expanded = true; - var height = this.onInsertItems(item.getNavigator(), item.id); - var scrollTop = this.scrollTop; + let height = this.onInsertItems(item.getNavigator(), item.id) || 0; + let scrollTop = this.scrollTop; if (viewItem.top + viewItem.height <= this.scrollTop) { scrollTop += height; @@ -1046,8 +1013,8 @@ export class TreeView extends HeightMap { } private onItemCollapsing(e: Model.IItemCollapseEvent): void { - var item = e.item; - var viewItem = this.items[item.id]; + let item = e.item; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.expanded = false; this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); @@ -1055,46 +1022,21 @@ export class TreeView extends HeightMap { } } - public updateWidth(item: Model.Item): void { - if (!item || !item.isVisible()) { - return; - } - - const viewItem = this.items[item.id]; - - if (!viewItem) { - return; - } - - viewItem.updateWidth(); - this.updateScrollWidth(); - } - - public getRelativeTop(item: Model.Item): number { - if (item && item.isVisible()) { - var viewItem = this.items[item.id]; - if (viewItem) { - return (viewItem.top - this.scrollTop) / (this.viewHeight - viewItem.height); - } - } - return -1; - } - private onItemReveal(e: Model.IItemRevealEvent): void { - var item = e.item; - var relativeTop = e.relativeTop; - var viewItem = this.items[item.id]; + let item = e.item; + let relativeTop = e.relativeTop; + let viewItem = this.items[item.id]; if (viewItem) { if (relativeTop !== null) { relativeTop = relativeTop < 0 ? 0 : relativeTop; relativeTop = relativeTop > 1 ? 1 : relativeTop; // y = mx + b - var m = viewItem.height - this.viewHeight; + let m = viewItem.height - this.viewHeight; this.scrollTop = m * relativeTop + viewItem.top; } else { - var viewItemBottom = viewItem.top + viewItem.height; - var wrapperBottom = this.scrollTop + this.viewHeight; + let viewItemBottom = viewItem.top + viewItem.height; + let wrapperBottom = this.scrollTop + this.viewHeight; if (viewItem.top < this.scrollTop) { this.scrollTop = viewItem.top; @@ -1106,9 +1048,9 @@ export class TreeView extends HeightMap { } private onItemAddTrait(e: Model.IItemTraitEvent): void { - var item = e.item; - var trait = e.trait; - var viewItem = this.items[item.id]; + let item = e.item; + let trait = e.trait; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.addClass(trait); } @@ -1126,9 +1068,9 @@ export class TreeView extends HeightMap { } private onItemRemoveTrait(e: Model.IItemTraitEvent): void { - var item = e.item; - var trait = e.trait; - var viewItem = this.items[item.id]; + let item = e.item; + let trait = e.trait; + let viewItem = this.items[item.id]; if (viewItem) { viewItem.removeClass(trait); } @@ -1195,8 +1137,8 @@ export class TreeView extends HeightMap { return; } - var event = new Mouse.StandardMouseEvent(e); - var item = this.getItemAround(event.target); + let event = new Mouse.StandardMouseEvent(e); + let item = this.getItemAround(event.target); if (!item) { return; @@ -1211,27 +1153,27 @@ export class TreeView extends HeightMap { } this.lastClickTimeStamp = Date.now(); - this.context.controller.onClick(this.context.tree, item.model.getElement(), event); + this.context.controller!.onClick(this.context.tree, item.model.getElement(), event); } private onMouseMiddleClick(e: MouseEvent): void { - if (!this.context.controller.onMouseMiddleClick) { + if (!this.context.controller!.onMouseMiddleClick!) { return; } - var event = new Mouse.StandardMouseEvent(e); - var item = this.getItemAround(event.target); + let event = new Mouse.StandardMouseEvent(e); + let item = this.getItemAround(event.target); if (!item) { return; } - this.context.controller.onMouseMiddleClick(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseMiddleClick!(this.context.tree, item.model.getElement(), event); } private onMouseDown(e: MouseEvent): void { this.didJustPressContextMenuKey = false; - if (!this.context.controller.onMouseDown) { + if (!this.context.controller!.onMouseDown!) { return; } @@ -1239,23 +1181,23 @@ export class TreeView extends HeightMap { return; } - var event = new Mouse.StandardMouseEvent(e); + let event = new Mouse.StandardMouseEvent(e); if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) { return; } - var item = this.getItemAround(event.target); + let item = this.getItemAround(event.target); if (!item) { return; } - this.context.controller.onMouseDown(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseDown!(this.context.tree, item.model.getElement(), event); } private onMouseUp(e: MouseEvent): void { - if (!this.context.controller.onMouseUp) { + if (!this.context.controller!.onMouseUp!) { return; } @@ -1263,29 +1205,29 @@ export class TreeView extends HeightMap { return; } - var event = new Mouse.StandardMouseEvent(e); + let event = new Mouse.StandardMouseEvent(e); if (event.ctrlKey && Platform.isNative && Platform.isMacintosh) { return; } - var item = this.getItemAround(event.target); + let item = this.getItemAround(event.target); if (!item) { return; } - this.context.controller.onMouseUp(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseUp!(this.context.tree, item.model.getElement(), event); } private onTap(e: Touch.GestureEvent): void { - var item = this.getItemAround(e.initialTarget); + let item = this.getItemAround(e.initialTarget); if (!item) { return; } - this.context.controller.onTap(this.context.tree, item.model.getElement(), e); + this.context.controller!.onTap(this.context.tree, item.model.getElement(), e); } private onTouchChange(event: Touch.GestureEvent): void { @@ -1298,31 +1240,31 @@ export class TreeView extends HeightMap { private onContextMenu(keyboardEvent: KeyboardEvent): void; private onContextMenu(mouseEvent: MouseEvent): void; private onContextMenu(event: KeyboardEvent | MouseEvent): void { - var resultEvent: _.ContextMenuEvent; - var element: any; + let resultEvent: _.ContextMenuEvent; + let element: any; if (event instanceof KeyboardEvent || this.didJustPressContextMenuKey) { this.didJustPressContextMenuKey = false; - var keyboardEvent = new Keyboard.StandardKeyboardEvent(event); - element = this.model.getFocus(); + let keyboardEvent = new Keyboard.StandardKeyboardEvent(event); + element = this.model!.getFocus(); - var position: DOM.IDomNodePagePosition; + let position: DOM.IDomNodePagePosition; if (!element) { - element = this.model.getInput(); + element = this.model!.getInput(); position = DOM.getDomNodePagePosition(this.inputItem.element); } else { - var id = this.context.dataSource.getId(this.context.tree, element); - var viewItem = this.items[id]; + const id = this.context.dataSource.getId(this.context.tree, element); + const viewItem = this.items[id!]; position = DOM.getDomNodePagePosition(viewItem.element); } resultEvent = new _.KeyboardContextMenuEvent(position.left + position.width, position.top, keyboardEvent); } else { - var mouseEvent = new Mouse.StandardMouseEvent(event); - var item = this.getItemAround(mouseEvent.target); + let mouseEvent = new Mouse.StandardMouseEvent(event); + let item = this.getItemAround(mouseEvent.target); if (!item) { return; @@ -1332,11 +1274,11 @@ export class TreeView extends HeightMap { resultEvent = new _.MouseContextMenuEvent(mouseEvent); } - this.context.controller.onContextMenu(this.context.tree, element, resultEvent); + this.context.controller!.onContextMenu(this.context.tree, element, resultEvent); } private onKeyDown(e: KeyboardEvent): void { - var event = new Keyboard.StandardKeyboardEvent(e); + let event = new Keyboard.StandardKeyboardEvent(e); this.didJustPressContextMenuKey = event.keyCode === KeyCode.ContextMenu || (event.shiftKey && event.keyCode === KeyCode.F10); @@ -1349,7 +1291,7 @@ export class TreeView extends HeightMap { event.stopPropagation(); } - this.context.controller.onKeyDown(this.context.tree, event); + this.context.controller!.onKeyDown(this.context.tree, event); } private onKeyUp(e: KeyboardEvent): void { @@ -1358,17 +1300,17 @@ export class TreeView extends HeightMap { } this.didJustPressContextMenuKey = false; - this.context.controller.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e)); + this.context.controller!.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e)); } private onDragStart(item: ViewItem, e: any): void { - if (this.model.getHighlight()) { + if (this.model!.getHighlight()) { return; } - var element = item.model.getElement(); - var selection = this.model.getSelection(); - var elements: any[]; + let element = item.model.getElement(); + let selection = this.model!.getSelection(); + let elements: any[]; if (selection.indexOf(element) > -1) { elements = selection; @@ -1381,8 +1323,8 @@ export class TreeView extends HeightMap { if (e.dataTransfer.setDragImage) { let label: string; - if (this.context.dnd.getDragLabel) { - label = this.context.dnd.getDragLabel(this.context.tree, elements); + if (this.context.dnd!.getDragLabel) { + label = this.context.dnd!.getDragLabel!(this.context.tree, elements); } else { label = String(elements.length); } @@ -1396,23 +1338,23 @@ export class TreeView extends HeightMap { } this.currentDragAndDropData = new dnd.ElementsDragAndDropData(elements); - TreeView.currentExternalDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); + StaticDND.CurrentDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); - this.context.dnd.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); + this.context.dnd!.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); } private setupDragAndDropScrollInterval(): void { - var viewTop = DOM.getTopLeftOffset(this.wrapper).top; + let viewTop = DOM.getTopLeftOffset(this.wrapper).top; if (!this.dragAndDropScrollInterval) { this.dragAndDropScrollInterval = window.setInterval(() => { - if (this.dragAndDropMouseY === undefined) { + if (this.dragAndDropMouseY === null) { return; } - var diff = this.dragAndDropMouseY - viewTop; - var scrollDiff = 0; - var upperLimit = this.viewHeight - 35; + let diff = this.dragAndDropMouseY - viewTop; + let scrollDiff = 0; + let upperLimit = this.viewHeight - 35; if (diff < 35) { scrollDiff = Math.max(-14, 0.2 * (diff - 35)); @@ -1449,9 +1391,11 @@ export class TreeView extends HeightMap { } private onDragOver(e: DragEvent): boolean { - var event = new Mouse.DragMouseEvent(e); + e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - var viewItem = this.getItemAround(event.target); + let event = new Mouse.DragMouseEvent(e); + + let viewItem = this.getItemAround(event.target); if (!viewItem || (event.posx === 0 && event.posy === 0 && event.browserEvent.type === DOM.EventType.DRAG_LEAVE)) { // dragging outside of tree @@ -1459,7 +1403,7 @@ export class TreeView extends HeightMap { if (this.currentDropTarget) { // clear previously hovered element feedback - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; this.currentDropDisposable.dispose(); } @@ -1479,8 +1423,8 @@ export class TreeView extends HeightMap { if (!this.currentDragAndDropData) { // just started dragging - if (TreeView.currentExternalDragAndDropData) { - this.currentDragAndDropData = TreeView.currentExternalDragAndDropData; + if (StaticDND.CurrentDragAndDropData) { + this.currentDragAndDropData = StaticDND.CurrentDragAndDropData; } else { if (!event.dataTransfer.types) { return false; @@ -1490,16 +1434,16 @@ export class TreeView extends HeightMap { } } - this.currentDragAndDropData.update(event); + this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer!); - var element: any; - var item: Model.Item = viewItem.model; - var reaction: _.IDragOverReaction; + let element: any; + let item: Model.Item | null = viewItem.model; + let reaction: _.IDragOverReaction | null; // check the bubble up behavior do { - element = item ? item.getElement() : this.model.getInput(); - reaction = this.context.dnd.onDragOver(this.context.tree, this.currentDragAndDropData, element, event); + element = item ? item.getElement() : this.model!.getInput(); + reaction = this.context.dnd!.onDragOver(this.context.tree, this.currentDragAndDropData, element, event); if (!reaction || reaction.bubble !== _.DragOverBubble.BUBBLE_UP) { break; @@ -1513,12 +1457,12 @@ export class TreeView extends HeightMap { return false; } - var canDrop = reaction && reaction.accept; + let canDrop = reaction && reaction.accept; if (canDrop) { this.currentDropElement = item.getElement(); event.preventDefault(); - event.dataTransfer.dropEffect = reaction.effect === _.DragOverEffect.COPY ? 'copy' : 'move'; + event.dataTransfer.dropEffect = reaction!.effect === _.DragOverEffect.COPY ? 'copy' : 'move'; } else { this.currentDropElement = null; } @@ -1526,41 +1470,41 @@ export class TreeView extends HeightMap { // item is the model item where drop() should be called // can be null - var currentDropTarget = item.id === this.inputItem.id ? this.inputItem : this.items[item.id]; + let currentDropTarget = item.id === this.inputItem.id ? this.inputItem : this.items[item.id]; if (this.shouldInvalidateDropReaction || this.currentDropTarget !== currentDropTarget || !reactionEquals(this.currentDropElementReaction, reaction)) { this.shouldInvalidateDropReaction = false; if (this.currentDropTarget) { - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; this.currentDropDisposable.dispose(); } this.currentDropTarget = currentDropTarget; - this.currentDropElementReaction = reaction; + this.currentDropElementReaction = reaction!; if (canDrop) { // setup hover feedback for drop target if (this.currentDropTarget) { this.currentDropTarget.dropTarget = true; - this.currentDropTargets.push(this.currentDropTarget); + this.currentDropTargets!.push(this.currentDropTarget); } - if (reaction.bubble === _.DragOverBubble.BUBBLE_DOWN) { - var nav = item.getNavigator(); - var child: Model.Item; + if (reaction!.bubble === _.DragOverBubble.BUBBLE_DOWN) { + let nav = item.getNavigator(); + let child: Model.Item | null; while (child = nav.next()) { viewItem = this.items[child.id]; if (viewItem) { viewItem.dropTarget = true; - this.currentDropTargets.push(viewItem); + this.currentDropTargets!.push(viewItem); } } } - if (reaction.autoExpand) { + if (reaction!.autoExpand) { const timeoutPromise = timeout(500); this.currentDropDisposable = Lifecycle.toDisposable(() => timeoutPromise.cancel()); @@ -1576,10 +1520,10 @@ export class TreeView extends HeightMap { private onDrop(e: DragEvent): void { if (this.currentDropElement) { - var event = new Mouse.DragMouseEvent(e); + let event = new Mouse.DragMouseEvent(e); event.preventDefault(); - this.currentDragAndDropData.update(event); - this.context.dnd.drop(this.context.tree, this.currentDragAndDropData, this.currentDropElement, event); + this.currentDragAndDropData!.update((event.browserEvent as DragEvent).dataTransfer!); + this.context.dnd!.drop(this.context.tree, this.currentDragAndDropData!, this.currentDropElement, event); this.onDragEnd(e); } this.cancelDragAndDropScrollInterval(); @@ -1587,7 +1531,7 @@ export class TreeView extends HeightMap { private onDragEnd(e: DragEvent): void { if (this.currentDropTarget) { - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; } @@ -1595,7 +1539,7 @@ export class TreeView extends HeightMap { this.cancelDragAndDropScrollInterval(); this.currentDragAndDropData = null; - TreeView.currentExternalDragAndDropData = null; + StaticDND.CurrentDragAndDropData = undefined; this.currentDropElement = null; this.currentDropTarget = null; this.dragAndDropMouseY = null; @@ -1627,7 +1571,7 @@ export class TreeView extends HeightMap { } // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions - var pointerType = event.pointerType; + let pointerType = event.pointerType; if (pointerType === ((event).MSPOINTER_TYPE_MOUSE || 'mouse')) { this.lastPointerType = 'mouse'; return; @@ -1655,8 +1599,8 @@ export class TreeView extends HeightMap { // DOM changes private insertItemInDOM(item: ViewItem): void { - var elementAfter: HTMLElement | null = null; - var itemAfter = this.itemAfter(item); + let elementAfter: HTMLElement | null = null; + let itemAfter = this.itemAfter(item); if (itemAfter && itemAfter.element) { elementAfter = itemAfter.element; @@ -1679,21 +1623,24 @@ export class TreeView extends HeightMap { return item.top < this.lastRenderTop + this.lastRenderHeight && item.top + item.height > this.lastRenderTop; } - private getItemAround(element: HTMLElement): ViewItem { - var candidate: ViewItem = this.inputItem; + private getItemAround(element: HTMLElement): ViewItem | undefined { + let candidate: ViewItem = this.inputItem; + let el: HTMLElement | null = element; + do { - if ((element)[TreeView.BINDING]) { - candidate = (element)[TreeView.BINDING]; + if ((el)[TreeView.BINDING]) { + candidate = (el)[TreeView.BINDING]; } - if (element === this.wrapper || element === this.domNode) { + if (el === this.wrapper || el === this.domNode) { return candidate; } - if (element === this.scrollableElement.getDomNode() || element === document.body) { - return null; + if (el === this.scrollableElement.getDomNode() || el === document.body) { + return undefined; } - } while (element = element.parentElement); + } while (el = el.parentElement); + return undefined; } @@ -1711,7 +1658,6 @@ export class TreeView extends HeightMap { this.scrollableElement.dispose(); this.releaseModel(); - this.modelListeners = null; this.viewListeners = Lifecycle.dispose(this.viewListeners); @@ -1721,16 +1667,13 @@ export class TreeView extends HeightMap { if (this.domNode.parentNode) { this.domNode.parentNode.removeChild(this.domNode); } - this.domNode = null; if (this.items) { Object.keys(this.items).forEach(key => this.items[key].removeFromDOM()); - this.items = null; } if (this.context.cache) { this.context.cache.dispose(); - this.context.cache = null; } super.dispose(); diff --git a/src/vs/base/parts/tree/browser/treeViewModel.ts b/src/vs/base/parts/tree/browser/treeViewModel.ts index ee14736039f..823f0bb0a9d 100644 --- a/src/vs/base/parts/tree/browser/treeViewModel.ts +++ b/src/vs/base/parts/tree/browser/treeViewModel.ts @@ -15,25 +15,20 @@ export interface IViewItem { export class HeightMap { - private heightMap: IViewItem[]; - private indexes: { [item: string]: number; }; + private heightMap: IViewItem[] = []; + private indexes: { [item: string]: number; } = {}; - constructor() { - this.heightMap = []; - this.indexes = {}; - } - - public getContentHeight(): number { - var last = this.heightMap[this.heightMap.length - 1]; + getContentHeight(): number { + let last = this.heightMap[this.heightMap.length - 1]; return !last ? 0 : last.top + last.height; } - public onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number { - var item: Item; - var viewItem: IViewItem; - var i: number, j: number; - var totalSize: number; - var sizeDiff = 0; + onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number | undefined { + let item: Item | null = null; + let viewItem: IViewItem; + let i: number, j: number; + let totalSize: number; + let sizeDiff = 0; if (afterItemId === null) { i = 0; @@ -50,9 +45,9 @@ export class HeightMap { totalSize = viewItem.top + viewItem.height; } - var boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0); + let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0); - var itemsToInsert: IViewItem[] = []; + let itemsToInsert: IViewItem[] = []; while (item = iterator.next()) { viewItem = this.createViewItem(item); @@ -82,17 +77,17 @@ export class HeightMap { return sizeDiff; } - public onInsertItem(item: IViewItem): void { + onInsertItem(item: IViewItem): void { // noop } // Contiguous items - public onRemoveItems(iterator: INextIterator): void { - var itemId: string; - var viewItem: IViewItem; - var startIndex: number | null = null; - var i: number; - var sizeDiff = 0; + onRemoveItems(iterator: INextIterator): void { + let itemId: string | null = null; + let viewItem: IViewItem; + let startIndex: number | null = null; + let i = 0; + let sizeDiff = 0; while (itemId = iterator.next()) { i = this.indexes[itemId]; @@ -112,7 +107,7 @@ export class HeightMap { } } - if (sizeDiff === 0) { + if (sizeDiff === 0 || startIndex === null) { return; } @@ -126,22 +121,22 @@ export class HeightMap { } } - public onRemoveItem(item: IViewItem): void { + onRemoveItem(item: IViewItem): void { // noop } - public onRefreshItemSet(items: Item[]): void { - var sortedItems = items.sort((a, b) => this.indexes[a.id] - this.indexes[b.id]); + onRefreshItemSet(items: Item[]): void { + let sortedItems = items.sort((a, b) => this.indexes[a.id] - this.indexes[b.id]); this.onRefreshItems(new ArrayIterator(sortedItems)); } // Ordered, but not necessarily contiguous items - public onRefreshItems(iterator: INextIterator): void { - var item: Item; - var viewItem: IViewItem; - var newHeight: number; - var i: number, j: number | null = null; - var cummDiff = 0; + onRefreshItems(iterator: INextIterator): void { + let item: Item | null = null; + let viewItem: IViewItem; + let newHeight: number; + let i: number, j: number | null = null; + let cummDiff = 0; while (item = iterator.next()) { i = this.indexes[item.id]; @@ -171,31 +166,31 @@ export class HeightMap { } } - public onRefreshItem(item: IViewItem, needsRender: boolean = false): void { + onRefreshItem(item: IViewItem, needsRender: boolean = false): void { // noop } - public itemsCount(): number { + itemsCount(): number { return this.heightMap.length; } - public itemAt(position: number): string { + itemAt(position: number): string { return this.heightMap[this.indexAt(position)].model.id; } - public withItemsInRange(start: number, end: number, fn: (item: string) => void): void { + withItemsInRange(start: number, end: number, fn: (item: string) => void): void { start = this.indexAt(start); end = this.indexAt(end); - for (var i = start; i <= end; i++) { + for (let i = start; i <= end; i++) { fn(this.heightMap[i].model.id); } } - public indexAt(position: number): number { - var left = 0; - var right = this.heightMap.length; - var center: number; - var item: IViewItem; + indexAt(position: number): number { + let left = 0; + let right = this.heightMap.length; + let center: number; + let item: IViewItem; // Binary search while (left < right) { @@ -217,15 +212,15 @@ export class HeightMap { return this.heightMap.length; } - public indexAfter(position: number): number { + indexAfter(position: number): number { return Math.min(this.indexAt(position) + 1, this.heightMap.length); } - public itemAtIndex(index: number): IViewItem { + itemAtIndex(index: number): IViewItem { return this.heightMap[index]; } - public itemAfter(item: IViewItem): IViewItem { + itemAfter(item: IViewItem): IViewItem { return this.heightMap[this.indexes[item.model.id] + 1] || null; } @@ -233,8 +228,8 @@ export class HeightMap { throw new Error('not implemented'); } - public dispose(): void { - this.heightMap = null; - this.indexes = null; + dispose(): void { + this.heightMap = []; + this.indexes = {}; } } diff --git a/src/vs/base/parts/tree/test/browser/treeModel.test.ts b/src/vs/base/parts/tree/test/browser/treeModel.test.ts index 6bdc99dfc77..5a880d99194 100644 --- a/src/vs/base/parts/tree/test/browser/treeModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeModel.test.ts @@ -36,12 +36,12 @@ export class FakeRenderer { class TreeContext implements _.ITreeContext { - public tree: _.ITree = null; + public tree: _.ITree = null!; public options: _.ITreeOptions = { autoExpandSingleChildren: true }; public dataSource: _.IDataSource; public renderer: _.IRenderer; - public controller: _.IController; - public dnd: _.IDragAndDrop; + public controller?: _.IController; + public dnd?: _.IDragAndDrop; public filter: _.IFilter; public sorter: _.ISorter; @@ -72,7 +72,7 @@ class EventCounter { this._count = 0; } - public listen(event: Event, fn: (e: T) => void = null): () => void { + public listen(event: Event, fn: ((e: T) => void) | null = null): () => void { let r = event(data => { this._count++; if (fn) { @@ -105,7 +105,7 @@ class EventCounter { } } -var SAMPLE: any = { +const SAMPLE: any = { ONE: { id: 'one' }, AB: { @@ -161,26 +161,26 @@ var SAMPLE: any = { }; class TestDataSource implements _.IDataSource { - public getId(tree, element): string { + public getId(tree: _.ITree, element: any): string { return element.id; } - public hasChildren(tree, element): boolean { + public hasChildren(tree: _.ITree, element: any): boolean { return !!element.children; } - public getChildren(tree, element): Thenable { + public getChildren(tree: _.ITree, element: any): Promise { return Promise.resolve(element.children); } - public getParent(tree, element): Thenable { + public getParent(tree: _.ITree, element: any): Promise { throw new Error('Not implemented'); } } suite('TreeModel', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -313,7 +313,7 @@ suite('TreeModel', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll(['a', 'c']).then(() => { // going internals - var r = (model).registry; + const r = (model).registry; assert(r.getItem('a').intersects(r.getItem('a'))); assert(r.getItem('a').intersects(r.getItem('aa'))); @@ -331,8 +331,8 @@ suite('TreeModel', () => { }); suite('TreeModel - TreeNavigator', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -348,24 +348,24 @@ suite('TreeModel - TreeNavigator', () => { test('next()', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); }); }); test('previous()', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(); + const nav = model.getNavigator(); nav.next(); nav.next(); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'a'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -373,22 +373,22 @@ suite('TreeModel - TreeNavigator', () => { test('parent()', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(); + const nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.parent().id, 'a'); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.parent()!.id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.parent().id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.parent()!.id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.parent().id, 'c'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.parent()!.id, 'c'); assert.equal(nav.parent() && false, null); }); @@ -397,10 +397,10 @@ suite('TreeModel - TreeNavigator', () => { test('next() - scoped', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0]); + const nav = model.getNavigator(SAMPLE.AB.children[0]); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); assert.equal(nav.next() && false, null); }); }); @@ -408,11 +408,11 @@ suite('TreeModel - TreeNavigator', () => { test('previous() - scoped', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0]); + const nav = model.getNavigator(SAMPLE.AB.children[0]); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); assert.equal(nav.previous() && false, null); }); }); @@ -421,10 +421,10 @@ suite('TreeModel - TreeNavigator', () => { test('parent() - scoped', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0]); + const nav = model.getNavigator(SAMPLE.AB.children[0]); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); assert.equal(nav.parent() && false, null); }); }); @@ -432,12 +432,12 @@ suite('TreeModel - TreeNavigator', () => { test('next() - non sub tree only', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0], false); + const nav = model.getNavigator(SAMPLE.AB.children[0], false); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); }); }); @@ -445,16 +445,16 @@ suite('TreeModel - TreeNavigator', () => { test('previous() - non sub tree only', () => { return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0], false); + const nav = model.getNavigator(SAMPLE.AB.children[0], false); return model.expand({ id: 'a' }).then(() => { - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); - assert.equal(nav.previous().id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -463,11 +463,11 @@ suite('TreeModel - TreeNavigator', () => { test('parent() - non sub tree only', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(SAMPLE.AB.children[0], false); + const nav = model.getNavigator(SAMPLE.AB.children[0], false); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.parent().id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.parent()!.id, 'a'); assert.equal(nav.parent() && false, null); }); }); @@ -477,9 +477,9 @@ suite('TreeModel - TreeNavigator', () => { return model.setInput(SAMPLE.DEEP).then(() => { return model.expand(SAMPLE.DEEP.children[0]).then(() => { return model.expand(SAMPLE.DEEP.children[0].children[0]).then(() => { - var nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); - assert.equal(nav.next().id, 'xa'); - assert.equal(nav.next().id, 'xb'); + const nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); + assert.equal(nav.next()!.id, 'xa'); + assert.equal(nav.next()!.id, 'xb'); assert.equal(nav.next() && false, null); }); }); @@ -490,10 +490,10 @@ suite('TreeModel - TreeNavigator', () => { return model.setInput(SAMPLE.DEEP).then(() => { return model.expand(SAMPLE.DEEP.children[0]).then(() => { return model.expand(SAMPLE.DEEP.children[0].children[0]).then(() => { - var nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); - assert.equal(nav.next().id, 'xa'); - assert.equal(nav.next().id, 'xb'); - assert.equal(nav.previous().id, 'xa'); + const nav = model.getNavigator(SAMPLE.DEEP.children[0].children[0]); + assert.equal(nav.next()!.id, 'xa'); + assert.equal(nav.next()!.id, 'xb'); + assert.equal(nav.previous()!.id, 'xa'); assert.equal(nav.previous() && false, null); }); }); @@ -504,15 +504,15 @@ suite('TreeModel - TreeNavigator', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { const nav = model.getNavigator(); - assert.equal(nav.last().id, 'cb'); + assert.equal(nav.last()!.id, 'cb'); }); }); }); }); suite('TreeModel - Expansion', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -530,24 +530,24 @@ suite('TreeModel - Expansion', () => { return model.setInput(SAMPLE.AB).then(() => { counter.listen(model.onExpandItem, (e) => { assert.equal(e.item.id, 'a'); - var nav = model.getNavigator(e.item); + const nav = model.getNavigator(e.item); assert.equal(nav.next() && false, null); }); counter.listen(model.onDidExpandItem, (e) => { assert.equal(e.item.id, 'a'); - var nav = model.getNavigator(e.item); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); + const nav = model.getNavigator(e.item); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); assert.equal(nav.next() && false, null); }); assert(!model.isExpanded(SAMPLE.AB.children[0])); - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); assert.equal(model.getExpandedElements().length, 0); @@ -556,14 +556,14 @@ suite('TreeModel - Expansion', () => { assert(model.isExpanded(SAMPLE.AB.children[0])); nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); - var expandedElements = model.getExpandedElements(); + const expandedElements = model.getExpandedElements(); assert.equal(expandedElements.length, 1); assert.equal(expandedElements[0].id, 'a'); @@ -626,18 +626,18 @@ suite('TreeModel - Expansion', () => { assert(!model.isExpanded(SAMPLE.AB.children[0])); - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); - var f: () => void = counter.listen(model.onRefreshItemChildren, (e) => { + const f: () => void = counter.listen(model.onRefreshItemChildren, (e) => { assert.equal(e.item.id, 'a'); f(); }); - var g: () => void = counter.listen(model.onDidRefreshItemChildren, (e) => { + const g: () => void = counter.listen(model.onDidRefreshItemChildren, (e) => { assert.equal(e.item.id, 'a'); g(); }); @@ -646,11 +646,11 @@ suite('TreeModel - Expansion', () => { assert(model.isExpanded(SAMPLE.AB.children[0])); nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); assert.equal(counter.count, 2); @@ -661,12 +661,12 @@ suite('TreeModel - Expansion', () => { test('top level collapsed', () => { return model.setInput(SAMPLE.AB).then(() => { return model.collapseAll([{ id: 'a' }, { id: 'b' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'a'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -683,7 +683,7 @@ suite('TreeModel - Expansion', () => { if (e === 'b') { return Promise.resolve(['b1']); } return Promise.resolve([]); }, - getParent: (_, e): Thenable => { throw new Error('not implemented'); }, + getParent: (_, e): Promise => { throw new Error('not implemented'); }, shouldAutoexpand: (_, e) => e === 'b' } }); @@ -700,21 +700,21 @@ suite('TreeModel - Expansion', () => { class TestFilter implements _.IFilter { - public fn: (any) => boolean; + public fn: (element: any) => boolean; constructor() { this.fn = () => true; } - public isVisible(tree, element): boolean { + public isVisible(tree: _.ITree, element: any): boolean { return this.fn(element); } } suite('TreeModel - Filter', () => { - var model: model.TreeModel; - var counter: EventCounter; - var filter: TestFilter; + let model: model.TreeModel; + let counter: EventCounter; + let filter: TestFilter; setup(() => { counter = new EventCounter(); @@ -734,21 +734,21 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expandAll([{ id: 'a' }, { id: 'c' }]).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.next().id, 'cb'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.next()!.id, 'cb'); - assert.equal(nav.previous().id, 'ca'); - assert.equal(nav.previous().id, 'c'); - assert.equal(nav.previous().id, 'b'); - assert.equal(nav.previous().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); - assert.equal(nav.previous().id, 'a'); + assert.equal(nav.previous()!.id, 'ca'); + assert.equal(nav.previous()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); + assert.equal(nav.previous()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -759,7 +759,7 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.refresh().then(() => { - var nav = model.getNavigator(); + const nav = model.getNavigator(); assert.equal(nav.next() && false, null); }); }); @@ -772,12 +772,12 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'a' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'ab'); - assert.equal(nav.previous().id, 'aa'); - assert.equal(nav.previous().id, 'a'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'ab'); + assert.equal(nav.previous()!.id, 'aa'); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); @@ -789,11 +789,11 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'a' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'a'); - assert.equal(nav.next().id, 'aa'); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'a'); + assert.equal(nav.next()!.id, 'aa'); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); assert.equal(nav.next() && false, null); }); }); @@ -806,14 +806,14 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'c' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.next().id, 'cb'); - assert.equal(nav.previous().id, 'ca'); - assert.equal(nav.previous().id, 'c'); - assert.equal(nav.previous().id, 'b'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.next()!.id, 'cb'); + assert.equal(nav.previous()!.id, 'ca'); + assert.equal(nav.previous()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); assert.equal(nav.previous() && false, null); }); }); @@ -826,14 +826,14 @@ suite('TreeModel - Filter', () => { return model.setInput(SAMPLE.AB).then(() => { return model.expand({ id: 'c' }).then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'b'); - assert.equal(nav.next().id, 'c'); - assert.equal(nav.next().id, 'ca'); - assert.equal(nav.next().id, 'cb'); - assert.equal(nav.previous().id, 'ca'); - assert.equal(nav.previous().id, 'c'); - assert.equal(nav.previous().id, 'b'); + const nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'b'); + assert.equal(nav.next()!.id, 'c'); + assert.equal(nav.next()!.id, 'ca'); + assert.equal(nav.next()!.id, 'cb'); + assert.equal(nav.previous()!.id, 'ca'); + assert.equal(nav.previous()!.id, 'c'); + assert.equal(nav.previous()!.id, 'b'); assert.equal(nav.previous() && false, null); }); }); @@ -844,16 +844,16 @@ suite('TreeModel - Filter', () => { filter.fn = (e) => e.id !== 'b'; return model.setInput(SAMPLE.AB).then(() => { - var nav = model.getNavigator({ id: 'c' }, false); - assert.equal(nav.previous().id, 'a'); + const nav = model.getNavigator({ id: 'c' }, false); + assert.equal(nav.previous()!.id, 'a'); assert.equal(nav.previous() && false, null); }); }); }); suite('TreeModel - Traits', () => { - var model: model.TreeModel; - var counter: EventCounter; + let model: model.TreeModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -1079,7 +1079,7 @@ suite('TreeModel - Traits', () => { class DynamicModel implements _.IDataSource { private data: any; - public promiseFactory: { (): Thenable; }; + public promiseFactory: { (): Promise; } | null; private _onGetChildren = new Emitter(); readonly onGetChildren: Event = this._onGetChildren.event; @@ -1092,56 +1092,56 @@ class DynamicModel implements _.IDataSource { this.promiseFactory = null; } - public addChild(parent, child): void { + public addChild(parent: string, child: string): void { if (!this.data[parent]) { this.data[parent] = []; } this.data[parent].push(child); } - public removeChild(parent, child): void { + public removeChild(parent: string, child: string): void { this.data[parent].splice(this.data[parent].indexOf(child), 1); if (this.data[parent].length === 0) { delete this.data[parent]; } } - public move(element, oldParent, newParent): void { + public move(element: string, oldParent: string, newParent: string): void { this.removeChild(oldParent, element); this.addChild(newParent, element); } - public rename(parent, oldName, newName): void { + public rename(parent: string, oldName: string, newName: string): void { this.removeChild(parent, oldName); this.addChild(parent, newName); } - public getId(tree, element): string { + public getId(tree: _.ITree, element: any): string { return element; } - public hasChildren(tree, element): boolean { + public hasChildren(tree: _.ITree, element: any): boolean { return !!this.data[element]; } - public getChildren(tree, element): Thenable { + public getChildren(tree: _.ITree, element: any): Promise { this._onGetChildren.fire(element); - var result = this.promiseFactory ? this.promiseFactory() : Promise.resolve(null); + const result = this.promiseFactory ? this.promiseFactory() : Promise.resolve(null); return result.then(() => { this._onDidGetChildren.fire(element); return Promise.resolve(this.data[element]); }); } - public getParent(tree, element): Thenable { + public getParent(tree: _.ITree, element: any): Promise { throw new Error('Not implemented'); } } suite('TreeModel - Dynamic data model', () => { - var model: model.TreeModel; - var dataModel: DynamicModel; - var counter: EventCounter; + let model: model.TreeModel; + let dataModel: DynamicModel; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -1167,8 +1167,8 @@ suite('TreeModel - Dynamic data model', () => { return model.expandAll(['grandfather', 'father', 'son']).then(() => { dataModel.removeChild('grandfather', 'father'); - var items = ['baby', 'son', 'daughter', 'father']; - var times = 0; + const items = ['baby', 'son', 'daughter', 'father']; + let times = 0; counter.listen(model.onDidDisposeItem, item => { assert.equal(items[times++], item.id); }); @@ -1187,17 +1187,17 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('root', 'mega'); return model.setInput('root').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'hyper'); - assert.equal(nav.next().id, 'mega'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'hyper'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); dataModel.removeChild('root', 'hyper'); return model.refresh().then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'mega'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); dataModel.addChild('mega', 'micro'); @@ -1207,17 +1207,17 @@ suite('TreeModel - Dynamic data model', () => { return model.refresh().then(() => { return model.expand('mega').then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'mega'); - assert.equal(nav.next().id, 'micro'); - assert.equal(nav.next().id, 'nano'); - assert.equal(nav.next().id, 'pico'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'mega'); + assert.equal(nav.next()!.id, 'micro'); + assert.equal(nav.next()!.id, 'nano'); + assert.equal(nav.next()!.id, 'pico'); assert.equal(nav.next() && false, null); model.collapse('mega'); nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'mega'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); }); }); @@ -1237,13 +1237,13 @@ suite('TreeModel - Dynamic data model', () => { return model.expand('super').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'apples'); - assert.equal(nav.next().id, 'bananas'); - assert.equal(nav.next().id, 'pears'); - assert.equal(nav.next().id, 'hyper'); - assert.equal(nav.next().id, 'mega'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'apples'); + assert.equal(nav.next()!.id, 'bananas'); + assert.equal(nav.next()!.id, 'pears'); + assert.equal(nav.next()!.id, 'hyper'); + assert.equal(nav.next()!.id, 'mega'); assert.equal(nav.next() && false, null); dataModel.move('bananas', 'super', 'hyper'); @@ -1253,12 +1253,12 @@ suite('TreeModel - Dynamic data model', () => { return model.expandAll(['hyper', 'mega']).then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'super'); - assert.equal(nav.next().id, 'pears'); - assert.equal(nav.next().id, 'hyper'); - assert.equal(nav.next().id, 'bananas'); - assert.equal(nav.next().id, 'mega'); - assert.equal(nav.next().id, 'apples'); + assert.equal(nav.next()!.id, 'super'); + assert.equal(nav.next()!.id, 'pears'); + assert.equal(nav.next()!.id, 'hyper'); + assert.equal(nav.next()!.id, 'bananas'); + assert.equal(nav.next()!.id, 'mega'); + assert.equal(nav.next()!.id, 'apples'); assert.equal(nav.next() && false, null); }); }); @@ -1274,8 +1274,8 @@ suite('TreeModel - Dynamic data model', () => { return model.setInput('root').then(() => { return model.expand('grandfather').then(() => { return model.collapse('father').then(() => { - var times = 0; - var listener = dataModel.onGetChildren((element) => { + let times = 0; + let listener = dataModel.onGetChildren((element) => { times++; assert.equal(element, 'grandfather'); }); @@ -1309,11 +1309,11 @@ suite('TreeModel - Dynamic data model', () => { return model.expand('father').then(() => { return model.expand('mother').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); - assert.equal(nav.next().id, 'mother'); - assert.equal(nav.next().id, 'daughter'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); + assert.equal(nav.next()!.id, 'mother'); + assert.equal(nav.next()!.id, 'daughter'); assert.equal(nav.next() && false, null); dataModel.removeChild('father', 'son'); @@ -1323,16 +1323,16 @@ suite('TreeModel - Dynamic data model', () => { dataModel.promiseFactory = () => { return timeout(0); }; - var getTimes = 0; - var gotTimes = 0; - var getListener = dataModel.onGetChildren((element) => { getTimes++; }); - var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); + let getTimes = 0; + let gotTimes = 0; + const getListener = dataModel.onGetChildren((element) => { getTimes++; }); + const gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - var p1 = model.refresh('father'); + const p1 = model.refresh('father'); assert.equal(getTimes, 1); assert.equal(gotTimes, 0); - var p2 = model.refresh('mother'); + const p2 = model.refresh('mother'); assert.equal(getTimes, 2); assert.equal(gotTimes, 0); @@ -1341,10 +1341,10 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(gotTimes, 2); nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'brother'); - assert.equal(nav.next().id, 'mother'); - assert.equal(nav.next().id, 'sister'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'brother'); + assert.equal(nav.next()!.id, 'mother'); + assert.equal(nav.next()!.id, 'sister'); assert.equal(nav.next() && false, null); getListener.dispose(); @@ -1363,22 +1363,22 @@ suite('TreeModel - Dynamic data model', () => { return model.setInput('root').then(() => { return model.expand('grandfather').then(() => { return model.expand('father').then(() => { - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); + let nav = model.getNavigator(); + assert.equal(nav.next()!.id, 'grandfather'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); assert.equal(nav.next() && false, null); - var refreshTimes = 0; + let refreshTimes = 0; counter.listen(model.onDidRefreshItem, (e) => { refreshTimes++; }); - var getTimes = 0; - var getListener = dataModel.onGetChildren((element) => { getTimes++; }); + let getTimes = 0; + const getListener = dataModel.onGetChildren((element) => { getTimes++; }); - var gotTimes = 0; - var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); + let gotTimes = 0; + const gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - var p1Completes = []; + const p1Completes: Array<(value?: any) => void> = []; dataModel.promiseFactory = () => { return new Promise((c) => { p1Completes.push(c); }); }; model.refresh('grandfather').then(() => { @@ -1388,16 +1388,16 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(gotTimes, 0); // unblock the first get - p1Completes.shift()(); + p1Completes.shift()!(); // once the first get is unblocked, the second get should appear assert.equal(refreshTimes, 2); // (+1) first father refresh assert.equal(getTimes, 2); assert.equal(gotTimes, 1); - var p2Complete; + let p2Complete: () => void; dataModel.promiseFactory = () => { return new Promise((c) => { p2Complete = c; }); }; - var p2 = model.refresh('father'); + const p2 = model.refresh('father'); // same situation still assert.equal(refreshTimes, 3); // (+1) second father refresh @@ -1405,14 +1405,14 @@ suite('TreeModel - Dynamic data model', () => { assert.equal(gotTimes, 1); // unblock the second get - p1Completes.shift()(); + p1Completes.shift()!(); // the third get should have appeared, it should've been waiting for the second one assert.equal(refreshTimes, 4); // (+1) first son request assert.equal(getTimes, 3); assert.equal(gotTimes, 2); - p2Complete(); + p2Complete!(); // all good assert.equal(refreshTimes, 5); // (+1) second son request @@ -1421,9 +1421,9 @@ suite('TreeModel - Dynamic data model', () => { return p2.then(() => { nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); + assert.equal(nav.next()!.id, 'grandfather'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); assert.equal(nav.next() && false, null); getListener.dispose(); @@ -1529,7 +1529,7 @@ suite('TreeModel - Dynamic data model', () => { // delay expansions and refreshes dataModel.promiseFactory = () => { return timeout(0); }; - var promises: Thenable[] = []; + const promises: Promise[] = []; promises.push(model.expand('father')); dataModel.removeChild('root', 'father'); @@ -1549,7 +1549,7 @@ suite('TreeModel - Dynamic data model', () => { }); suite('TreeModel - bugs', () => { - var counter: EventCounter; + let counter: EventCounter; setup(() => { counter = new EventCounter(); @@ -1573,17 +1573,17 @@ suite('TreeModel - bugs', () => { if (e === 'bart') { return getBartChildren(); } return Promise.resolve([]); }, - getParent: (_, e): Thenable => { throw new Error('not implemented'); }, + getParent: (_, e): Promise => { throw new Error('not implemented'); }, } }); let listeners = []; // helpers - var getGetRootChildren = (children: string[], millis = 0) => () => timeout(millis).then(() => children); - var getRootChildren = getGetRootChildren(['homer', 'bart', 'lisa', 'marge', 'maggie'], 0); - var getGetBartChildren = (millis = 0) => () => timeout(millis).then(() => ['milhouse', 'nelson']); - var getBartChildren = getGetBartChildren(0); + const getGetRootChildren = (children: string[], millis = 0) => () => timeout(millis).then(() => children); + let getRootChildren = getGetRootChildren(['homer', 'bart', 'lisa', 'marge', 'maggie'], 0); + const getGetBartChildren = (millis = 0) => () => timeout(millis).then(() => ['milhouse', 'nelson']); + const getBartChildren = getGetBartChildren(0); // item expanding should not exist! counter.listen(model.onExpandItem, () => { assert(false, 'should never receive item:expanding event'); }); @@ -1595,14 +1595,14 @@ suite('TreeModel - bugs', () => { getRootChildren = getGetRootChildren(['homer', 'lisa', 'marge', 'maggie'], 10); // refresh root - var p1 = model.refresh('root', true).then(() => { + const p1 = model.refresh('root', true).then(() => { assert(true); }, () => { assert(false, 'should never reach this'); }); // at the same time, try to expand bart! - var p2 = model.expand('bart').then(() => { + const p2 = model.expand('bart').then(() => { assert(false, 'should never reach this'); }, () => { assert(true, 'bart should fail to expand since he was removed meanwhile'); @@ -1617,7 +1617,6 @@ suite('TreeModel - bugs', () => { while (listeners.length > 0) { listeners.pop()(); } listeners = null; model.dispose(); - model = null; assert.equal(counter.count, 0); }); @@ -1643,8 +1642,8 @@ suite('TreeModel - bugs', () => { await model.expand('father'); let nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); + assert.equal(nav.next()!.id, 'father'); + assert.equal(nav.next()!.id, 'son'); assert.equal(nav.next(), null); await model.collapse('father'); @@ -1654,7 +1653,7 @@ suite('TreeModel - bugs', () => { await model.expand('father'); nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); + assert.equal(nav.next()!.id, 'father'); assert.equal(nav.next(), null); counter.dispose(); diff --git a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts index 0f0a1f81644..97c2846ebb6 100644 --- a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { ArrayIterator } from 'vs/base/common/iterator'; import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; -function makeItem(id, height): any { +function makeItem(id: any, height: any): any { return { id: id, getHeight: function () { return height; }, @@ -17,9 +17,9 @@ function makeItem(id, height): any { } function makeItems(...args: any[]) { - var r = []; + let r: any[] = []; - for (var i = 0; i < args.length; i += 2) { + for (let i = 0; i < args.length; i += 2) { r.push(makeItem(args[i], args[i + 1])); } @@ -27,8 +27,8 @@ function makeItems(...args: any[]) { } function makeNavigator(...args: any[]): any { - var items = makeItems.apply(null, args); - var i = 0; + let items = makeItems.apply(null, args); + let i = 0; return { next: function () { @@ -50,7 +50,7 @@ class TestHeightMap extends HeightMap { } suite('TreeView - HeightMap', () => { - var rangeMap: HeightMap; + let rangeMap: HeightMap; setup(() => { rangeMap = new TestHeightMap(); @@ -59,7 +59,7 @@ suite('TreeView - HeightMap', () => { teardown(() => { rangeMap.dispose(); - rangeMap = null; + rangeMap = null!; }); test('simple', () => { @@ -76,7 +76,7 @@ suite('TreeView - HeightMap', () => { }); test('onInsertItems at beginning', () => { - var navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); + let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); rangeMap.onInsertItems(navigator); assert.equal(rangeMap.itemAt(0), 'x'); @@ -97,7 +97,7 @@ suite('TreeView - HeightMap', () => { }); test('onInsertItems in middle', () => { - var navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); + let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); rangeMap.onInsertItems(navigator, 'a'); assert.equal(rangeMap.itemAt(0), 'a'); @@ -118,7 +118,7 @@ suite('TreeView - HeightMap', () => { }); test('onInsertItems at end', () => { - var navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); + let navigator = makeNavigator('x', 4, 'y', 20, 'z', 8); rangeMap.onInsertItems(navigator, 'd'); assert.equal(rangeMap.itemAt(0), 'a'); @@ -171,7 +171,7 @@ suite('TreeView - HeightMap', () => { }); test('onRefreshItems at beginning', () => { - var navigator = makeNavigator('a', 1, 'b', 1); + let navigator = makeNavigator('a', 1, 'b', 1); rangeMap.onRefreshItems(navigator); assert.equal(rangeMap.itemAt(0), 'a'); @@ -184,7 +184,7 @@ suite('TreeView - HeightMap', () => { }); test('onRefreshItems in middle', () => { - var navigator = makeNavigator('b', 40, 'c', 4); + let navigator = makeNavigator('b', 40, 'c', 4); rangeMap.onRefreshItems(navigator); assert.equal(rangeMap.itemAt(0), 'a'); @@ -199,7 +199,7 @@ suite('TreeView - HeightMap', () => { }); test('onRefreshItems at end', () => { - var navigator = makeNavigator('d', 22); + let navigator = makeNavigator('d', 22); rangeMap.onRefreshItems(navigator); assert.equal(rangeMap.itemAt(0), 'a'); @@ -214,8 +214,8 @@ suite('TreeView - HeightMap', () => { }); test('withItemsInRange', () => { - var i = 0; - var itemsInRange = ['a', 'b']; + let i = 0; + let itemsInRange = ['a', 'b']; rangeMap.withItemsInRange(2, 27, function (item) { assert.equal(item, itemsInRange[i++]); }); assert.equal(i, itemsInRange.length); diff --git a/src/vs/base/test/browser/dom.test.ts b/src/vs/base/test/browser/dom.test.ts index dd5f75b164d..61252159d23 100644 --- a/src/vs/base/test/browser/dom.test.ts +++ b/src/vs/base/test/browser/dom.test.ts @@ -97,18 +97,19 @@ suite('dom', () => { let div = $('div', { class: 'test' }); assert.equal(div.className, 'test'); - div = $('div', null); + div = $('div', undefined); assert.equal(div.className, ''); }); test('should build nodes with children', () => { - let div = $('div', null, $('span', { id: 'demospan' })); + let div = $('div', undefined, $('span', { id: 'demospan' })); let firstChild = div.firstChild as HTMLElement; assert.equal(firstChild.tagName, 'SPAN'); assert.equal(firstChild.id, 'demospan'); - div = $('div', null, 'hello'); - assert.equal(div.firstChild.textContent, 'hello'); + div = $('div', undefined, 'hello'); + + assert.equal(div.firstChild && div.firstChild.textContent, 'hello'); }); }); }); diff --git a/src/vs/base/test/browser/hash.test.ts b/src/vs/base/test/browser/hash.test.ts new file mode 100644 index 00000000000..adbedc411f1 --- /dev/null +++ b/src/vs/base/test/browser/hash.test.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as assert from 'assert'; +import { createSHA1 } from 'vs/base/browser/hash'; + +suite('Hash', () => { + test('computeSHA1Hash', async () => { + assert.equal(await createSHA1(''), 'da39a3ee5e6b4b0d3255bfef95601890afd80709'); + assert.equal(await createSHA1('hello world'), '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'); + assert.equal(await createSHA1('da39a3ee5e6b4b0d3255bfef95601890afd80709'), '10a34637ad661d98ba3344717656fcc76209c2f8'); + assert.equal(await createSHA1('2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'), 'd6b0d82cea4269b51572b8fab43adcee9fc3cf9a'); + assert.equal(await createSHA1('öäü_?ß()<>ÖÄÜ'), 'b64beaeff9e317b0193c8e40a2431b210388eba9'); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index 30105b727ec..c658c835c04 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -12,11 +12,6 @@ suite('HighlightedLabel', () => { label = new HighlightedLabel(document.createElement('div'), true); }); - teardown(() => { - label.dispose(); - label = null; - }); - test('empty label', function () { assert.equal(label.element.innerHTML, ''); }); diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/htmlContent.test.ts index 20db9845e0a..6035010b3b4 100644 --- a/src/vs/base/test/browser/htmlContent.test.ts +++ b/src/vs/base/test/browser/htmlContent.test.ts @@ -8,7 +8,7 @@ import { renderMarkdown, renderText, renderFormattedText } from 'vs/base/browser suite('HtmlContent', () => { test('render simple element', () => { - var result: HTMLElement = renderText('testing'); + let result: HTMLElement = renderText('testing'); assert.strictEqual(result.nodeType, document.ELEMENT_NODE); assert.strictEqual(result.textContent, 'testing'); @@ -16,7 +16,7 @@ suite('HtmlContent', () => { }); test('render element with class', () => { - var result: HTMLElement = renderText('testing', { + let result: HTMLElement = renderText('testing', { className: 'testClass' }); assert.strictEqual(result.nodeType, document.ELEMENT_NODE); @@ -24,9 +24,9 @@ suite('HtmlContent', () => { }); test('simple formatting', () => { - var result: HTMLElement = renderFormattedText('**bold**'); + let result: HTMLElement = renderFormattedText('**bold**'); assert.strictEqual(result.children.length, 1); - assert.strictEqual(result.firstChild.textContent, 'bold'); + assert.strictEqual(result.firstChild!.textContent, 'bold'); assert.strictEqual((result.firstChild).tagName, 'B'); assert.strictEqual(result.innerHTML, 'bold'); @@ -38,18 +38,18 @@ suite('HtmlContent', () => { }); test('no formatting', () => { - var result: HTMLElement = renderFormattedText('this is just a string'); + let result: HTMLElement = renderFormattedText('this is just a string'); assert.strictEqual(result.innerHTML, 'this is just a string'); }); test('preserve newlines', () => { - var result: HTMLElement = renderFormattedText('line one\nline two'); + let result: HTMLElement = renderFormattedText('line one\nline two'); assert.strictEqual(result.innerHTML, 'line one
line two'); }); test('action', () => { - var callbackCalled = false; - var result: HTMLElement = renderFormattedText('[[action]]', { + let callbackCalled = false; + let result: HTMLElement = renderFormattedText('[[action]]', { actionHandler: { callback(content) { assert.strictEqual(content, '0'); @@ -60,15 +60,15 @@ suite('HtmlContent', () => { }); assert.strictEqual(result.innerHTML, 'action'); - var event: MouseEvent = document.createEvent('MouseEvent'); + let event: MouseEvent = document.createEvent('MouseEvent'); event.initEvent('click', true, true); - result.firstChild.dispatchEvent(event); + result.firstChild!.dispatchEvent(event); assert.strictEqual(callbackCalled, true); }); test('fancy action', () => { - var callbackCalled = false; - var result: HTMLElement = renderFormattedText('__**[[action]]**__', { + let callbackCalled = false; + let result: HTMLElement = renderFormattedText('__**[[action]]**__', { actionHandler: { callback(content) { assert.strictEqual(content, '0'); @@ -79,14 +79,14 @@ suite('HtmlContent', () => { }); assert.strictEqual(result.innerHTML, 'action'); - var event: MouseEvent = document.createEvent('MouseEvent'); + let event: MouseEvent = document.createEvent('MouseEvent'); event.initEvent('click', true, true); - result.firstChild.firstChild.firstChild.dispatchEvent(event); + result.firstChild!.firstChild!.firstChild!.dispatchEvent(event); assert.strictEqual(callbackCalled, true); }); test('escaped formatting', () => { - var result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); + let result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); assert.strictEqual(result.children.length, 0); assert.strictEqual(result.innerHTML, '**bold**'); }); @@ -111,15 +111,15 @@ suite('HtmlContent', () => { assert.strictEqual(result.innerHTML, imageFromMarked); }); test('image width from title params', () => { - var result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); test('image height from title params', () => { - var result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); test('image width and height from title params', () => { - var result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); }); diff --git a/src/vs/base/test/browser/ui/grid/grid.test.ts b/src/vs/base/test/browser/ui/grid/grid.test.ts index b6a14909bde..7385b3ca4d6 100644 --- a/src/vs/base/test/browser/ui/grid/grid.test.ts +++ b/src/vs/base/test/browser/ui/grid/grid.test.ts @@ -18,10 +18,6 @@ suite('Grid', function () { container.style.height = `${600}px`; }); - teardown(function () { - container = null; - }); - test('getRelativeLocation', () => { assert.deepEqual(getRelativeLocation(Orientation.VERTICAL, [0], Direction.Up), [0]); assert.deepEqual(getRelativeLocation(Orientation.VERTICAL, [0], Direction.Down), [1]); @@ -473,7 +469,11 @@ class TestViewDeserializer implements IViewDeserializer { } getView(id: string): TestSerializableView { - return this.views.get(id); + const view = this.views.get(id); + if (!view) { + throw new Error('Unknown view'); + } + return view; } } @@ -496,10 +496,6 @@ suite('SerializableGrid', function () { container.style.height = `${600}px`; }); - teardown(function () { - container = null; - }); - test('serialize empty', function () { const view1 = new TestSerializableView('view1', 50, Number.MAX_VALUE, 50, Number.MAX_VALUE); const grid = new SerializableGrid(view1); diff --git a/src/vs/base/test/browser/ui/grid/gridview.test.ts b/src/vs/base/test/browser/ui/grid/gridview.test.ts index 3485fa63b31..3d000d85a67 100644 --- a/src/vs/base/test/browser/ui/grid/gridview.test.ts +++ b/src/vs/base/test/browser/ui/grid/gridview.test.ts @@ -21,10 +21,6 @@ suite('Gridview', function () { container.appendChild(gridview.element); }); - teardown(function () { - gridview = null; - }); - test('empty gridview is empty', function () { assert.deepEqual(nodesToArrays(gridview.getViews()), []); gridview.dispose(); diff --git a/src/vs/base/test/browser/ui/grid/util.ts b/src/vs/base/test/browser/ui/grid/util.ts index 7c4912b3101..0efdc44851a 100644 --- a/src/vs/base/test/browser/ui/grid/util.ts +++ b/src/vs/base/test/browser/ui/grid/util.ts @@ -9,20 +9,20 @@ import { IView, GridNode, isGridBranchNode, } from 'vs/base/browser/ui/grid/grid export class TestView implements IView { - private _onDidChange = new Emitter<{ width: number; height: number; }>(); + private _onDidChange = new Emitter<{ width: number; height: number; } | undefined>(); readonly onDidChange = this._onDidChange.event; get minimumWidth(): number { return this._minimumWidth; } - set minimumWidth(size: number) { this._minimumWidth = size; this._onDidChange.fire(); } + set minimumWidth(size: number) { this._minimumWidth = size; this._onDidChange.fire(undefined); } get maximumWidth(): number { return this._maximumWidth; } - set maximumWidth(size: number) { this._maximumWidth = size; this._onDidChange.fire(); } + set maximumWidth(size: number) { this._maximumWidth = size; this._onDidChange.fire(undefined); } get minimumHeight(): number { return this._minimumHeight; } - set minimumHeight(size: number) { this._minimumHeight = size; this._onDidChange.fire(); } + set minimumHeight(size: number) { this._minimumHeight = size; this._onDidChange.fire(undefined); } get maximumHeight(): number { return this._maximumHeight; } - set maximumHeight(size: number) { this._maximumHeight = size; this._onDidChange.fire(); } + set maximumHeight(size: number) { this._maximumHeight = size; this._onDidChange.fire(undefined); } private _element: HTMLElement = document.createElement('div'); get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; } diff --git a/src/vs/base/test/browser/ui/list/listView.test.ts b/src/vs/base/test/browser/ui/list/listView.test.ts index 997b336f3cb..99f9bac73cf 100644 --- a/src/vs/base/test/browser/ui/list/listView.test.ts +++ b/src/vs/base/test/browser/ui/list/listView.test.ts @@ -25,7 +25,6 @@ suite('ListView', function () { templateId: 'template', renderTemplate() { templatesCount++; }, renderElement() { }, - disposeElement() { }, disposeTemplate() { templatesCount--; } }; diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index 3e1aa10693b..819bd312bab 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -8,7 +8,7 @@ import { RangeMap, groupIntersect, consolidate } from 'vs/base/browser/ui/list/r import { Range } from 'vs/base/common/range'; suite('RangeMap', () => { - var rangeMap: RangeMap; + let rangeMap: RangeMap; setup(() => { rangeMap = new RangeMap(); diff --git a/src/vs/base/test/browser/ui/menu/menubar.test.ts b/src/vs/base/test/browser/ui/menu/menubar.test.ts new file mode 100644 index 00000000000..8bbeddcfc58 --- /dev/null +++ b/src/vs/base/test/browser/ui/menu/menubar.test.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { $ } from 'vs/base/browser/dom'; +import { MenuBar } from 'vs/base/browser/ui/menu/menubar'; + +function getButtonElementByAriaLabel(menubarElement: HTMLElement, ariaLabel: string): HTMLElement | null { + let i; + for (i = 0; i < menubarElement.childElementCount; i++) { + + if (menubarElement.children[i].getAttribute('aria-label') === ariaLabel) { + return menubarElement.children[i] as HTMLElement; + } + } + + return null; +} + +function getTitleDivFromButtonDiv(menuButtonElement: HTMLElement): HTMLElement | null { + let i; + for (i = 0; i < menuButtonElement.childElementCount; i++) { + if (menuButtonElement.children[i].classList.contains('menubar-menu-title')) { + return menuButtonElement.children[i] as HTMLElement; + } + } + + return null; +} + +function getMnemonicFromTitleDiv(menuTitleDiv: HTMLElement): string | null { + let i; + for (i = 0; i < menuTitleDiv.childElementCount; i++) { + if (menuTitleDiv.children[i].tagName.toLocaleLowerCase() === 'mnemonic') { + return menuTitleDiv.children[i].textContent; + } + } + + return null; +} + +function validateMenuBarItem(menubar: MenuBar, menubarContainer: HTMLElement, label: string, readableLabel: string, mnemonic: string) { + menubar.push([ + { + actions: [], + label: label + } + ]); + + const buttonElement = getButtonElementByAriaLabel(menubarContainer, readableLabel); + assert(buttonElement !== null, `Button element not found for ${readableLabel} button.`); + + const titleDiv = getTitleDivFromButtonDiv(buttonElement!); + assert(titleDiv !== null, `Title div not found for ${readableLabel} button.`); + + const mnem = getMnemonicFromTitleDiv(titleDiv!); + assert.equal(mnem, mnemonic, 'Mnemonic not correct'); +} + +suite('Menubar', () => { + const container = $('.container'); + + const menubar = new MenuBar(container, { + enableMnemonics: true, + visibility: 'visible' + }); + + test('English File menu renders mnemonics', function () { + validateMenuBarItem(menubar, container, '&File', 'File', 'F'); + }); + + test('Russian File menu renders mnemonics', function () { + validateMenuBarItem(menubar, container, '&Файл', 'Файл', 'Ф'); + }); + + test('Chinese File menu renders mnemonics', function () { + validateMenuBarItem(menubar, container, '文件(&F)', '文件', 'F'); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 185c9f25aa4..00c698c79cb 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -14,10 +14,10 @@ class TestView implements IView { readonly onDidChange = this._onDidChange.event; get minimumSize(): number { return this._minimumSize; } - set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(); } + set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(undefined); } get maximumSize(): number { return this._maximumSize; } - set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(); } + set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(undefined); } private _element: HTMLElement = document.createElement('div'); get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; } @@ -59,7 +59,7 @@ class TestView implements IView { } function getSashes(splitview: SplitView): Sash[] { - return (splitview as any).sashItems.map(i => i.sash) as Sash[]; + return (splitview as any).sashItems.map((i: any) => i.sash) as Sash[]; } suite('Splitview', () => { @@ -72,13 +72,9 @@ suite('Splitview', () => { container.style.height = `${200}px`; }); - teardown(() => { - container = null; - }); - test('empty splitview has empty DOM', () => { const splitview = new SplitView(container); - assert.equal(container.firstElementChild.firstElementChild.childElementCount, 0, 'split view should be empty'); + assert.equal(container.firstElementChild!.firstElementChild!.childElementCount, 0, 'split view should be empty'); splitview.dispose(); }); @@ -135,7 +131,7 @@ suite('Splitview', () => { let didLayout = false; const layoutDisposable = view.onDidLayout(() => didLayout = true); - const renderDisposable = view.onDidGetElement(() => void 0); + const renderDisposable = view.onDidGetElement(() => undefined); splitview.addView(view, 20); diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts new file mode 100644 index 00000000000..3c934126df9 --- /dev/null +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -0,0 +1,329 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITreeNode, ITreeRenderer, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { hasClass } from 'vs/base/browser/dom'; + +interface Element { + id: string; + children?: Element[]; +} + +function find(elements: Element[] | undefined, id: string): Element { + while (elements) { + for (const element of elements) { + if (element.id === id) { + return element; + } + } + } + + throw new Error('element not found'); +} + +suite('AsyncDataTree', function () { + + test('Collapse state should be preserved across refresh calls', async () => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(element: Element): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = element.element.id; + } + disposeTemplate(templateData: HTMLElement): void { + // noop + } + }; + + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + return Promise.resolve(element.children || []); + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: Element) { + return element.id; + } + }; + + const root: Element = { + id: 'root', + children: [{ + id: 'a' + }] + }; + + const _: (id: string) => Element = find.bind(null, root.children); + + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); + tree.layout(200); + assert.equal(container.querySelectorAll('.monaco-list-row').length, 0); + + await tree.setInput(root); + assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); + let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(!hasClass(twistie, 'collapsible')); + assert(!hasClass(twistie, 'collapsed')); + + _('a').children = [ + { id: 'aa' }, + { id: 'ab' }, + { id: 'ac' } + ]; + + await tree.updateChildren(root); + assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); + + await tree.expand(_('a')); + assert.equal(container.querySelectorAll('.monaco-list-row').length, 4); + + _('a').children = []; + await tree.updateChildren(root); + assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); + }); + + test('issue #68648', async () => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(element: Element): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = element.element.id; + } + disposeTemplate(templateData: HTMLElement): void { + // noop + } + }; + + const getChildrenCalls: string[] = []; + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + getChildrenCalls.push(element.id); + return Promise.resolve(element.children || []); + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: Element) { + return element.id; + } + }; + + const root: Element = { + id: 'root', + children: [{ + id: 'a' + }] + }; + + const _: (id: string) => Element = find.bind(null, root.children); + + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); + tree.layout(200); + + await tree.setInput(root); + assert.deepStrictEqual(getChildrenCalls, ['root']); + + let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(!hasClass(twistie, 'collapsible')); + assert(!hasClass(twistie, 'collapsed')); + assert(tree.getNode().children[0].collapsed); + + _('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]; + await tree.updateChildren(root); + + assert.deepStrictEqual(getChildrenCalls, ['root', 'root']); + twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(hasClass(twistie, 'collapsible')); + assert(hasClass(twistie, 'collapsed')); + assert(tree.getNode().children[0].collapsed); + + _('a').children = []; + await tree.updateChildren(root); + + assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root']); + twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(!hasClass(twistie, 'collapsible')); + assert(!hasClass(twistie, 'collapsed')); + assert(tree.getNode().children[0].collapsed); + + _('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]; + await tree.updateChildren(root); + + assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root', 'root']); + twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(hasClass(twistie, 'collapsible')); + assert(hasClass(twistie, 'collapsed')); + assert(tree.getNode().children[0].collapsed); + }); + + test('issue #67722 - once resolved, refreshed collapsed nodes should only get children when expanded', async () => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(element: Element): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = element.element.id; + } + disposeTemplate(templateData: HTMLElement): void { + // noop + } + }; + + const getChildrenCalls: string[] = []; + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + getChildrenCalls.push(element.id); + return Promise.resolve(element.children || []); + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: Element) { + return element.id; + } + }; + + const root: Element = { + id: 'root', + children: [{ + id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }] + }] + }; + + const _: (id: string) => Element = find.bind(null, root.children); + + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); + tree.layout(200); + + await tree.setInput(root); + assert(tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root']); + + await tree.expand(_('a')); + assert(!tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root', 'a']); + + tree.collapse(_('a')); + assert(tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root', 'a']); + + await tree.updateChildren(); + assert(tree.getNode(_('a')).collapsed); + assert.deepStrictEqual(getChildrenCalls, ['root', 'a', 'root'], 'a should not be refreshed, since it\' collapsed'); + }); + + test('resolved collapsed nodes which lose children should lose twistie as well', async () => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(element: Element): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = element.element.id; + } + disposeTemplate(templateData: HTMLElement): void { + // noop + } + }; + + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + return Promise.resolve(element.children || []); + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: Element) { + return element.id; + } + }; + + const root: Element = { + id: 'root', + children: [{ + id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }] + }] + }; + + const _: (id: string) => Element = find.bind(null, root.children); + + const tree = new AsyncDataTree(container, delegate, [renderer], dataSource, { identityProvider }); + tree.layout(200); + + await tree.setInput(root); + await tree.expand(_('a')); + + let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(hasClass(twistie, 'collapsible')); + assert(!hasClass(twistie, 'collapsed')); + assert(!tree.getNode(_('a')).collapsed); + + tree.collapse(_('a')); + _('a').children = []; + await tree.updateChildren(root); + + twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement; + assert(!hasClass(twistie, 'collapsible')); + assert(!hasClass(twistie, 'collapsed')); + assert(tree.getNode(_('a')).collapsed); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/tree/dataTree.test.ts b/src/vs/base/test/browser/ui/tree/dataTree.test.ts new file mode 100644 index 00000000000..fee529b745e --- /dev/null +++ b/src/vs/base/test/browser/ui/tree/dataTree.test.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITreeNode, ITreeRenderer, IDataSource } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { DataTree } from 'vs/base/browser/ui/tree/dataTree'; + +interface E { + value: number; + children?: E[]; +} + +suite('DataTree', function () { + let tree: DataTree; + + const root: E = { + value: -1, + children: [ + { value: 0, children: [{ value: 10 }, { value: 11 }, { value: 12 }] }, + { value: 1 }, + { value: 2 }, + ] + }; + + const empty: E = { + value: -1, + children: [] + }; + + setup(() => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = `${element.element.value}`; + } + disposeTemplate(): void { } + }; + + const dataSource = new class implements IDataSource { + getChildren(element: E): E[] { + return element.children || []; + } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: E): { toString(): string; } { + return `${element.value}`; + } + }; + + tree = new DataTree(container, delegate, [renderer], dataSource, { + identityProvider + }); + tree.layout(200); + }); + + teardown(() => { + tree.dispose(); + }); + + test('view state is lost implicitly', () => { + tree.setInput(root); + + let navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 10); + assert.equal(navigator.next()!.value, 11); + assert.equal(navigator.next()!.value, 12); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.collapse(root.children![0]); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.setSelection([root.children![1]]); + tree.setFocus([root.children![2]]); + + tree.setInput(empty); + tree.setInput(root); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 10); + assert.equal(navigator.next()!.value, 11); + assert.equal(navigator.next()!.value, 12); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + assert.deepEqual(tree.getSelection(), []); + assert.deepEqual(tree.getFocus(), []); + }); + + test('view state can be preserved', () => { + tree.setInput(root); + + let navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 10); + assert.equal(navigator.next()!.value, 11); + assert.equal(navigator.next()!.value, 12); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.collapse(root.children![0]); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + tree.setSelection([root.children![1]]); + tree.setFocus([root.children![2]]); + + const viewState = tree.getViewState(); + + tree.setInput(empty); + tree.setInput(root, viewState); + navigator = tree.navigate(); + assert.equal(navigator.next()!.value, 0); + assert.equal(navigator.next()!.value, 1); + assert.equal(navigator.next()!.value, 2); + assert.equal(navigator.next()!, null); + + assert.deepEqual(tree.getSelection(), [root.children![1]]); + assert.deepEqual(tree.getFocus(), [root.children![2]]); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index 07431d166b8..25e84a34f4c 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -25,14 +25,14 @@ suite('IndexTreeModel', function () { test('ctor', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); assert(model); assert.equal(list.length, 0); }); test('insert', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { element: 0 }, @@ -54,7 +54,7 @@ suite('IndexTreeModel', function () { test('deep insert', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -91,7 +91,7 @@ suite('IndexTreeModel', function () { test('deep insert collapsed', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -119,7 +119,7 @@ suite('IndexTreeModel', function () { test('delete', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { element: 0 }, @@ -144,7 +144,7 @@ suite('IndexTreeModel', function () { test('nested delete', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -178,7 +178,7 @@ suite('IndexTreeModel', function () { test('deep delete', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -206,7 +206,7 @@ suite('IndexTreeModel', function () { test('hidden delete', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -231,7 +231,7 @@ suite('IndexTreeModel', function () { test('collapse', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -262,7 +262,7 @@ suite('IndexTreeModel', function () { test('expand', () => { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -302,7 +302,7 @@ suite('IndexTreeModel', function () { test('collapse should recursively adjust visible count', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -341,7 +341,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel(toSpliceable(list), { filter }); + const model = new IndexTreeModel(toSpliceable(list), -1, { filter }); model.splice([0], 0, Iterator.fromArray([ { @@ -375,7 +375,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel(toSpliceable(list), { filter }); + const model = new IndexTreeModel(toSpliceable(list), -1, { filter }); model.splice([0], 0, Iterator.fromArray([ { @@ -398,7 +398,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel(toSpliceable(list), { filter }); + const model = new IndexTreeModel(toSpliceable(list), -1, { filter }); model.splice([0], 0, Iterator.fromArray([ { @@ -437,7 +437,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel(toSpliceable(list), { filter }); + const model = new IndexTreeModel(toSpliceable(list), 'root', { filter }); model.splice([0], 0, Iterator.fromArray([ { @@ -483,7 +483,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel(toSpliceable(list), { filter }); + const model = new IndexTreeModel(toSpliceable(list), 'root', { filter }); model.splice([0], 0, Iterator.fromArray([ { @@ -529,7 +529,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel(toSpliceable(list), { filter }); + const model = new IndexTreeModel(toSpliceable(list), 'root', { filter }); model.splice([0], 0, Iterator.fromArray([ { @@ -577,7 +577,7 @@ suite('IndexTreeModel', function () { test('simple', function () { const list: ITreeNode[] = []; - const model = new IndexTreeModel(toSpliceable(list)); + const model = new IndexTreeModel(toSpliceable(list), -1); model.splice([0], 0, Iterator.fromArray([ { @@ -607,7 +607,7 @@ suite('IndexTreeModel', function () { } }; - const model = new IndexTreeModel(toSpliceable(list), { filter }); + const model = new IndexTreeModel(toSpliceable(list), -1, { filter }); model.splice([0], 0, Iterator.fromArray([ { diff --git a/src/vs/base/test/browser/ui/tree/objectTree.test.ts b/src/vs/base/test/browser/ui/tree/objectTree.test.ts new file mode 100644 index 00000000000..46e03f8884d --- /dev/null +++ b/src/vs/base/test/browser/ui/tree/objectTree.test.ts @@ -0,0 +1,227 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; +import { Iterator } from 'vs/base/common/iterator'; + +suite('ObjectTree', function () { + suite('TreeNavigator', function () { + let tree: ObjectTree; + let filter = (_: number) => true; + + setup(() => { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = `${element.element}`; + } + disposeTemplate(): void { } + }; + + tree = new ObjectTree(container, delegate, [renderer], { filter: { filter: (el) => filter(el) } }); + tree.layout(200); + }); + + teardown(() => { + tree.dispose(); + filter = (_: number) => true; + }); + + test('should be able to navigate', () => { + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(); + + assert.equal(navigator.current(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.current(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.current(), 10); + assert.equal(navigator.next(), 11); + assert.equal(navigator.current(), 11); + assert.equal(navigator.next(), 12); + assert.equal(navigator.current(), 12); + assert.equal(navigator.next(), 1); + assert.equal(navigator.current(), 1); + assert.equal(navigator.next(), 2); + assert.equal(navigator.current(), 2); + assert.equal(navigator.previous(), 1); + assert.equal(navigator.current(), 1); + assert.equal(navigator.previous(), 12); + assert.equal(navigator.previous(), 11); + assert.equal(navigator.previous(), 10); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.parent(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + + test('should skip collapsed nodes', () => { + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, collapsed: true, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(); + + assert.equal(navigator.current(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 1); + assert.equal(navigator.next(), 2); + assert.equal(navigator.next(), null); + assert.equal(navigator.previous(), 2); + assert.equal(navigator.previous(), 1); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + + test('should skip filtered elements', () => { + filter = el => el % 2 === 0; + + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(); + + assert.equal(navigator.current(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.next(), 12); + assert.equal(navigator.next(), 2); + assert.equal(navigator.next(), null); + assert.equal(navigator.previous(), 2); + assert.equal(navigator.previous(), 12); + assert.equal(navigator.previous(), 10); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.parent(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + + test('should be able to start from node', () => { + tree.setChildren(null, Iterator.fromArray([ + { + element: 0, children: Iterator.fromArray([ + { element: 10 }, + { element: 11 }, + { element: 12 }, + ]) + }, + { element: 1 }, + { element: 2 } + ])); + + const navigator = tree.navigate(1); + + assert.equal(navigator.current(), 1); + assert.equal(navigator.next(), 2); + assert.equal(navigator.current(), 2); + assert.equal(navigator.previous(), 1); + assert.equal(navigator.current(), 1); + assert.equal(navigator.previous(), 12); + assert.equal(navigator.previous(), 11); + assert.equal(navigator.previous(), 10); + assert.equal(navigator.previous(), 0); + assert.equal(navigator.previous(), null); + assert.equal(navigator.next(), 0); + assert.equal(navigator.next(), 10); + assert.equal(navigator.parent(), 0); + assert.equal(navigator.parent(), null); + assert.equal(navigator.first(), 0); + assert.equal(navigator.last(), 2); + }); + }); + + test('traits are preserved according to string identity', function () { + const container = document.createElement('div'); + container.style.width = '200px'; + container.style.height = '200px'; + + const delegate = new class implements IListVirtualDelegate { + getHeight() { return 20; } + getTemplateId(): string { return 'default'; } + }; + + const renderer = new class implements ITreeRenderer { + readonly templateId = 'default'; + renderTemplate(container: HTMLElement): HTMLElement { + return container; + } + renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { + templateData.textContent = `${element.element}`; + } + disposeTemplate(): void { } + }; + + const identityProvider = new class implements IIdentityProvider { + getId(element: number): { toString(): string; } { + return `${element % 100}`; + } + }; + + const tree = new ObjectTree(container, delegate, [renderer], { identityProvider }); + tree.layout(200); + + tree.setChildren(null, [{ element: 0 }, { element: 1 }, { element: 2 }, { element: 3 }]); + tree.setFocus([1]); + assert.deepStrictEqual(tree.getFocus(), [1]); + + tree.setChildren(null, [{ element: 100 }, { element: 101 }, { element: 102 }, { element: 103 }]); + assert.deepStrictEqual(tree.getFocus(), [101]); + }); +}); \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index 6aac4a6ad68..6a5ec8713c5 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -53,7 +53,7 @@ suite('ObjectTreeModel', function () { assert.deepEqual(toArray(list), [3, 4, 5]); assert.equal(model.size, 3); - model.setChildren(null); + model.setChildren(null, Iterator.empty()); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); @@ -85,11 +85,11 @@ suite('ObjectTreeModel', function () { assert.deepEqual(toArray(list), [0, 10, 11, 12, 120, 121, 1, 2]); assert.equal(model.size, 8); - model.setChildren(0); + model.setChildren(0, Iterator.empty()); assert.deepEqual(toArray(list), [0, 1, 2]); assert.equal(model.size, 3); - model.setChildren(null); + model.setChildren(null, Iterator.empty()); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); @@ -140,4 +140,105 @@ suite('ObjectTreeModel', function () { model.setCollapsed(1, false); assert.deepEqual(toArray(list), [1, 11, 111, 112, 2]); }); + + test('collapse state is preserved with strict identity', () => { + const list: ITreeNode[] = []; + const model = new ObjectTreeModel(toSpliceable(list), { collapseByDefault: true }); + const data = [{ element: 'father', children: [{ element: 'child' }] }]; + + model.setChildren(null, data); + assert.deepEqual(toArray(list), ['father']); + + model.setCollapsed('father', false); + assert.deepEqual(toArray(list), ['father', 'child']); + + model.setChildren(null, data); + assert.deepEqual(toArray(list), ['father', 'child']); + + const data2 = [{ element: 'father', children: [{ element: 'child' }] }, { element: 'uncle' }]; + model.setChildren(null, data2); + assert.deepEqual(toArray(list), ['father', 'child', 'uncle']); + + model.setChildren(null, [{ element: 'uncle' }]); + assert.deepEqual(toArray(list), ['uncle']); + + model.setChildren(null, data2); + assert.deepEqual(toArray(list), ['father', 'uncle']); + + model.setChildren(null, data); + assert.deepEqual(toArray(list), ['father']); + }); + + test('sorter', () => { + let compare: (a: string, b: string) => number = (a, b) => a < b ? -1 : 1; + + const list: ITreeNode[] = []; + const model = new ObjectTreeModel(toSpliceable(list), { sorter: { compare(a, b) { return compare(a, b); } } }); + const data = [ + { element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] }, + { element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] }, + { element: 'bicycles', children: [{ element: 'dutch' }, { element: 'mountain' }, { element: 'electric' }] }, + ]; + + model.setChildren(null, data); + assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']); + }); + + test('resort', () => { + let compare: (a: string, b: string) => number = () => 0; + + const list: ITreeNode[] = []; + const model = new ObjectTreeModel(toSpliceable(list), { sorter: { compare(a, b) { return compare(a, b); } } }); + const data = [ + { element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] }, + { element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] }, + { element: 'bicycles', children: [{ element: 'dutch' }, { element: 'mountain' }, { element: 'electric' }] }, + ]; + + model.setChildren(null, data); + assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric']); + + // lexicographical + compare = (a, b) => a < b ? -1 : 1; + + // non-recursive + model.resort(null, false); + assert.deepEqual(toArray(list), ['airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric', 'cars', 'sedan', 'convertible', 'compact']); + + // recursive + model.resort(); + assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']); + + // reverse + compare = (a, b) => a < b ? 1 : -1; + + // scoped + model.resort('cars'); + assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'sedan', 'convertible', 'compact']); + + // recursive + model.resort(); + assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'bicycles', 'mountain', 'electric', 'dutch', 'airplanes', 'passenger', 'jet']); + }); + + test('expandTo', () => { + const list: ITreeNode[] = []; + const model = new ObjectTreeModel(toSpliceable(list), { collapseByDefault: true }); + + model.setChildren(null, [ + { + element: 0, children: [ + { element: 10, children: [{ element: 100, children: [{ element: 1000 }] }] }, + { element: 11 }, + { element: 12 }, + ] + }, + { element: 1 }, + { element: 2 } + ]); + + assert.deepEqual(toArray(list), [0, 1, 2]); + model.expandTo(1000); + assert.deepEqual(toArray(list), [0, 10, 100, 1000, 11, 12, 1, 2]); + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index dbc2162bc1d..4c0fe3e3e17 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -32,8 +32,16 @@ suite('Arrays', () => { }); test('stableSort', () => { + function fill(num: number, valueFn: () => T, arr: T[] = []): T[] { + for (let i = 0; i < num; i++) { + arr[i] = valueFn(); + } + + return arr; + } + let counter = 0; - let data = arrays.fill(10000, () => ({ n: 1, m: counter++ })); + let data = fill(10000, () => ({ n: 1, m: counter++ })); arrays.mergeSort(data, (a, b) => a.n - b.n); @@ -262,13 +270,13 @@ suite('Arrays', () => { } test('coalesce', () => { - let a = arrays.coalesce([null, 1, null, 2, 3]); + let a: Array = arrays.coalesce([null, 1, null, 2, 3]); assert.equal(a.length, 3); assert.equal(a[0], 1); assert.equal(a[1], 2); assert.equal(a[2], 3); - arrays.coalesce([null, 1, null, void 0, undefined, 2, 3]); + arrays.coalesce([null, 1, null, undefined, undefined, 2, 3]); assert.equal(a.length, 3); assert.equal(a[0], 1); assert.equal(a[1], 2); @@ -298,14 +306,14 @@ suite('Arrays', () => { }); test('coalesce - inplace', function () { - let a = [null, 1, null, 2, 3]; + let a: Array = [null, 1, null, 2, 3]; arrays.coalesceInPlace(a); assert.equal(a.length, 3); assert.equal(a[0], 1); assert.equal(a[1], 2); assert.equal(a[2], 3); - a = [null, 1, null, void 0, undefined, 2, 3]; + a = [null, 1, null, undefined!, undefined!, 2, 3]; arrays.coalesceInPlace(a); assert.equal(a.length, 3); assert.equal(a[0], 1); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index f5c3af9ba74..586c0fa8a7f 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -39,17 +39,6 @@ suite('Async', () => { return result; }); - // test('Cancel callback behaviour', async function () { - // let withCancelCallback = new WinJsPromise(() => { }, () => { }); - // let withoutCancelCallback = new TPromise(() => { }); - - // withCancelCallback.cancel(); - // (withoutCancelCallback as WinJsPromise).cancel(); - - // await withCancelCallback.then(undefined, err => { assert.ok(isPromiseCanceledError(err)); }); - // await withoutCancelCallback.then(undefined, err => { assert.ok(isPromiseCanceledError(err)); }); - // }); - // Cancelling a sync cancelable promise will fire the cancelled token. // Also, every `then` callback runs in another execution frame. test('CancelablePromise execution order (sync)', function () { @@ -64,7 +53,7 @@ suite('Async', () => { order.push('afterCreate'); const promise = cancellablePromise - .then(null, err => null) + .then(undefined, err => null) .then(() => order.push('finally')); cancellablePromise.cancel(); @@ -86,7 +75,7 @@ suite('Async', () => { order.push('afterCreate'); const promise = cancellablePromise - .then(null, err => null) + .then(undefined, err => null) .then(() => order.push('finally')); cancellablePromise.cancel(); @@ -95,50 +84,6 @@ suite('Async', () => { return promise.then(() => assert.deepEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally'])); }); - // // Cancelling a sync tpromise will NOT cancel the promise, since it has resolved already. - // // Every `then` callback runs sync in the same execution frame, thus `finally` executes - // // before `afterCancel`. - // test('TPromise execution order (sync)', function () { - // const order = []; - // let promise = new WinJsPromise(resolve => { - // order.push('in executor'); - // resolve(1234); - // }, () => order.push('cancelled')); - - // order.push('afterCreate'); - - // promise = promise - // .then(null, err => null) - // .then(() => order.push('finally')); - - // promise.cancel(); - // order.push('afterCancel'); - - // return promise.then(() => assert.deepEqual(order, ['in executor', 'afterCreate', 'finally', 'afterCancel'])); - // }); - - // // Cancelling an async tpromise will cancel the promise. - // // Every `then` callback runs sync on the same execution frame as the `cancel` call, - // // so finally still executes before `afterCancel`. - // test('TPromise execution order (async)', function () { - // const order = []; - // let promise = new WinJsPromise(resolve => { - // order.push('in executor'); - // setTimeout(() => resolve(1234)); - // }, () => order.push('cancelled')); - - // order.push('afterCreate'); - - // promise = promise - // .then(null, err => null) - // .then(() => order.push('finally')); - - // promise.cancel(); - // order.push('afterCancel'); - - // return promise.then(() => assert.deepEqual(order, ['in executor', 'afterCreate', 'cancelled', 'finally', 'afterCancel'])); - // }); - test('cancelablePromise - get inner result', async function () { let promise = async.createCancelablePromise(token => { return async.timeout(12).then(_ => 1234); @@ -195,7 +140,7 @@ suite('Async', () => { let throttler = new async.Throttler(); - let promises: Thenable[] = []; + let promises: Promise[] = []; promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.equal(n, 1); })); promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.equal(n, 3); })); @@ -211,7 +156,7 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -259,17 +204,17 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); delayer.cancel(); @@ -286,7 +231,7 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -294,10 +239,10 @@ suite('Async', () => { assert.equal(result, 1); assert(!delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); - promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled'); })); + promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); })); assert(delayer.isTriggered()); delayer.cancel(); @@ -336,7 +281,7 @@ suite('Async', () => { }; let delayer = new async.Delayer(0); - let promises: Thenable[] = []; + let promises: Promise[] = []; assert(!delayer.isTriggered()); @@ -381,7 +326,7 @@ suite('Async', () => { let limiter = new async.Limiter(1); - let promises: Thenable[] = []; + let promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { @@ -402,7 +347,7 @@ suite('Async', () => { let factoryFactory = (n: number) => () => async.timeout(0).then(() => n); let limiter = new async.Limiter(1); - let promises: Thenable[] = []; + let promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { @@ -429,7 +374,7 @@ suite('Async', () => { let limiter = new async.Limiter(5); - let promises: Thenable[] = []; + let promises: Promise[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); return Promise.all(promises).then((res) => { @@ -499,7 +444,7 @@ suite('Async', () => { queue.queue(f1); queue.queue(f2); - queue.queue(f3).then(null, () => error = true); + queue.queue(f3).then(undefined, () => error = true); queue.queue(f4); return queue.queue(f5).then(() => { assert.equal(res[0], 1); @@ -577,7 +522,7 @@ suite('Async', () => { assert.ok(r2Queue); assert.equal(r1Queue, queue.queueFor(URI.file('/some/path'))); // same queue returned - let syncPromiseFactory = () => Promise.resolve(null); + let syncPromiseFactory = () => Promise.resolve(undefined); r1Queue.queue(syncPromiseFactory); @@ -586,4 +531,30 @@ suite('Async', () => { assert.notEqual(r1Queue, r1Queue2); // previous one got disposed after finishing }); }); + + test('retry - success case', async () => { + let counter = 0; + + const res = await async.retry(() => { + counter++; + if (counter < 2) { + return Promise.reject(new Error('fail')); + } + + return Promise.resolve(true); + }, 10, 3); + + assert.equal(res, true); + }); + + test('retry - error case', async () => { + let expectedError = new Error('fail'); + try { + await async.retry(() => { + return Promise.reject(expectedError); + }, 10, 3); + } catch (error) { + assert.equal(error, error); + } + }); }); diff --git a/src/vs/base/test/common/buffer.test.ts b/src/vs/base/test/common/buffer.test.ts new file mode 100644 index 00000000000..c5a3cd676ee --- /dev/null +++ b/src/vs/base/test/common/buffer.test.ts @@ -0,0 +1,367 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { VSBuffer, bufferToReadable, readableToBuffer, bufferToStream, streamToBuffer, writeableBufferStream } from 'vs/base/common/buffer'; +import { timeout } from 'vs/base/common/async'; + +suite('Buffer', () => { + + test('issue #71993 - VSBuffer#toString returns numbers', () => { + const data = new Uint8Array([1, 2, 3, 'h'.charCodeAt(0), 'i'.charCodeAt(0), 4, 5]).buffer; + const buffer = VSBuffer.wrap(new Uint8Array(data, 3, 2)); + assert.deepEqual(buffer.toString(), 'hi'); + }); + + test('bufferToReadable / readableToBuffer', () => { + const content = 'Hello World'; + const readable = bufferToReadable(VSBuffer.fromString(content)); + + assert.equal(readableToBuffer(readable).toString(), content); + }); + + test('bufferToStream / streamToBuffer', async () => { + const content = 'Hello World'; + const stream = bufferToStream(VSBuffer.fromString(content)); + + assert.equal((await streamToBuffer(stream)).toString(), content); + }); + + test('bufferWriteableStream - basics (no error)', async () => { + const stream = writeableBufferStream(); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + assert.equal(chunks.length, 2); + assert.equal(chunks[0].toString(), 'Hello'); + assert.equal(chunks[1].toString(), 'World'); + assert.equal(ended, true); + assert.equal(errors.length, 0); + }); + + test('bufferWriteableStream - basics (error)', async () => { + const stream = writeableBufferStream(); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(new Error()); + + assert.equal(chunks.length, 1); + assert.equal(chunks[0].toString(), 'Hello'); + assert.equal(ended, true); + assert.equal(errors.length, 1); + }); + + test('bufferWriteableStream - buffers data when no listener', async () => { + const stream = writeableBufferStream(); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + assert.equal(chunks.length, 1); + assert.equal(chunks[0].toString(), 'HelloWorld'); + assert.equal(ended, true); + assert.equal(errors.length, 0); + }); + + test('bufferWriteableStream - buffers errors when no listener', async () => { + const stream = writeableBufferStream(); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.error(new Error()); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + stream.end(); + + assert.equal(chunks.length, 1); + assert.equal(chunks[0].toString(), 'Hello'); + assert.equal(ended, true); + assert.equal(errors.length, 1); + }); + + test('bufferWriteableStream - buffers end when no listener', async () => { + const stream = writeableBufferStream(); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + assert.equal(chunks.length, 1); + assert.equal(chunks[0].toString(), 'HelloWorld'); + assert.equal(ended, true); + assert.equal(errors.length, 0); + }); + + test('bufferWriteableStream - nothing happens after end()', async () => { + const stream = writeableBufferStream(); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + let dataCalledAfterEnd = false; + stream.on('data', data => { + dataCalledAfterEnd = true; + }); + + let errorCalledAfterEnd = false; + stream.on('error', error => { + errorCalledAfterEnd = true; + }); + + let endCalledAfterEnd = false; + stream.on('end', () => { + endCalledAfterEnd = true; + }); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.error(new Error()); + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + assert.equal(dataCalledAfterEnd, false); + assert.equal(errorCalledAfterEnd, false); + assert.equal(endCalledAfterEnd, false); + + assert.equal(chunks.length, 2); + assert.equal(chunks[0].toString(), 'Hello'); + assert.equal(chunks[1].toString(), 'World'); + }); + + test('bufferWriteableStream - pause/resume (simple)', async () => { + const stream = writeableBufferStream(); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + stream.pause(); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + assert.equal(chunks.length, 0); + assert.equal(errors.length, 0); + assert.equal(ended, false); + + stream.resume(); + + assert.equal(chunks.length, 1); + assert.equal(chunks[0].toString(), 'HelloWorld'); + assert.equal(ended, true); + assert.equal(errors.length, 0); + }); + + test('bufferWriteableStream - pause/resume (pause after first write)', async () => { + const stream = writeableBufferStream(); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + + stream.pause(); + + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + assert.equal(chunks.length, 1); + assert.equal(chunks[0].toString(), 'Hello'); + assert.equal(errors.length, 0); + assert.equal(ended, false); + + stream.resume(); + + assert.equal(chunks.length, 2); + assert.equal(chunks[0].toString(), 'Hello'); + assert.equal(chunks[1].toString(), 'World'); + assert.equal(ended, true); + assert.equal(errors.length, 0); + }); + + test('bufferWriteableStream - pause/resume (error)', async () => { + const stream = writeableBufferStream(); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + stream.pause(); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(new Error()); + + assert.equal(chunks.length, 0); + assert.equal(ended, false); + assert.equal(errors.length, 0); + + stream.resume(); + + assert.equal(chunks.length, 1); + assert.equal(chunks[0].toString(), 'Hello'); + assert.equal(ended, true); + assert.equal(errors.length, 1); + }); + + test('bufferWriteableStream - destroy', async () => { + const stream = writeableBufferStream(); + + let chunks: VSBuffer[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + let ended = false; + stream.on('end', () => { + ended = true; + }); + + let errors: Error[] = []; + stream.on('error', error => { + errors.push(error); + }); + + stream.destroy(); + + await timeout(0); + stream.write(VSBuffer.fromString('Hello')); + await timeout(0); + stream.end(VSBuffer.fromString('World')); + + assert.equal(chunks.length, 0); + assert.equal(ended, false); + assert.equal(errors.length, 0); + }); +}); diff --git a/src/vs/base/test/common/cache.test.ts b/src/vs/base/test/common/cache.test.ts index 912e8fdfe62..81b3214efe3 100644 --- a/src/vs/base/test/common/cache.test.ts +++ b/src/vs/base/test/common/cache.test.ts @@ -42,7 +42,7 @@ suite('Cache', () => { let result = cache.get(); assert.equal(counter1, 1); assert.equal(counter2, 0); - result.promise.then(null, () => assert(true)); + result.promise.then(undefined, () => assert(true)); result.dispose(); assert.equal(counter1, 1); assert.equal(counter2, 0); diff --git a/src/vs/base/test/common/cancellation.test.ts b/src/vs/base/test/common/cancellation.test.ts index 74102fafc07..536b2e21139 100644 --- a/src/vs/base/test/common/cancellation.test.ts +++ b/src/vs/base/test/common/cancellation.test.ts @@ -94,4 +94,19 @@ suite('CancellationToken', function () { source.cancel(); assert.equal(count, 0); }); + + test('parent cancels child', function () { + + let parent = new CancellationTokenSource(); + let child = new CancellationTokenSource(parent.token); + + let count = 0; + child.token.onCancellationRequested(() => count += 1); + + parent.cancel(); + + assert.equal(count, 1); + assert.equal(child.token.isCancellationRequested, true); + assert.equal(parent.token.isCancellationRequested, true); + }); }); diff --git a/src/vs/base/test/common/collections.test.ts b/src/vs/base/test/common/collections.test.ts index 57acf4263a3..353b5d0147d 100644 --- a/src/vs/base/test/common/collections.test.ts +++ b/src/vs/base/test/common/collections.test.ts @@ -54,9 +54,4 @@ suite('Collections', () => { assert.equal(grouped[group2].length, 1); assert.equal(grouped[group2][0].value, value3); }); - - test('remove', () => { - assert(collections.remove({ 'far': 1 }, 'far')); - assert(!collections.remove({ 'far': 1 }, 'boo')); - }); }); diff --git a/src/vs/base/test/common/color.test.ts b/src/vs/base/test/common/color.test.ts index 69308a43369..865c158bab5 100644 --- a/src/vs/base/test/common/color.test.ts +++ b/src/vs/base/test/common/color.test.ts @@ -186,7 +186,7 @@ suite('Color', () => { }); test('bug#36240', () => { - assert.deepEqual(HSVA.fromRGBA(new RGBA(92, 106, 196, 1)), new HSVA(232, .531, .769, 1)); + assert.deepEqual(HSVA.fromRGBA(new RGBA(92, 106, 196, 1)), new HSVA(232, 0.531, 0.769, 1)); assert.deepEqual(HSVA.toRGBA(HSVA.fromRGBA(new RGBA(92, 106, 196, 1))), new RGBA(92, 106, 196, 1)); }); }); @@ -196,50 +196,50 @@ suite('Color', () => { test('parseHex', () => { // invalid - assert.deepEqual(Color.Format.CSS.parseHex(null), null); + assert.deepEqual(Color.Format.CSS.parseHex(null!), null); assert.deepEqual(Color.Format.CSS.parseHex(''), null); assert.deepEqual(Color.Format.CSS.parseHex('#'), null); assert.deepEqual(Color.Format.CSS.parseHex('#0102030'), null); // somewhat valid - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0').rgba, new RGBA(255, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0').rgba, new RGBA(255, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00').rgba, new RGBA(15, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0')!.rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0')!.rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00')!.rgba, new RGBA(15, 255, 0, 1)); // valid - assert.deepEqual(Color.Format.CSS.parseHex('#000000').rgba, new RGBA(0, 0, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF').rgba, new RGBA(255, 255, 255, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#000000')!.rgba, new RGBA(0, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF')!.rgba, new RGBA(255, 255, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FF0000').rgba, new RGBA(255, 0, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FF00').rgba, new RGBA(0, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0000FF').rgba, new RGBA(0, 0, 255, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FF0000')!.rgba, new RGBA(255, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FF00')!.rgba, new RGBA(0, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0000FF')!.rgba, new RGBA(0, 0, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00').rgba, new RGBA(255, 255, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF').rgba, new RGBA(0, 255, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF').rgba, new RGBA(255, 0, 255, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00')!.rgba, new RGBA(255, 255, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF')!.rgba, new RGBA(0, 255, 255, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF')!.rgba, new RGBA(255, 0, 255, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0').rgba, new RGBA(192, 192, 192, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0')!.rgba, new RGBA(192, 192, 192, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#808080').rgba, new RGBA(128, 128, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#800000').rgba, new RGBA(128, 0, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#808000').rgba, new RGBA(128, 128, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#008000').rgba, new RGBA(0, 128, 0, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#800080').rgba, new RGBA(128, 0, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#008080').rgba, new RGBA(0, 128, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#000080').rgba, new RGBA(0, 0, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#808080')!.rgba, new RGBA(128, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800000')!.rgba, new RGBA(128, 0, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#808000')!.rgba, new RGBA(128, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008000')!.rgba, new RGBA(0, 128, 0, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#800080')!.rgba, new RGBA(128, 0, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#008080')!.rgba, new RGBA(0, 128, 128, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#000080')!.rgba, new RGBA(0, 0, 128, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#010203').rgba, new RGBA(1, 2, 3, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#040506').rgba, new RGBA(4, 5, 6, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#070809').rgba, new RGBA(7, 8, 9, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a').rgba, new RGBA(10, 10, 10, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b').rgba, new RGBA(11, 11, 11, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c').rgba, new RGBA(12, 12, 12, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d').rgba, new RGBA(13, 13, 13, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e').rgba, new RGBA(14, 14, 14, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f').rgba, new RGBA(15, 15, 15, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0').rgba, new RGBA(160, 160, 160, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA').rgba, new RGBA(204, 255, 170, 1)); - assert.deepEqual(Color.Format.CSS.parseHex('#CFA8').rgba, new RGBA(204, 255, 170, 0.533)); + assert.deepEqual(Color.Format.CSS.parseHex('#010203')!.rgba, new RGBA(1, 2, 3, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#040506')!.rgba, new RGBA(4, 5, 6, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#070809')!.rgba, new RGBA(7, 8, 9, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a')!.rgba, new RGBA(10, 10, 10, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b')!.rgba, new RGBA(11, 11, 11, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c')!.rgba, new RGBA(12, 12, 12, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d')!.rgba, new RGBA(13, 13, 13, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e')!.rgba, new RGBA(14, 14, 14, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f')!.rgba, new RGBA(15, 15, 15, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0')!.rgba, new RGBA(160, 160, 160, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA')!.rgba, new RGBA(204, 255, 170, 1)); + assert.deepEqual(Color.Format.CSS.parseHex('#CFA8')!.rgba, new RGBA(204, 255, 170, 0.533)); }); }); }); diff --git a/src/vs/base/test/common/decorators.test.ts b/src/vs/base/test/common/decorators.test.ts index 1261efae925..aaabb2b5094 100644 --- a/src/vs/base/test/common/decorators.test.ts +++ b/src/vs/base/test/common/decorators.test.ts @@ -11,7 +11,7 @@ suite('Decorators', () => { class Foo { count = 0; - constructor(private _answer: number) { } + constructor(private _answer: number | null | undefined) { } @memoize answer() { @@ -56,7 +56,7 @@ suite('Decorators', () => { class Foo { count = 0; - constructor(private _answer: number) { } + constructor(private _answer: number | null | undefined) { } @memoize get answer() { diff --git a/src/vs/base/test/common/diff/diff.test.ts b/src/vs/base/test/common/diff/diff.test.ts index 70fe3acb597..f80a6169f28 100644 --- a/src/vs/base/test/common/diff/diff.test.ts +++ b/src/vs/base/test/common/diff/diff.test.ts @@ -21,16 +21,16 @@ class StringDiffSequence implements ISequence { } function createArray(length: number, value: T): T[] { - var r = []; - for (var i = 0; i < length; i++) { + const r: T[] = []; + for (let i = 0; i < length; i++) { r[i] = value; } return r; } function maskBasedSubstring(str: string, mask: boolean[]): string { - var r = ''; - for (var i = 0; i < str.length; i++) { + let r = ''; + for (let i = 0; i < str.length; i++) { if (mask[i]) { r += str.charAt(i); } @@ -39,10 +39,10 @@ function maskBasedSubstring(str: string, mask: boolean[]): string { } function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffChange[], answerStr: string, onlyLength: boolean = false): void { - var originalMask = createArray(originalStr.length, true); - var modifiedMask = createArray(modifiedStr.length, true); + let originalMask = createArray(originalStr.length, true); + let modifiedMask = createArray(modifiedStr.length, true); - var i, j, change; + let i, j, change; for (i = 0; i < changes.length; i++) { change = changes[i]; @@ -59,8 +59,8 @@ function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffCh } } - var originalAnswer = maskBasedSubstring(originalStr, originalMask); - var modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask); + let originalAnswer = maskBasedSubstring(originalStr, originalMask); + let modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask); if (onlyLength) { assert.equal(originalAnswer.length, answerStr.length); @@ -72,14 +72,14 @@ function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffCh } function lcsInnerTest(Algorithm: any, originalStr: string, modifiedStr: string, answerStr: string, onlyLength: boolean = false): void { - var diff = new Algorithm(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr)); - var changes = diff.ComputeDiff(); + let diff = new Algorithm(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr)); + let changes = diff.ComputeDiff(); assertAnswer(originalStr, modifiedStr, changes, answerStr, onlyLength); } function stringPower(str: string, power: number): string { - var r = str; - for (var i = 0; i < power; i++) { + let r = str; + for (let i = 0; i < power; i++) { r += r; } return r; @@ -87,7 +87,7 @@ function stringPower(str: string, power: number): string { function lcsTest(Algorithm: any, originalStr: string, modifiedStr: string, answerStr: string) { lcsInnerTest(Algorithm, originalStr, modifiedStr, answerStr); - for (var i = 2; i <= 5; i++) { + for (let i = 2; i <= 5; i++) { lcsInnerTest(Algorithm, stringPower(originalStr, i), stringPower(modifiedStr, i), stringPower(answerStr, i), true); } } @@ -116,14 +116,14 @@ suite('Diff', () => { suite('Diff - Ported from VS', () => { test('using continue processing predicate to quit early', function () { - var left = 'abcdef'; - var right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf'; + let left = 'abcdef'; + let right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf'; // We use a long non-matching portion at the end of the right-side string, so the backwards tracking logic // doesn't get there first. - var predicateCallCount = 0; + let predicateCallCount = 0; - var diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { + let diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { assert.equal(predicateCallCount, 0); predicateCallCount++; @@ -134,7 +134,7 @@ suite('Diff - Ported from VS', () => { // cancel processing return false; }); - var changes = diff.ComputeDiff(true); + let changes = diff.ComputeDiff(true); assert.equal(predicateCallCount, 1); @@ -170,11 +170,11 @@ suite('Diff - Ported from VS', () => { // Cancel *one iteration* after the second match ('d') - var hitSecondMatch = false; + let hitSecondMatch = false; diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, leftSequence, longestMatchSoFar) { assert(longestMatchSoFar <= 2); // We never see a match of length > 2 - var hitYet = hitSecondMatch; + let hitYet = hitSecondMatch; hitSecondMatch = longestMatchSoFar > 1; // Continue processing as long as there hasn't been a match made. return !hitYet; diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 19acb225e99..efc4dec869d 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Event, Emitter, debounceEvent, EventBufferer, once, fromPromise, stopwatch, buffer, echo, EventMultiplexer, latch, AsyncEmitter, IWaitUntil } from 'vs/base/common/event'; +import { Event, Emitter, EventBufferer, EventMultiplexer, AsyncEmitter, IWaitUntil, PauseableEmitter } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; @@ -71,7 +71,7 @@ suite('Event', function () { // unhook listener while (bucket.length) { - bucket.pop().dispose(); + bucket.pop()!.dispose(); } // noop @@ -111,7 +111,7 @@ suite('Event', function () { Errors.setUnexpectedErrorHandler(() => null); try { - let a = new Emitter(); + let a = new Emitter(); let hit = false; a.event(function () { throw 9; @@ -134,26 +134,26 @@ suite('Event', function () { } const context = {}; - let emitter = new Emitter(); + let emitter = new Emitter(); let reg1 = emitter.event(listener, context); let reg2 = emitter.event(listener, context); - emitter.fire(); + emitter.fire(undefined); assert.equal(counter, 2); reg1.dispose(); - emitter.fire(); + emitter.fire(undefined); assert.equal(counter, 3); reg2.dispose(); - emitter.fire(); + emitter.fire(undefined); assert.equal(counter, 3); }); test('Debounce Event', function (done: () => void) { let doc = new Samples.Document3(); - let onDocDidChange = debounceEvent(doc.onDidChange, (prev: string[], cur) => { + let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[], cur) => { if (!prev) { prev = [cur]; } else if (prev.indexOf(cur) < 0) { @@ -183,7 +183,7 @@ suite('Event', function () { test('Debounce Event - leading', async function () { const emitter = new Emitter(); - let debounced = debounceEvent(emitter.event, (l, e) => e, 0, /*leading=*/true); + let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); let calls = 0; debounced(() => { @@ -199,7 +199,7 @@ suite('Event', function () { test('Debounce Event - leading', async function () { const emitter = new Emitter(); - let debounced = debounceEvent(emitter.event, (l, e) => e, 0, /*leading=*/true); + let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true); let calls = 0; debounced(() => { @@ -254,7 +254,7 @@ suite('AsyncEmitter', function () { emitter.fireAsync(thenables => ({ foo: true, bar: 1, - waitUntil(t: Thenable) { thenables.push(t); } + waitUntil(t: Promise) { thenables.push(t); } })); emitter.dispose(); }); @@ -331,6 +331,133 @@ suite('AsyncEmitter', function () { }); }); +suite('PausableEmitter', function () { + + test('basic', function () { + const data: number[] = []; + const emitter = new PauseableEmitter(); + + emitter.event(e => data.push(e)); + emitter.fire(1); + emitter.fire(2); + + assert.deepEqual(data, [1, 2]); + }); + + test('pause/resume - no merge', function () { + const data: number[] = []; + const emitter = new PauseableEmitter(); + + emitter.event(e => data.push(e)); + emitter.fire(1); + emitter.fire(2); + assert.deepEqual(data, [1, 2]); + + emitter.pause(); + emitter.fire(3); + emitter.fire(4); + assert.deepEqual(data, [1, 2]); + + emitter.resume(); + assert.deepEqual(data, [1, 2, 3, 4]); + emitter.fire(5); + assert.deepEqual(data, [1, 2, 3, 4, 5]); + }); + + test('pause/resume - merge', function () { + const data: number[] = []; + const emitter = new PauseableEmitter({ merge: (a) => a.reduce((p, c) => p + c, 0) }); + + emitter.event(e => data.push(e)); + emitter.fire(1); + emitter.fire(2); + assert.deepEqual(data, [1, 2]); + + emitter.pause(); + emitter.fire(3); + emitter.fire(4); + assert.deepEqual(data, [1, 2]); + + emitter.resume(); + assert.deepEqual(data, [1, 2, 7]); + + emitter.fire(5); + assert.deepEqual(data, [1, 2, 7, 5]); + }); + + test('double pause/resume', function () { + const data: number[] = []; + const emitter = new PauseableEmitter(); + + emitter.event(e => data.push(e)); + emitter.fire(1); + emitter.fire(2); + assert.deepEqual(data, [1, 2]); + + emitter.pause(); + emitter.pause(); + emitter.fire(3); + emitter.fire(4); + assert.deepEqual(data, [1, 2]); + + emitter.resume(); + assert.deepEqual(data, [1, 2]); + + emitter.resume(); + assert.deepEqual(data, [1, 2, 3, 4]); + + emitter.resume(); + assert.deepEqual(data, [1, 2, 3, 4]); + }); + + test('resume, no pause', function () { + const data: number[] = []; + const emitter = new PauseableEmitter(); + + emitter.event(e => data.push(e)); + emitter.fire(1); + emitter.fire(2); + assert.deepEqual(data, [1, 2]); + + emitter.resume(); + emitter.fire(3); + assert.deepEqual(data, [1, 2, 3]); + }); + + test('nested pause', function () { + const data: number[] = []; + const emitter = new PauseableEmitter(); + + let once = true; + emitter.event(e => { + data.push(e); + + if (once) { + emitter.pause(); + once = false; + } + }); + emitter.event(e => { + data.push(e); + }); + + emitter.pause(); + emitter.fire(1); + emitter.fire(2); + assert.deepEqual(data, []); + + emitter.resume(); + assert.deepEqual(data, [1, 1]); // paused after first event + + emitter.resume(); + assert.deepEqual(data, [1, 1, 2, 2]); // remaing event delivered + + emitter.fire(3); + assert.deepEqual(data, [1, 1, 2, 2, 3, 3]); + + }); +}); + suite('Event utils', () => { suite('EventBufferer', () => { @@ -384,8 +511,8 @@ suite('Event utils', () => { let counter1 = 0, counter2 = 0, counter3 = 0; const listener1 = emitter.event(() => counter1++); - const listener2 = once(emitter.event)(() => counter2++); - const listener3 = once(emitter.event)(() => counter3++); + const listener2 = Event.once(emitter.event)(() => counter2++); + const listener3 = Event.once(emitter.event)(() => counter3++); assert.equal(counter1, 0); assert.equal(counter2, 0); @@ -412,7 +539,7 @@ suite('Event utils', () => { test('should emit when done', async () => { let count = 0; - const event = fromPromise(Promise.resolve(null)); + const event = Event.fromPromise(Promise.resolve(null)); event(() => count++); assert.equal(count, 0); @@ -425,7 +552,7 @@ suite('Event utils', () => { let count = 0; const promise = timeout(5); - const event = fromPromise(promise); + const event = Event.fromPromise(promise); event(() => count++); assert.equal(count, 0); @@ -438,7 +565,7 @@ suite('Event utils', () => { test('should emit', () => { const emitter = new Emitter(); - const event = stopwatch(emitter.event); + const event = Event.stopwatch(emitter.event); return new Promise((c, e) => { event(duration => { @@ -448,7 +575,7 @@ suite('Event utils', () => { e(err); } - c(null); + c(undefined); }); setTimeout(() => emitter.fire(), 10); @@ -462,7 +589,7 @@ suite('Event utils', () => { const result: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const bufferedEvent = buffer(event); + const bufferedEvent = Event.buffer(event); emitter.fire(1); emitter.fire(2); @@ -484,7 +611,7 @@ suite('Event utils', () => { const result: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const bufferedEvent = buffer(event, true); + const bufferedEvent = Event.buffer(event, true); emitter.fire(1); emitter.fire(2); @@ -506,7 +633,7 @@ suite('Event utils', () => { const result: number[] = []; const emitter = new Emitter(); const event = emitter.event; - const bufferedEvent = buffer(event, false, [-2, -1, 0]); + const bufferedEvent = Event.buffer(event, false, [-2, -1, 0]); emitter.fire(1); emitter.fire(2); @@ -518,67 +645,6 @@ suite('Event utils', () => { }); }); - suite('echo', () => { - - test('should echo events', () => { - const result: number[] = []; - const emitter = new Emitter(); - const event = emitter.event; - const echoEvent = echo(event); - - emitter.fire(1); - emitter.fire(2); - emitter.fire(3); - assert.deepEqual(result, []); - - const listener = echoEvent(num => result.push(num)); - assert.deepEqual(result, [1, 2, 3]); - - emitter.fire(4); - assert.deepEqual(result, [1, 2, 3, 4]); - - listener.dispose(); - emitter.fire(5); - assert.deepEqual(result, [1, 2, 3, 4]); - }); - - test('should echo events for every listener', () => { - const result1: number[] = []; - const result2: number[] = []; - const emitter = new Emitter(); - const event = emitter.event; - const echoEvent = echo(event); - - emitter.fire(1); - emitter.fire(2); - emitter.fire(3); - assert.deepEqual(result1, []); - assert.deepEqual(result2, []); - - const listener1 = echoEvent(num => result1.push(num)); - assert.deepEqual(result1, [1, 2, 3]); - assert.deepEqual(result2, []); - - emitter.fire(4); - assert.deepEqual(result1, [1, 2, 3, 4]); - assert.deepEqual(result2, []); - - const listener2 = echoEvent(num => result2.push(num)); - assert.deepEqual(result1, [1, 2, 3, 4]); - assert.deepEqual(result2, [1, 2, 3, 4]); - - emitter.fire(5); - assert.deepEqual(result1, [1, 2, 3, 4, 5]); - assert.deepEqual(result2, [1, 2, 3, 4, 5]); - - listener1.dispose(); - listener2.dispose(); - emitter.fire(6); - assert.deepEqual(result1, [1, 2, 3, 4, 5]); - assert.deepEqual(result2, [1, 2, 3, 4, 5]); - }); - }); - suite('EventMultiplexer', () => { test('works', () => { @@ -744,7 +810,7 @@ suite('Event utils', () => { test('latch', () => { const emitter = new Emitter(); - const event = latch(emitter.event); + const event = Event.latch(emitter.event); const result: number[] = []; const listener = event(num => result.push(num)); @@ -777,4 +843,5 @@ suite('Event utils', () => { listener.dispose(); }); + }); diff --git a/src/vs/base/test/common/extpath.test.ts b/src/vs/base/test/common/extpath.test.ts new file mode 100644 index 00000000000..da6da32873f --- /dev/null +++ b/src/vs/base/test/common/extpath.test.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as assert from 'assert'; +import * as extpath from 'vs/base/common/extpath'; +import * as platform from 'vs/base/common/platform'; + +suite('Paths', () => { + + test('toForwardSlashes', () => { + assert.equal(extpath.toSlashes('\\\\server\\share\\some\\path'), '//server/share/some/path'); + assert.equal(extpath.toSlashes('c:\\test'), 'c:/test'); + assert.equal(extpath.toSlashes('foo\\bar'), 'foo/bar'); + assert.equal(extpath.toSlashes('/user/far'), '/user/far'); + }); + + test('getRoot', () => { + assert.equal(extpath.getRoot('/user/far'), '/'); + assert.equal(extpath.getRoot('\\\\server\\share\\some\\path'), '//server/share/'); + assert.equal(extpath.getRoot('//server/share/some/path'), '//server/share/'); + assert.equal(extpath.getRoot('//server/share'), '/'); + assert.equal(extpath.getRoot('//server'), '/'); + assert.equal(extpath.getRoot('//server//'), '/'); + assert.equal(extpath.getRoot('c:/user/far'), 'c:/'); + assert.equal(extpath.getRoot('c:user/far'), 'c:'); + assert.equal(extpath.getRoot('http://www'), ''); + assert.equal(extpath.getRoot('http://www/'), 'http://www/'); + assert.equal(extpath.getRoot('file:///foo'), 'file:///'); + assert.equal(extpath.getRoot('file://foo'), ''); + + }); + + test('isUNC', () => { + if (platform.isWindows) { + assert.ok(!extpath.isUNC('foo')); + assert.ok(!extpath.isUNC('/foo')); + assert.ok(!extpath.isUNC('\\foo')); + assert.ok(!extpath.isUNC('\\\\foo')); + assert.ok(extpath.isUNC('\\\\a\\b')); + assert.ok(!extpath.isUNC('//a/b')); + assert.ok(extpath.isUNC('\\\\server\\share')); + assert.ok(extpath.isUNC('\\\\server\\share\\')); + assert.ok(extpath.isUNC('\\\\server\\share\\path')); + } + }); + + test('isValidBasename', () => { + assert.ok(!extpath.isValidBasename(null)); + assert.ok(!extpath.isValidBasename('')); + assert.ok(extpath.isValidBasename('test.txt')); + assert.ok(!extpath.isValidBasename('/test.txt')); + assert.ok(!extpath.isValidBasename('\\test.txt')); + + if (platform.isWindows) { + assert.ok(!extpath.isValidBasename('aux')); + assert.ok(!extpath.isValidBasename('Aux')); + assert.ok(!extpath.isValidBasename('LPT0')); + assert.ok(!extpath.isValidBasename('test.txt.')); + assert.ok(!extpath.isValidBasename('test.txt..')); + assert.ok(!extpath.isValidBasename('test.txt ')); + assert.ok(!extpath.isValidBasename('test.txt\t')); + assert.ok(!extpath.isValidBasename('tes:t.txt')); + assert.ok(!extpath.isValidBasename('tes"t.txt')); + } + }); + + test('sanitizeFilePath', () => { + if (platform.isWindows) { + assert.equal(extpath.sanitizeFilePath('.', 'C:\\the\\cwd'), 'C:\\the\\cwd'); + assert.equal(extpath.sanitizeFilePath('', 'C:\\the\\cwd'), 'C:\\the\\cwd'); + + assert.equal(extpath.sanitizeFilePath('C:', 'C:\\the\\cwd'), 'C:\\'); + assert.equal(extpath.sanitizeFilePath('C:\\', 'C:\\the\\cwd'), 'C:\\'); + assert.equal(extpath.sanitizeFilePath('C:\\\\', 'C:\\the\\cwd'), 'C:\\'); + + assert.equal(extpath.sanitizeFilePath('C:\\folder\\my.txt', 'C:\\the\\cwd'), 'C:\\folder\\my.txt'); + assert.equal(extpath.sanitizeFilePath('C:\\folder\\my', 'C:\\the\\cwd'), 'C:\\folder\\my'); + assert.equal(extpath.sanitizeFilePath('C:\\folder\\..\\my', 'C:\\the\\cwd'), 'C:\\my'); + assert.equal(extpath.sanitizeFilePath('C:\\folder\\my\\', 'C:\\the\\cwd'), 'C:\\folder\\my'); + assert.equal(extpath.sanitizeFilePath('C:\\folder\\my\\\\\\', 'C:\\the\\cwd'), 'C:\\folder\\my'); + + assert.equal(extpath.sanitizeFilePath('my.txt', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt'); + assert.equal(extpath.sanitizeFilePath('my.txt\\', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt'); + + assert.equal(extpath.sanitizeFilePath('\\\\localhost\\folder\\my', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my'); + assert.equal(extpath.sanitizeFilePath('\\\\localhost\\folder\\my\\', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my'); + } else { + assert.equal(extpath.sanitizeFilePath('.', '/the/cwd'), '/the/cwd'); + assert.equal(extpath.sanitizeFilePath('', '/the/cwd'), '/the/cwd'); + assert.equal(extpath.sanitizeFilePath('/', '/the/cwd'), '/'); + + assert.equal(extpath.sanitizeFilePath('/folder/my.txt', '/the/cwd'), '/folder/my.txt'); + assert.equal(extpath.sanitizeFilePath('/folder/my', '/the/cwd'), '/folder/my'); + assert.equal(extpath.sanitizeFilePath('/folder/../my', '/the/cwd'), '/my'); + assert.equal(extpath.sanitizeFilePath('/folder/my/', '/the/cwd'), '/folder/my'); + assert.equal(extpath.sanitizeFilePath('/folder/my///', '/the/cwd'), '/folder/my'); + + assert.equal(extpath.sanitizeFilePath('my.txt', '/the/cwd'), '/the/cwd/my.txt'); + assert.equal(extpath.sanitizeFilePath('my.txt/', '/the/cwd'), '/the/cwd/my.txt'); + } + }); + + test('isRoot', () => { + if (platform.isWindows) { + assert.ok(extpath.isRootOrDriveLetter('c:')); + assert.ok(extpath.isRootOrDriveLetter('D:')); + assert.ok(extpath.isRootOrDriveLetter('D:/')); + assert.ok(extpath.isRootOrDriveLetter('D:\\')); + assert.ok(!extpath.isRootOrDriveLetter('D:\\path')); + assert.ok(!extpath.isRootOrDriveLetter('D:/path')); + } else { + assert.ok(extpath.isRootOrDriveLetter('/')); + assert.ok(!extpath.isRootOrDriveLetter('/path')); + } + }); +}); diff --git a/src/vs/base/test/common/filters.perf.test.ts b/src/vs/base/test/common/filters.perf.test.ts index 61a57d33335..3c20621a578 100644 --- a/src/vs/base/test/common/filters.perf.test.ts +++ b/src/vs/base/test/common/filters.perf.test.ts @@ -5,7 +5,7 @@ import * as filters from 'vs/base/common/filters'; import { data } from './filters.perf.data'; -const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons']; +const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons', 'zyx', 'aBc']; const _enablePerf = false; @@ -24,22 +24,49 @@ perfSuite('Performance - fuzzyMatch', function () { const t1 = Date.now(); let count = 0; - for (const pattern of patterns) { - const patternLow = pattern.toLowerCase(); - for (const item of data) { - count += 1; - match(pattern, patternLow, 0, item, item.toLowerCase(), 0, false); + for (let i = 0; i < 2; i++) { + for (const pattern of patterns) { + const patternLow = pattern.toLowerCase(); + for (const item of data) { + count += 1; + match(pattern, patternLow, 0, item, item.toLowerCase(), 0, false); + } } } const d = Date.now() - t1; - console.log(name, `${d}ms, ${Math.round(count / d) * 15}ops/15ms`); + console.log(name, `${d}ms, ${Math.round(count / d) * 15}/15ms, ${Math.round(count / d)}/1ms`); }); } - // perfTest('matchesFuzzy', filters.matchesFuzzy); - // perfTest('fuzzyContiguousFilter', filters.fuzzyContiguousFilter); perfTest('fuzzyScore', filters.fuzzyScore); perfTest('fuzzyScoreGraceful', filters.fuzzyScoreGraceful); perfTest('fuzzyScoreGracefulAggressive', filters.fuzzyScoreGracefulAggressive); }); + +perfSuite('Performance - IFilter', function () { + + function perfTest(name: string, match: filters.IFilter) { + test(name, () => { + + const t1 = Date.now(); + let count = 0; + for (let i = 0; i < 2; i++) { + for (const pattern of patterns) { + for (const item of data) { + count += 1; + match(pattern, item); + } + } + } + const d = Date.now() - t1; + console.log(name, `${d}ms, ${Math.round(count / d) * 15}/15ms, ${Math.round(count / d)}/1ms`); + }); + } + + perfTest('matchesFuzzy', filters.matchesFuzzy); + perfTest('matchesFuzzy2', filters.matchesFuzzy2); + perfTest('matchesPrefix', filters.matchesPrefix); + perfTest('matchesContiguousSubString', filters.matchesContiguousSubString); + perfTest('matchesCamelCase', filters.matchesCamelCase); +}); diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 8ce816defaf..c4d582eeda0 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer } from 'vs/base/common/filters'; +import { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer, createMatches } from 'vs/base/common/filters'; function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) { let r = filter(word, wordToMatchAgainst); - assert(r); + assert(r, `${word} didn't match ${wordToMatchAgainst}`); if (highlights) { assert.deepEqual(r, highlights); } @@ -202,20 +202,33 @@ suite('Filters', () => { assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null); assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]); + + filterOk(matchesWords, 'bar', 'foo-bar'); + filterOk(matchesWords, 'bar test', 'foo-bar test'); + filterOk(matchesWords, 'fbt', 'foo-bar test'); + filterOk(matchesWords, 'bar test', 'foo-bar (test)'); + filterOk(matchesWords, 'foo bar', 'foo (bar)'); + + filterNotOk(matchesWords, 'bar est', 'foo-bar test'); + filterNotOk(matchesWords, 'fo ar', 'foo-bar test'); + filterNotOk(matchesWords, 'for', 'foo-bar test'); + filterNotOk(matchesWords, 'foo bar', 'foo-bar'); }); - function assertMatches(pattern: string, word: string, decoratedWord: string, filter: FuzzyScorer, opts: { patternPos?: number, wordPos?: number, firstMatchCanBeWeak?: boolean } = {}) { + function assertMatches(pattern: string, word: string, decoratedWord: string | undefined, filter: FuzzyScorer, opts: { patternPos?: number, wordPos?: number, firstMatchCanBeWeak?: boolean } = {}) { let r = filter(pattern, pattern.toLowerCase(), opts.patternPos || 0, word, word.toLowerCase(), opts.wordPos || 0, opts.firstMatchCanBeWeak || false); - assert.ok(!decoratedWord === (!r || r[1].length === 0)); + assert.ok(!decoratedWord === !r); if (r) { - const [, matches] = r; + let matches = createMatches(r); + let actualWord = ''; let pos = 0; - for (let i = 0; i < matches.length; i++) { - let actual = matches[i]; - let expected = decoratedWord.indexOf('^', pos) - i; - assert.equal(actual, expected); - pos = expected + 1 + i; + for (const match of matches) { + actualWord += word.substring(pos, match.start); + actualWord += '^' + word.substring(match.start, match.end).split('').join('^'); + pos = match.end; } + actualWord += word.substring(pos); + assert.equal(actualWord, decoratedWord); } } @@ -448,4 +461,8 @@ suite('Filters', () => { assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGraceful); assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGracefulAggressive); }); + + test('List highlight filter: Not all characters from match are highlighterd #66923', () => { + assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore); + }); }); diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts index 2691c16b444..d197cdf4f1f 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/common/hash.test.ts @@ -17,7 +17,7 @@ suite('Hash', () => { }); test('number', () => { - assert.equal(hash(1), hash(1.0)); + assert.equal(hash(1), hash(1)); assert.notEqual(hash(0), hash(1)); assert.notEqual(hash(1), hash(-1)); assert.notEqual(hash(0x12345678), hash(0x123456789)); @@ -39,7 +39,7 @@ suite('Hash', () => { test('object', () => { assert.equal(hash({}), hash({})); assert.equal(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar' })); - assert.equal(hash({ 'foo': 'bar', 'foo2': void 0 }), hash({ 'foo2': void 0, 'foo': 'bar' })); + assert.equal(hash({ 'foo': 'bar', 'foo2': undefined }), hash({ 'foo2': undefined, 'foo': 'bar' })); assert.notEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' })); assert.notEqual(hash({}), hash([])); }); diff --git a/src/vs/base/test/common/history.test.ts b/src/vs/base/test/common/history.test.ts index b5d274656fb..fc32e74324b 100644 --- a/src/vs/base/test/common/history.test.ts +++ b/src/vs/base/test/common/history.test.ts @@ -113,12 +113,12 @@ suite('History Navigator', () => { assert.equal(testObject.current(), undefined); }); - function toArray(historyNavigator: HistoryNavigator): string[] { - let result: string[] = []; + function toArray(historyNavigator: HistoryNavigator): Array { + let result: Array = []; historyNavigator.first(); if (historyNavigator.current()) { do { - result.push(historyNavigator.current()); + result.push(historyNavigator.current()!); } while (historyNavigator.next()); } return result; diff --git a/src/vs/base/test/common/json.test.ts b/src/vs/base/test/common/json.test.ts index 86988f99f92..bd66bc67028 100644 --- a/src/vs/base/test/common/json.test.ts +++ b/src/vs/base/test/common/json.test.ts @@ -9,23 +9,23 @@ import { import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; function assertKinds(text: string, ...kinds: SyntaxKind[]): void { - var scanner = createScanner(text); - var kind: SyntaxKind; + let scanner = createScanner(text); + let kind: SyntaxKind; while ((kind = scanner.scan()) !== SyntaxKind.EOF) { assert.equal(kind, kinds.shift()); } assert.equal(kinds.length, 0); } function assertScanError(text: string, expectedKind: SyntaxKind, scanError: ScanError): void { - var scanner = createScanner(text); + let scanner = createScanner(text); scanner.scan(); assert.equal(scanner.getToken(), expectedKind); assert.equal(scanner.getTokenError(), scanError); } function assertValidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: ParseError[] = []; - var actual = parse(input, errors, options); + let errors: ParseError[] = []; + let actual = parse(input, errors, options); if (errors.length !== 0) { assert(false, getParseErrorMessage(errors[0].error)); @@ -34,16 +34,16 @@ function assertValidParse(input: string, expected: any, options?: ParseOptions): } function assertInvalidParse(input: string, expected: any, options?: ParseOptions): void { - var errors: ParseError[] = []; - var actual = parse(input, errors, options); + let errors: ParseError[] = []; + let actual = parse(input, errors, options); assert(errors.length > 0); assert.deepEqual(actual, expected); } function assertTree(input: string, expected: any, expectedErrors: number[] = [], options?: ParseOptions): void { - var errors: ParseError[] = []; - var actual = parseTree(input, errors, options); + let errors: ParseError[] = []; + let actual = parseTree(input, errors, options); assert.deepEqual(errors.map(e => e.error, expected), expectedErrors); let checkParent = (node: Node) => { diff --git a/src/vs/base/test/common/jsonEdit.test.ts b/src/vs/base/test/common/jsonEdit.test.ts index a7c5978d96b..194f246a402 100644 --- a/src/vs/base/test/common/jsonEdit.test.ts +++ b/src/vs/base/test/common/jsonEdit.test.ts @@ -132,31 +132,31 @@ suite('JSON - edits', () => { test('remove item in array with one item', () => { let content = '[\n 1\n]'; - let edits = setProperty(content, [0], void 0, formatterOptions); + let edits = setProperty(content, [0], undefined, formatterOptions); assertEdit(content, edits, '[]'); }); test('remove item in the middle of the array', () => { let content = '[\n 1,\n 2,\n 3\n]'; - let edits = setProperty(content, [1], void 0, formatterOptions); + let edits = setProperty(content, [1], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n 3\n]'); }); test('remove last item in the array', () => { let content = '[\n 1,\n 2,\n "bar"\n]'; - let edits = setProperty(content, [2], void 0, formatterOptions); + let edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n 2\n]'); }); test('remove last item in the array if ends with comma', () => { let content = '[\n 1,\n "foo",\n "bar",\n]'; - let edits = setProperty(content, [2], void 0, formatterOptions); + let edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '[\n 1,\n "foo"\n]'); }); test('remove last item in the array if there is a comment in the beginning', () => { let content = '// This is a comment\n[\n 1,\n "foo",\n "bar"\n]'; - let edits = setProperty(content, [2], void 0, formatterOptions); + let edits = setProperty(content, [2], undefined, formatterOptions); assertEdit(content, edits, '// This is a comment\n[\n 1,\n "foo"\n]'); }); diff --git a/src/vs/base/test/common/jsonFormatter.test.ts b/src/vs/base/test/common/jsonFormatter.test.ts index 2ef51e1b383..71aa3d8d314 100644 --- a/src/vs/base/test/common/jsonFormatter.test.ts +++ b/src/vs/base/test/common/jsonFormatter.test.ts @@ -8,15 +8,15 @@ import * as assert from 'assert'; suite('JSON - formatter', () => { function format(content: string, expected: string, insertSpaces = true) { - let range: Formatter.Range | undefined = void 0; - var rangeStart = content.indexOf('|'); - var rangeEnd = content.lastIndexOf('|'); + let range: Formatter.Range | undefined = undefined; + const rangeStart = content.indexOf('|'); + const rangeEnd = content.lastIndexOf('|'); if (rangeStart !== -1 && rangeEnd !== -1) { content = content.substring(0, rangeStart) + content.substring(rangeStart + 1, rangeEnd) + content.substring(rangeEnd + 1); range = { offset: rangeStart, length: rangeEnd - rangeStart }; } - var edits = Formatter.format(content, range, { tabSize: 2, insertSpaces: insertSpaces, eol: '\n' }); + const edits = Formatter.format(content, range, { tabSize: 2, insertSpaces: insertSpaces, eol: '\n' }); let lastEditOffset = content.length; for (let i = edits.length - 1; i >= 0; i--) { @@ -32,11 +32,11 @@ suite('JSON - formatter', () => { } test('object - single property', () => { - var content = [ + const content = [ '{"x" : 1}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": 1', '}' @@ -45,11 +45,11 @@ suite('JSON - formatter', () => { format(content, expected); }); test('object - multiple properties', () => { - var content = [ + const content = [ '{"x" : 1, "y" : "foo", "z" : true}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": 1,', ' "y": "foo",', @@ -60,11 +60,11 @@ suite('JSON - formatter', () => { format(content, expected); }); test('object - no properties ', () => { - var content = [ + const content = [ '{"x" : { }, "y" : {}}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": {},', ' "y": {}', @@ -74,11 +74,11 @@ suite('JSON - formatter', () => { format(content, expected); }); test('object - nesting', () => { - var content = [ + const content = [ '{"x" : { "y" : { "z" : { }}, "a": true}}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "x": {', ' "y": {', @@ -93,11 +93,11 @@ suite('JSON - formatter', () => { }); test('array - single items', () => { - var content = [ + const content = [ '["[]"]' ].join('\n'); - var expected = [ + const expected = [ '[', ' "[]"', ']' @@ -107,11 +107,11 @@ suite('JSON - formatter', () => { }); test('array - multiple items', () => { - var content = [ + const content = [ '[true,null,1.2]' ].join('\n'); - var expected = [ + const expected = [ '[', ' true,', ' null,', @@ -123,11 +123,11 @@ suite('JSON - formatter', () => { }); test('array - no items', () => { - var content = [ + const content = [ '[ ]' ].join('\n'); - var expected = [ + const expected = [ '[]' ].join('\n'); @@ -135,11 +135,11 @@ suite('JSON - formatter', () => { }); test('array - nesting', () => { - var content = [ + const content = [ '[ [], [ [ {} ], "a" ] ]' ].join('\n'); - var expected = [ + const expected = [ '[', ' [],', ' [', @@ -155,11 +155,11 @@ suite('JSON - formatter', () => { }); test('syntax errors', () => { - var content = [ + const content = [ '[ null 1.2 ]' ].join('\n'); - var expected = [ + const expected = [ '[', ' null 1.2', ']', @@ -169,7 +169,7 @@ suite('JSON - formatter', () => { }); test('empty lines', () => { - var content = [ + const content = [ '{', '"a": true,', '', @@ -177,7 +177,7 @@ suite('JSON - formatter', () => { '}', ].join('\n'); - var expected = [ + const expected = [ '{', '\t"a": true,', '\t"b": true', @@ -187,14 +187,14 @@ suite('JSON - formatter', () => { format(content, expected, false); }); test('single line comment', () => { - var content = [ + const content = [ '[ ', '//comment', '"foo", "bar"', '] ' ].join('\n'); - var expected = [ + const expected = [ '[', ' //comment', ' "foo",', @@ -205,14 +205,14 @@ suite('JSON - formatter', () => { format(content, expected); }); test('block line comment', () => { - var content = [ + const content = [ '[{', ' /*comment*/ ', '"foo" : true', '}] ' ].join('\n'); - var expected = [ + const expected = [ '[', ' {', ' /*comment*/', @@ -224,13 +224,13 @@ suite('JSON - formatter', () => { format(content, expected); }); test('single line comment on same line', () => { - var content = [ + const content = [ ' { ', ' "a": {}// comment ', ' } ' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": {} // comment ', '}', @@ -239,12 +239,12 @@ suite('JSON - formatter', () => { format(content, expected); }); test('single line comment on same line 2', () => { - var content = [ + const content = [ '{ //comment', '}' ].join('\n'); - var expected = [ + const expected = [ '{ //comment', '}' ].join('\n'); @@ -252,13 +252,13 @@ suite('JSON - formatter', () => { format(content, expected); }); test('block comment on same line', () => { - var content = [ + const content = [ '{ "a": {}, /*comment*/ ', ' /*comment*/ "b": {}, ', ' "c": {/*comment*/} } ', ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": {}, /*comment*/', ' /*comment*/ "b": {},', @@ -270,14 +270,14 @@ suite('JSON - formatter', () => { }); test('block comment on same line advanced', () => { - var content = [ + const content = [ ' { "d": [', ' null', ' ] /*comment*/', ' ,"e": /*comment*/ [null] }', ].join('\n'); - var expected = [ + const expected = [ '{', ' "d": [', ' null', @@ -292,12 +292,12 @@ suite('JSON - formatter', () => { }); test('multiple block comments on same line', () => { - var content = [ + const content = [ '{ "a": {} /*comment*/, /*comment*/ ', ' /*comment*/ "b": {} /*comment*/ } ' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": {} /*comment*/, /*comment*/', ' /*comment*/ "b": {} /*comment*/', @@ -307,12 +307,12 @@ suite('JSON - formatter', () => { format(content, expected); }); test('multiple mixed comments on same line', () => { - var content = [ + const content = [ '[ /*comment*/ /*comment*/ // comment ', ']' ].join('\n'); - var expected = [ + const expected = [ '[ /*comment*/ /*comment*/ // comment ', ']' ].join('\n'); @@ -321,13 +321,13 @@ suite('JSON - formatter', () => { }); test('range', () => { - var content = [ + const content = [ '{ "a": {},', '|"b": [null, null]|', '} ' ].join('\n'); - var expected = [ + const expected = [ '{ "a": {},', '"b": [', ' null,', @@ -340,14 +340,14 @@ suite('JSON - formatter', () => { }); test('range with existing indent', () => { - var content = [ + const content = [ '{ "a": {},', ' |"b": [null],', '"c": {}', '}|' ].join('\n'); - var expected = [ + const expected = [ '{ "a": {},', ' "b": [', ' null', @@ -360,14 +360,14 @@ suite('JSON - formatter', () => { }); test('range with existing indent - tabs', () => { - var content = [ + const content = [ '{ "a": {},', '| "b": [null], ', '"c": {}', '} | ' ].join('\n'); - var expected = [ + const expected = [ '{ "a": {},', '\t"b": [', '\t\tnull', @@ -381,7 +381,7 @@ suite('JSON - formatter', () => { test('block comment none-line breaking symbols', () => { - var content = [ + const content = [ '{ "a": [ 1', '/* comment */', ', 2', @@ -394,7 +394,7 @@ suite('JSON - formatter', () => { '}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a": [', ' 1', @@ -413,7 +413,7 @@ suite('JSON - formatter', () => { format(content, expected); }); test('line comment after none-line breaking symbols', () => { - var content = [ + const content = [ '{ "a":', '// comment', 'null,', @@ -424,7 +424,7 @@ suite('JSON - formatter', () => { '}' ].join('\n'); - var expected = [ + const expected = [ '{', ' "a":', ' // comment', diff --git a/src/vs/base/test/common/keyCodes.test.ts b/src/vs/base/test/common/keyCodes.test.ts index bc746461f94..f8b2b55a452 100644 --- a/src/vs/base/test/common/keyCodes.test.ts +++ b/src/vs/base/test/common/keyCodes.test.ts @@ -9,46 +9,46 @@ import { OperatingSystem } from 'vs/base/common/platform'; suite('keyCodes', () => { - function testBinaryEncoding(expected: Keybinding, k: number, OS: OperatingSystem): void { + function testBinaryEncoding(expected: Keybinding | null, k: number, OS: OperatingSystem): void { assert.deepEqual(createKeybinding(k, OS), expected); } test('MAC binary encoding', () => { - function test(expected: Keybinding, k: number): void { + function test(expected: Keybinding | null, k: number): void { testBinaryEncoding(expected, k, OperatingSystem.Macintosh); } test(null, 0); - test(new SimpleKeybinding(false, false, false, false, KeyCode.Enter), KeyCode.Enter); - test(new SimpleKeybinding(true, false, false, false, KeyCode.Enter), KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, false, true, false, KeyCode.Enter), KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(true, false, true, false, KeyCode.Enter), KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, true, false, false, KeyCode.Enter), KeyMod.Shift | KeyCode.Enter); - test(new SimpleKeybinding(true, true, false, false, KeyCode.Enter), KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, true, true, false, KeyCode.Enter), KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(true, true, true, false, KeyCode.Enter), KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, false, false, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyCode.Enter); - test(new SimpleKeybinding(true, false, false, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, false, true, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(true, false, true, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, true, false, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter); - test(new SimpleKeybinding(true, true, false, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, true, true, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(true, true, true, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, false, false, false, KeyCode.Enter).toChord(), KeyCode.Enter); + test(new SimpleKeybinding(true, false, false, false, KeyCode.Enter).toChord(), KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, false, true, false, KeyCode.Enter).toChord(), KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(true, false, true, false, KeyCode.Enter).toChord(), KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, true, false, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyCode.Enter); + test(new SimpleKeybinding(true, true, false, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, true, true, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(true, true, true, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, false, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyCode.Enter); + test(new SimpleKeybinding(true, false, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, false, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(true, false, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, true, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter); + test(new SimpleKeybinding(true, true, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, true, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(true, true, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); test( - new ChordKeybinding( + new ChordKeybinding([ new SimpleKeybinding(false, false, false, false, KeyCode.Enter), new SimpleKeybinding(false, false, false, false, KeyCode.Tab) - ), + ]), KeyChord(KeyCode.Enter, KeyCode.Tab) ); test( - new ChordKeybinding( + new ChordKeybinding([ new SimpleKeybinding(false, false, false, true, KeyCode.KEY_Y), new SimpleKeybinding(false, false, false, false, KeyCode.KEY_Z) - ), + ]), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z) ); }); @@ -57,40 +57,40 @@ suite('keyCodes', () => { [OperatingSystem.Linux, OperatingSystem.Windows].forEach((OS) => { - function test(expected: Keybinding, k: number): void { + function test(expected: Keybinding | null, k: number): void { testBinaryEncoding(expected, k, OS); } test(null, 0); - test(new SimpleKeybinding(false, false, false, false, KeyCode.Enter), KeyCode.Enter); - test(new SimpleKeybinding(false, false, false, true, KeyCode.Enter), KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, false, true, false, KeyCode.Enter), KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(false, false, true, true, KeyCode.Enter), KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, true, false, false, KeyCode.Enter), KeyMod.Shift | KeyCode.Enter); - test(new SimpleKeybinding(false, true, false, true, KeyCode.Enter), KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(false, true, true, false, KeyCode.Enter), KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(false, true, true, true, KeyCode.Enter), KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(true, false, false, false, KeyCode.Enter), KeyMod.CtrlCmd | KeyCode.Enter); - test(new SimpleKeybinding(true, false, false, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(true, false, true, false, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(true, false, true, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(true, true, false, false, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter); - test(new SimpleKeybinding(true, true, false, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); - test(new SimpleKeybinding(true, true, true, false, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); - test(new SimpleKeybinding(true, true, true, true, KeyCode.Enter), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, false, false, false, KeyCode.Enter).toChord(), KeyCode.Enter); + test(new SimpleKeybinding(false, false, false, true, KeyCode.Enter).toChord(), KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, false, true, false, KeyCode.Enter).toChord(), KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(false, false, true, true, KeyCode.Enter).toChord(), KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, true, false, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyCode.Enter); + test(new SimpleKeybinding(false, true, false, true, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(false, true, true, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(false, true, true, true, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(true, false, false, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyCode.Enter); + test(new SimpleKeybinding(true, false, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(true, false, true, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(true, false, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(true, true, false, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter); + test(new SimpleKeybinding(true, true, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter); + test(new SimpleKeybinding(true, true, true, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.Enter); + test(new SimpleKeybinding(true, true, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter); test( - new ChordKeybinding( + new ChordKeybinding([ new SimpleKeybinding(false, false, false, false, KeyCode.Enter), new SimpleKeybinding(false, false, false, false, KeyCode.Tab) - ), + ]), KeyChord(KeyCode.Enter, KeyCode.Tab) ); test( - new ChordKeybinding( + new ChordKeybinding([ new SimpleKeybinding(true, false, false, false, KeyCode.KEY_Y), new SimpleKeybinding(false, false, false, false, KeyCode.KEY_Z) - ), + ]), KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z) ); diff --git a/src/vs/base/test/common/labels.test.ts b/src/vs/base/test/common/labels.test.ts index d34a08e58c6..a3e54c16232 100644 --- a/src/vs/base/test/common/labels.test.ts +++ b/src/vs/base/test/common/labels.test.ts @@ -55,7 +55,7 @@ suite('Labels', () => { assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\C']), ['…\\c', '…\\C']); // empty or null - assert.deepEqual(labels.shorten(['', null]), ['.\\', null]); + assert.deepEqual(labels.shorten(['', null!]), ['.\\', null]); assert.deepEqual(labels.shorten(['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']), ['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']); assert.deepEqual(labels.shorten(['a', 'a\\b', 'b']), ['a', 'a\\b', 'b']); @@ -103,7 +103,7 @@ suite('Labels', () => { assert.deepEqual(labels.shorten(['a/b/c', 'd/b/C']), ['…/c', '…/C']); // empty or null - assert.deepEqual(labels.shorten(['', null]), ['./', null]); + assert.deepEqual(labels.shorten(['', null!]), ['./', null]); assert.deepEqual(labels.shorten(['a', 'a/b', 'a/b/c', 'd/b/c', 'd/b']), ['a', 'a/b', 'a/b/c', 'd/b/c', 'd/b']); assert.deepEqual(labels.shorten(['a', 'a/b', 'b']), ['a', 'a/b', 'b']); @@ -164,4 +164,19 @@ suite('Labels', () => { assert.equal(labels.getBaseLabel('c:\\some\\folder\\file.txt'), 'file.txt'); assert.equal(labels.getBaseLabel('c:\\some\\folder'), 'folder'); }); + + test('mnemonicButtonLabel', () => { + assert.equal(labels.mnemonicButtonLabel('Hello World'), 'Hello World'); + assert.equal(labels.mnemonicButtonLabel(''), ''); + if (platform.isWindows) { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello && World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do ¬ Save && Continue'); + } else if (platform.isMacintosh) { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do not Save & Continue'); + } else { + assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World'); + assert.equal(labels.mnemonicButtonLabel('Do &¬ Save & Continue'), 'Do _not Save & Continue'); + } + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 31269d7f2cb..29e93e64a41 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IDisposable, dispose, ReferenceCollection } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, ReferenceCollection, Disposable as DisposableBase, toDisposable } from 'vs/base/common/lifecycle'; class Disposable implements IDisposable { isDisposed = false; @@ -49,6 +49,38 @@ suite('Lifecycle', () => { }); }); +suite('DisposableBase', () => { + test('register should not leak if object has already been disposed', () => { + let aCount = 0; + let bCount = 0; + + const disposable = new class extends DisposableBase { + register(other: IDisposable) { + this._register(other); + } + }; + + disposable.register(toDisposable(() => ++aCount)); + + assert.strictEqual(aCount, 0); + assert.strictEqual(bCount, 0); + + disposable.dispose(); + assert.strictEqual(aCount, 1); + assert.strictEqual(bCount, 0); + + // Any newly added disposables should be disposed of immediately + disposable.register(toDisposable(() => ++bCount)); + assert.strictEqual(aCount, 1); + assert.strictEqual(bCount, 1); + + // Further dispose calls should have no effect + disposable.dispose(); + assert.strictEqual(aCount, 1); + assert.strictEqual(bCount, 1); + }); +}); + suite('Reference Collection', () => { class Collection extends ReferenceCollection { private _count = 0; diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts index caaf3736cb0..7dc178dbbc0 100644 --- a/src/vs/base/test/common/linkedList.test.ts +++ b/src/vs/base/test/common/linkedList.test.ts @@ -52,6 +52,14 @@ suite('LinkedList', function () { disp = list.push(2); disp(); assertElements(list, 0, 1); + + list = new LinkedList(); + list.push(0); + list.push(1); + disp = list.push(2); + disp(); + disp(); + assertElements(list, 0, 1); }); test('Push/toArray', () => { @@ -61,15 +69,7 @@ suite('LinkedList', function () { list.push('far'); list.push('boo'); - assert.deepEqual( - list.toArray(), - [ - 'foo', - 'bar', - 'far', - 'boo', - ] - ); + assertElements(list, 'foo', 'bar', 'far', 'boo'); }); test('unshift/Iter', () => { @@ -109,15 +109,26 @@ suite('LinkedList', function () { list.unshift('bar'); list.unshift('far'); list.unshift('boo'); + assertElements(list, 'boo', 'far', 'bar', 'foo'); + }); + + test('pop/unshift', function () { + let list = new LinkedList(); + list.push('a'); + list.push('b'); + + assertElements(list, 'a', 'b'); + + let a = list.shift(); + assert.equal(a, 'a'); + assertElements(list, 'b'); + + list.unshift('a'); + assertElements(list, 'a', 'b'); + + let b = list.pop(); + assert.equal(b, 'b'); + assertElements(list, 'a'); - assert.deepEqual( - list.toArray(), - [ - 'boo', - 'far', - 'bar', - 'foo', - ] - ); }); }); diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 47e9b3ca9e5..245cccdf776 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map'; +import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, mapToSerializable, serializableToMap } from 'vs/base/common/map'; import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { IteratorResult } from 'vs/base/common/iterator'; @@ -140,7 +140,7 @@ suite('Map', () => { assert.strictEqual(cache.size, 5); assert.deepStrictEqual(cache.keys(), [3, 4, 5, 6, 7]); let values: number[] = []; - [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key))); + [3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [3, 4, 5, 6, 7]); }); @@ -155,7 +155,7 @@ suite('Map', () => { cache.peek(4); assert.deepStrictEqual(cache.keys(), [1, 2, 4, 5, 3]); let values: number[] = []; - [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key))); + [1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [1, 2, 3, 4, 5]); }); @@ -177,7 +177,7 @@ suite('Map', () => { assert.deepEqual(cache.size, 15); let values: number[] = []; for (let i = 6; i <= 20; i++) { - values.push(cache.get(i)); + values.push(cache.get(i)!); assert.strictEqual(cache.get(i), i); } assert.deepStrictEqual(cache.values(), values); @@ -194,7 +194,7 @@ suite('Map', () => { assert.strictEqual(cache.size, 5); assert.deepStrictEqual(cache.keys(), [7, 8, 9, 10, 11]); let values: number[] = []; - cache.keys().forEach(key => values.push(cache.get(key))); + cache.keys().forEach(key => values.push(cache.get(key)!)); assert.deepStrictEqual(values, [7, 8, 9, 10, 11]); assert.deepStrictEqual(cache.values(), values); }); @@ -226,6 +226,50 @@ suite('Map', () => { }); }); + test('LinkedMap - delete Head and Tail', function () { + const map = new LinkedMap(); + + assert.equal(map.size, 0); + + map.set('1', 1); + assert.equal(map.size, 1); + map.delete('1'); + assert.equal(map.get('1'), undefined); + assert.equal(map.size, 0); + assert.equal(map.keys().length, 0); + }); + + test('LinkedMap - delete Head', function () { + const map = new LinkedMap(); + + assert.equal(map.size, 0); + + map.set('1', 1); + map.set('2', 2); + assert.equal(map.size, 2); + map.delete('1'); + assert.equal(map.get('2'), 2); + assert.equal(map.size, 1); + assert.equal(map.keys().length, 1); + assert.equal(map.keys()[0], 2); + }); + + test('LinkedMap - delete Tail', function () { + const map = new LinkedMap(); + + assert.equal(map.size, 0); + + map.set('1', 1); + map.set('2', 2); + assert.equal(map.size, 2); + map.delete('2'); + assert.equal(map.get('1'), 1); + assert.equal(map.size, 1); + assert.equal(map.keys().length, 1); + assert.equal(map.keys()[0], 1); + }); + + test('PathIterator', () => { const iter = new PathIterator(); iter.reset('file:///usr/bin/file.txt'); @@ -420,25 +464,25 @@ suite('Map', () => { let item: IteratorResult; let iter = map.findSuperstr('/user'); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 2); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 1); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 3); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, undefined); assert.equal(item.done, true); iter = map.findSuperstr('/usr'); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, 4); assert.equal(item.done, false); - item = iter.next(); + item = iter!.next(); assert.equal(item.value, undefined); assert.equal(item.done, true); @@ -584,4 +628,17 @@ suite('Map', () => { // assert.equal(map.get(windowsFile), 'true'); // assert.equal(map.get(uncFile), 'true'); // }); + + test('mapToSerializable / serializableToMap', function () { + const map = new Map(); + map.set('1', 'foo'); + map.set('2', null!); + map.set('3', 'bar'); + + const map2 = serializableToMap(mapToSerializable(map)); + assert.equal(map2.size, map.size); + assert.equal(map2.get('1'), map.get('1')); + assert.equal(map2.get('2'), map.get('2')); + assert.equal(map2.get('3'), map.get('3')); + }); }); diff --git a/src/vs/base/test/common/marshalling.test.ts b/src/vs/base/test/common/marshalling.test.ts index 16c6e8f5011..c886abea556 100644 --- a/src/vs/base/test/common/marshalling.test.ts +++ b/src/vs/base/test/common/marshalling.test.ts @@ -20,9 +20,9 @@ suite('Marshalling', () => { }); test('URI', () => { - let value = URI.from({ scheme: 'file', authority: 'server', path: '/shares/c#files', query: 'q', fragment: 'f' }); - let raw = stringify(value); - let clone = parse(raw); + const value = URI.from({ scheme: 'file', authority: 'server', path: '/shares/c#files', query: 'q', fragment: 'f' }); + const raw = stringify(value); + const clone = parse(raw); assert.equal(value.scheme, clone.scheme); assert.equal(value.authority, clone.authority); @@ -32,7 +32,7 @@ suite('Marshalling', () => { }); test('Bug 16793:# in folder name => mirror models get out of sync', () => { - var uri1 = URI.file('C:\\C#\\file.txt'); + const uri1 = URI.file('C:\\C#\\file.txt'); assert.equal(parse(stringify(uri1)).toString(), uri1.toString()); }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/mime.test.ts b/src/vs/base/test/common/mime.test.ts index 75f6d531fe7..4cea0feb565 100644 --- a/src/vs/base/test/common/mime.test.ts +++ b/src/vs/base/test/common/mime.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { guessMimeTypes, registerTextMime, suggestFilename } from 'vs/base/common/mime'; suite('Mime', () => { + test('Dynamically Register Text Mime', () => { let guess = guessMimeTypes('foo.monaco'); assert.deepEqual(guess, ['application/unknown']); @@ -56,6 +57,11 @@ suite('Mime', () => { registerTextMime({ id: 'docker', filepattern: 'dockerfile*', mime: 'text/looser' }); guess = guessMimeTypes('dockerfile'); assert.deepEqual(guess, ['text/winner', 'text/plain']); + + registerTextMime({ id: 'azure-looser', mime: 'text/azure-looser', firstline: /azure/ }); + registerTextMime({ id: 'azure-winner', mime: 'text/azure-winner', firstline: /azure/ }); + guess = guessMimeTypes('azure', 'azure'); + assert.deepEqual(guess, ['text/azure-winner', 'text/plain']); }); test('Specificity priority 1', () => { diff --git a/src/vs/base/test/common/objects.test.ts b/src/vs/base/test/common/objects.test.ts index 0ce34acb432..37e428d8a86 100644 --- a/src/vs/base/test/common/objects.test.ts +++ b/src/vs/base/test/common/objects.test.ts @@ -193,13 +193,13 @@ suite('Objects', () => { three: { 3: true }, - four: void 0 + four: undefined }; diff = objects.distinct(base, obj); assert.deepEqual(diff, { one: null, - four: void 0 + four: undefined }); obj = { diff --git a/src/vs/base/test/common/octicon.test.ts b/src/vs/base/test/common/octicon.test.ts index 9135c38e1e2..c91055525d2 100644 --- a/src/vs/base/test/common/octicon.test.ts +++ b/src/vs/base/test/common/octicon.test.ts @@ -8,7 +8,7 @@ import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon' export interface IOcticonFilter { // Returns null if word doesn't match. - (query: string, target: { text: string, octiconOffsets?: number[] }): IMatch[]; + (query: string, target: { text: string, octiconOffsets?: number[] }): IMatch[] | null; } function filterOk(filter: IOcticonFilter, word: string, target: { text: string, octiconOffsets?: number[] }, highlights?: { start: number; end: number; }[]) { diff --git a/src/vs/base/test/common/paging.test.ts b/src/vs/base/test/common/paging.test.ts index fe332c0bd31..3ce69663384 100644 --- a/src/vs/base/test/common/paging.test.ts +++ b/src/vs/base/test/common/paging.test.ts @@ -8,7 +8,7 @@ import { IPager, PagedModel } from 'vs/base/common/paging'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError, canceled } from 'vs/base/common/errors'; -function getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable { +function getPage(pageIndex: number, cancellationToken: CancellationToken): Promise { if (cancellationToken.isCancellationRequested) { return Promise.reject(canceled()); } @@ -21,9 +21,9 @@ class TestPager implements IPager { readonly firstPage = [0, 1, 2, 3, 4]; readonly pageSize = 5; readonly total = 100; - readonly getPage: (pageIndex: number, cancellationToken: CancellationToken) => Thenable; + readonly getPage: (pageIndex: number, cancellationToken: CancellationToken) => Promise; - constructor(getPageFn?: (pageIndex: number, cancellationToken: CancellationToken) => Thenable) { + constructor(getPageFn?: (pageIndex: number, cancellationToken: CancellationToken) => Promise) { this.getPage = getPageFn || getPage; } } diff --git a/src/vs/base/test/common/path.test.ts b/src/vs/base/test/common/path.test.ts new file mode 100644 index 00000000000..cfceaedc9e9 --- /dev/null +++ b/src/vs/base/test/common/path.test.ts @@ -0,0 +1,824 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace +// Copied from: https://github.com/nodejs/node/tree/43dd49c9782848c25e5b03448c8a0f923f13c158 + +// Copyright Joyent, Inc. and other Node 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: +// +// 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. + +import * as assert from 'assert'; +import * as path from 'vs/base/common/path'; +import { isWindows } from 'vs/base/common/platform'; + +suite('Paths (Node Implementation)', () => { + test('join', () => { + const failures = [] as string[]; + const backslashRE = /\\/g; + + const joinTests: any = [ + [[path.posix.join, path.win32.join], + // arguments result + [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], + [[], '.'], + [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], + [['/foo', '../../../bar'], '/bar'], + [['foo', '../../../bar'], '../../bar'], + [['foo/', '../../../bar'], '../../bar'], + [['foo/x', '../../../bar'], '../bar'], + [['foo/x', './bar'], 'foo/x/bar'], + [['foo/x/', './bar'], 'foo/x/bar'], + [['foo/x/', '.', 'bar'], 'foo/x/bar'], + [['./'], './'], + [['.', './'], './'], + [['.', '.', '.'], '.'], + [['.', './', '.'], '.'], + [['.', '/./', '.'], '.'], + [['.', '/////./', '.'], '.'], + [['.'], '.'], + [['', '.'], '.'], + [['', 'foo'], 'foo'], + [['foo', '/bar'], 'foo/bar'], + [['', '/foo'], '/foo'], + [['', '', '/foo'], '/foo'], + [['', '', 'foo'], 'foo'], + [['foo', ''], 'foo'], + [['foo/', ''], 'foo/'], + [['foo', '', '/bar'], 'foo/bar'], + [['./', '..', '/foo'], '../foo'], + [['./', '..', '..', '/foo'], '../../foo'], + [['.', '..', '..', '/foo'], '../../foo'], + [['', '..', '..', '/foo'], '../../foo'], + [['/'], '/'], + [['/', '.'], '/'], + [['/', '..'], '/'], + [['/', '..', '..'], '/'], + [[''], '.'], + [['', ''], '.'], + [[' /foo'], ' /foo'], + [[' ', 'foo'], ' /foo'], + [[' ', '.'], ' '], + [[' ', '/'], ' /'], + [[' ', ''], ' '], + [['/', 'foo'], '/foo'], + [['/', '/foo'], '/foo'], + [['/', '//foo'], '/foo'], + [['/', '', '/foo'], '/foo'], + [['', '/', 'foo'], '/foo'], + [['', '/', '/foo'], '/foo'] + ] + ] + ]; + + // Windows-specific join tests + joinTests.push([ + path.win32.join, + joinTests[0][1].slice(0).concat( + [// arguments result + // UNC path expected + [['//foo/bar'], '\\\\foo\\bar\\'], + [['\\/foo/bar'], '\\\\foo\\bar\\'], + [['\\\\foo/bar'], '\\\\foo\\bar\\'], + // UNC path expected - server and share separate + [['//foo', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', 'bar'], '\\\\foo\\bar\\'], + [['//foo', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - questionable + [['//foo', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - even more questionable + [['', '//foo', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', '/bar'], '\\\\foo\\bar\\'], + // No UNC path expected (no double slash in first component) + [['\\', 'foo/bar'], '\\foo\\bar'], + [['\\', '/foo/bar'], '\\foo\\bar'], + [['', '/', '/foo/bar'], '\\foo\\bar'], + // No UNC path expected (no non-slashes in first component - + // questionable) + [['//', 'foo/bar'], '\\foo\\bar'], + [['//', '/foo/bar'], '\\foo\\bar'], + [['\\\\', '/', '/foo/bar'], '\\foo\\bar'], + [['//'], '\\'], + // No UNC path expected (share name missing - questionable). + [['//foo'], '\\foo'], + [['//foo/'], '\\foo\\'], + [['//foo', '/'], '\\foo\\'], + [['//foo', '', '/'], '\\foo\\'], + // No UNC path expected (too many leading slashes - questionable) + [['///foo/bar'], '\\foo\\bar'], + [['////foo', 'bar'], '\\foo\\bar'], + [['\\\\\\/foo/bar'], '\\foo\\bar'], + // Drive-relative vs drive-absolute paths. This merely describes the + // status quo, rather than being obviously right + [['c:'], 'c:.'], + [['c:.'], 'c:.'], + [['c:', ''], 'c:.'], + [['', 'c:'], 'c:.'], + [['c:.', '/'], 'c:.\\'], + [['c:.', 'file'], 'c:file'], + [['c:', '/'], 'c:\\'], + [['c:', 'file'], 'c:\\file'] + ] + ) + ]); + joinTests.forEach((test: any[]) => { + if (!Array.isArray(test[0])) { + test[0] = [test[0]]; + } + test[0].forEach((join: any) => { + test[1].forEach((test: any) => { + const actual = join.apply(null, test[0]); + const expected = test[1]; + // For non-Windows specific tests with the Windows join(), we need to try + // replacing the slashes since the non-Windows specific tests' `expected` + // use forward slashes + let actualAlt; + let os; + if (join === path.win32.join) { + actualAlt = actual.replace(backslashRE, '/'); + os = 'win32'; + } else { + os = 'posix'; + } + const message = + `path.${os}.join(${test[0].map(JSON.stringify).join(',')})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected && actualAlt !== expected) { + failures.push(`\n${message}`); + } + }); + }); + }); + assert.strictEqual(failures.length, 0, failures.join('')); + }); + + test('dirname', () => { + assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-11), + isWindows ? 'test\\common' : 'test/common'); + + assert.strictEqual(path.posix.dirname('/a/b/'), '/a'); + assert.strictEqual(path.posix.dirname('/a/b'), '/a'); + assert.strictEqual(path.posix.dirname('/a'), '/'); + assert.strictEqual(path.posix.dirname(''), '.'); + assert.strictEqual(path.posix.dirname('/'), '/'); + assert.strictEqual(path.posix.dirname('////'), '/'); + assert.strictEqual(path.posix.dirname('//a'), '//'); + assert.strictEqual(path.posix.dirname('foo'), '.'); + + assert.strictEqual(path.win32.dirname('c:\\'), 'c:\\'); + assert.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\'); + assert.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\'); + assert.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo'); + assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo'); + assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar'); + assert.strictEqual(path.win32.dirname('\\'), '\\'); + assert.strictEqual(path.win32.dirname('\\foo'), '\\'); + assert.strictEqual(path.win32.dirname('\\foo\\'), '\\'); + assert.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo'); + assert.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo'); + assert.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar'); + assert.strictEqual(path.win32.dirname('c:'), 'c:'); + assert.strictEqual(path.win32.dirname('c:foo'), 'c:'); + assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:'); + assert.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo'); + assert.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo'); + assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar'); + assert.strictEqual(path.win32.dirname('file:stream'), '.'); + assert.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir'); + assert.strictEqual(path.win32.dirname('\\\\unc\\share'), + '\\\\unc\\share'); + assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'), + '\\\\unc\\share\\'); + assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'), + '\\\\unc\\share\\'); + assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'), + '\\\\unc\\share\\foo'); + assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'), + '\\\\unc\\share\\foo'); + assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'), + '\\\\unc\\share\\foo\\bar'); + assert.strictEqual(path.win32.dirname('/a/b/'), '/a'); + assert.strictEqual(path.win32.dirname('/a/b'), '/a'); + assert.strictEqual(path.win32.dirname('/a'), '/'); + assert.strictEqual(path.win32.dirname(''), '.'); + assert.strictEqual(path.win32.dirname('/'), '/'); + assert.strictEqual(path.win32.dirname('////'), '/'); + assert.strictEqual(path.win32.dirname('foo'), '.'); + + // Tests from VSCode + + function assertDirname(p: string, expected: string, win = false) { + const actual = win ? path.win32.dirname(p) : path.posix.dirname(p); + + if (actual !== expected) { + assert.fail(`${p}: expected: ${expected}, ours: ${actual}`); + } + } + + assertDirname('foo/bar', 'foo'); + assertDirname('foo\\bar', 'foo', true); + assertDirname('/foo/bar', '/foo'); + assertDirname('\\foo\\bar', '\\foo', true); + assertDirname('/foo', '/'); + assertDirname('\\foo', '\\', true); + assertDirname('/', '/'); + assertDirname('\\', '\\', true); + assertDirname('foo', '.'); + assertDirname('f', '.'); + assertDirname('f/', '.'); + assertDirname('/folder/', '/'); + assertDirname('c:\\some\\file.txt', 'c:\\some', true); + assertDirname('c:\\some', 'c:\\', true); + assertDirname('c:\\', 'c:\\', true); + assertDirname('c:', 'c:', true); + assertDirname('\\\\server\\share\\some\\path', '\\\\server\\share\\some', true); + assertDirname('\\\\server\\share\\some', '\\\\server\\share\\', true); + assertDirname('\\\\server\\share\\', '\\\\server\\share\\', true); + }); + + test('extname', () => { + const failures = [] as string[]; + const slashRE = /\//g; + + [ + [__filename, '.js'], + ['', ''], + ['/path/to/file', ''], + ['/path/to/file.ext', '.ext'], + ['/path.to/file.ext', '.ext'], + ['/path.to/file', ''], + ['/path.to/.file', ''], + ['/path.to/.file.ext', '.ext'], + ['/path/to/f.ext', '.ext'], + ['/path/to/..ext', '.ext'], + ['/path/to/..', ''], + ['file', ''], + ['file.ext', '.ext'], + ['.file', ''], + ['.file.ext', '.ext'], + ['/file', ''], + ['/file.ext', '.ext'], + ['/.file', ''], + ['/.file.ext', '.ext'], + ['.path/file.ext', '.ext'], + ['file.ext.ext', '.ext'], + ['file.', '.'], + ['.', ''], + ['./', ''], + ['.file.ext', '.ext'], + ['.file', ''], + ['.file.', '.'], + ['.file..', '.'], + ['..', ''], + ['../', ''], + ['..file.ext', '.ext'], + ['..file', '.file'], + ['..file.', '.'], + ['..file..', '.'], + ['...', '.'], + ['...ext', '.ext'], + ['....', '.'], + ['file.ext/', '.ext'], + ['file.ext//', '.ext'], + ['file/', ''], + ['file//', ''], + ['file./', '.'], + ['file.//', '.'], + ].forEach((test) => { + const expected = test[1]; + [path.posix.extname, path.win32.extname].forEach((extname) => { + let input = test[0]; + let os; + if (extname === path.win32.extname) { + input = input.replace(slashRE, '\\'); + os = 'win32'; + } else { + os = 'posix'; + } + const actual = extname(input); + const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) { + failures.push(`\n${message}`); + } + }); + { + const input = `C:${test[0].replace(slashRE, '\\')}`; + const actual = path.win32.extname(input); + const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) { + failures.push(`\n${message}`); + } + } + }); + assert.strictEqual(failures.length, 0, failures.join('')); + + // On Windows, backslash is a path separator. + assert.strictEqual(path.win32.extname('.\\'), ''); + assert.strictEqual(path.win32.extname('..\\'), ''); + assert.strictEqual(path.win32.extname('file.ext\\'), '.ext'); + assert.strictEqual(path.win32.extname('file.ext\\\\'), '.ext'); + assert.strictEqual(path.win32.extname('file\\'), ''); + assert.strictEqual(path.win32.extname('file\\\\'), ''); + assert.strictEqual(path.win32.extname('file.\\'), '.'); + assert.strictEqual(path.win32.extname('file.\\\\'), '.'); + + // On *nix, backslash is a valid name component like any other character. + assert.strictEqual(path.posix.extname('.\\'), ''); + assert.strictEqual(path.posix.extname('..\\'), '.\\'); + assert.strictEqual(path.posix.extname('file.ext\\'), '.ext\\'); + assert.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\'); + assert.strictEqual(path.posix.extname('file\\'), ''); + assert.strictEqual(path.posix.extname('file\\\\'), ''); + assert.strictEqual(path.posix.extname('file.\\'), '.\\'); + assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\'); + + // Tests from VSCode + assert.equal(path.extname('far.boo'), '.boo'); + assert.equal(path.extname('far.b'), '.b'); + assert.equal(path.extname('far.'), '.'); + assert.equal(path.extname('far.boo/boo.far'), '.far'); + assert.equal(path.extname('far.boo/boo'), ''); + }); + + test('resolve', () => { + const failures = [] as string[]; + const slashRE = /\//g; + const backslashRE = /\\/g; + + const resolveTests = [ + [path.win32.resolve, + // arguments result + [[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'], + [['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'], + [['c:/ignore', 'c:/some/file'], 'c:\\some\\file'], + [['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'], + [['.'], process.cwd()], + [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'], + [['c:/', '//'], 'c:\\'], + [['c:/', '//dir'], 'c:\\dir'], + [['c:/', '//server/share'], '\\\\server\\share\\'], + [['c:/', '//server//share'], '\\\\server\\share\\'], + [['c:/', '///some//dir'], 'c:\\some\\dir'], + [['C:\\foo\\tmp.3\\', '..\\tmp.3\\cycles\\root.js'], + 'C:\\foo\\tmp.3\\cycles\\root.js'] + ] + ], + [path.posix.resolve, + // arguments result + [[['/var/lib', '../', 'file/'], '/var/file'], + [['/var/lib', '/../', 'file/'], '/file'], + [['a/b/c/', '../../..'], process.cwd()], + [['.'], process.cwd()], + [['/some/dir', '.', '/absolute/'], '/absolute'], + [['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js'] + ] + ] + ]; + resolveTests.forEach((test) => { + const resolve = test[0]; + //@ts-ignore + test[1].forEach((test) => { + //@ts-ignore + const actual = resolve.apply(null, test[0]); + let actualAlt; + const os = resolve === path.win32.resolve ? 'win32' : 'posix'; + if (resolve === path.win32.resolve && !isWindows) { + actualAlt = actual.replace(backslashRE, '/'); + } + else if (resolve !== path.win32.resolve && isWindows) { + actualAlt = actual.replace(slashRE, '\\'); + } + + const expected = test[1]; + const message = + `path.${os}.resolve(${test[0].map(JSON.stringify).join(',')})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected && actualAlt !== expected) { + failures.push(`\n${message}`); + } + }); + }); + assert.strictEqual(failures.length, 0, failures.join('')); + + // if (isWindows) { + // // Test resolving the current Windows drive letter from a spawned process. + // // See https://github.com/nodejs/node/issues/7215 + // const currentDriveLetter = path.parse(process.cwd()).root.substring(0, 2); + // const resolveFixture = fixtures.path('path-resolve.js'); + // const spawnResult = child.spawnSync( + // process.argv[0], [resolveFixture, currentDriveLetter]); + // const resolvedPath = spawnResult.stdout.toString().trim(); + // assert.strictEqual(resolvedPath.toLowerCase(), process.cwd().toLowerCase()); + // } + }); + + test('basename', () => { + assert.strictEqual(path.basename(__filename), 'path.test.js'); + assert.strictEqual(path.basename(__filename, '.js'), 'path.test'); + assert.strictEqual(path.basename('.js', '.js'), ''); + assert.strictEqual(path.basename(''), ''); + assert.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext'); + assert.strictEqual(path.basename('/basename.ext'), 'basename.ext'); + assert.strictEqual(path.basename('basename.ext'), 'basename.ext'); + assert.strictEqual(path.basename('basename.ext/'), 'basename.ext'); + assert.strictEqual(path.basename('basename.ext//'), 'basename.ext'); + assert.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb'); + assert.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb'); + assert.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb'); + assert.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb'); + assert.strictEqual(path.basename('aaa/bbb', 'bb'), 'b'); + assert.strictEqual(path.basename('aaa/bbb', 'b'), 'bb'); + assert.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb'); + assert.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb'); + assert.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb'); + assert.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb'); + assert.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b'); + assert.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb'); + assert.strictEqual(path.basename('/aaa/bbb'), 'bbb'); + assert.strictEqual(path.basename('/aaa/'), 'aaa'); + assert.strictEqual(path.basename('/aaa/b'), 'b'); + assert.strictEqual(path.basename('/a/b'), 'b'); + assert.strictEqual(path.basename('//a'), 'a'); + assert.strictEqual(path.basename('a', 'a'), ''); + + // On Windows a backslash acts as a path separator. + assert.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext'); + assert.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext'); + assert.strictEqual(path.win32.basename('basename.ext'), 'basename.ext'); + assert.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext'); + assert.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext'); + assert.strictEqual(path.win32.basename('foo'), 'foo'); + assert.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb'); + assert.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb'); + assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb'); + assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb'); + assert.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b'); + assert.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb'); + assert.strictEqual(path.win32.basename('C:'), ''); + assert.strictEqual(path.win32.basename('C:.'), '.'); + assert.strictEqual(path.win32.basename('C:\\'), ''); + assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext'); + assert.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext'); + assert.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext'); + assert.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext'); + assert.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext'); + assert.strictEqual(path.win32.basename('C:foo'), 'foo'); + assert.strictEqual(path.win32.basename('file:stream'), 'file:stream'); + assert.strictEqual(path.win32.basename('a', 'a'), ''); + + // On unix a backslash is just treated as any other character. + assert.strictEqual(path.posix.basename('\\dir\\basename.ext'), + '\\dir\\basename.ext'); + assert.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext'); + assert.strictEqual(path.posix.basename('basename.ext'), 'basename.ext'); + assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\'); + assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\'); + assert.strictEqual(path.posix.basename('foo'), 'foo'); + + // POSIX filenames may include control characters + // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html + const controlCharFilename = `Icon${String.fromCharCode(13)}`; + assert.strictEqual(path.posix.basename(`/a/b/${controlCharFilename}`), + controlCharFilename); + + // Tests from VSCode + assert.equal(path.basename('foo/bar'), 'bar'); + assert.equal(path.posix.basename('foo\\bar'), 'foo\\bar'); + assert.equal(path.win32.basename('foo\\bar'), 'bar'); + assert.equal(path.basename('/foo/bar'), 'bar'); + assert.equal(path.posix.basename('\\foo\\bar'), '\\foo\\bar'); + assert.equal(path.win32.basename('\\foo\\bar'), 'bar'); + assert.equal(path.basename('./bar'), 'bar'); + assert.equal(path.posix.basename('.\\bar'), '.\\bar'); + assert.equal(path.win32.basename('.\\bar'), 'bar'); + assert.equal(path.basename('/bar'), 'bar'); + assert.equal(path.posix.basename('\\bar'), '\\bar'); + assert.equal(path.win32.basename('\\bar'), 'bar'); + assert.equal(path.basename('bar/'), 'bar'); + assert.equal(path.posix.basename('bar\\'), 'bar\\'); + assert.equal(path.win32.basename('bar\\'), 'bar'); + assert.equal(path.basename('bar'), 'bar'); + assert.equal(path.basename('////////'), ''); + assert.equal(path.posix.basename('\\\\\\\\'), '\\\\\\\\'); + assert.equal(path.win32.basename('\\\\\\\\'), ''); + }); + + test('relative', () => { + const failures = [] as string[]; + + const relativeTests = [ + [path.win32.relative, + // arguments result + [['c:/blah\\blah', 'd:/games', 'd:\\games'], + ['c:/aaaa/bbbb', 'c:/aaaa', '..'], + ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], + ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'], + ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], + ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], + ['c:/aaaa/bbbb', 'd:\\', 'd:\\'], + ['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'], + ['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'], + ['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'], + ['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'], + ['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'], + ['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['C:\\baz-quux', 'C:\\baz', '..\\baz'], + ['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'], + ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'], + ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'], + ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz'] + ] + ], + [path.posix.relative, + // arguments result + [['/var/lib', '/var', '..'], + ['/var/lib', '/bin', '../../bin'], + ['/var/lib', '/var/lib', ''], + ['/var/lib', '/var/apache', '../apache'], + ['/var/', '/var/lib', 'lib'], + ['/', '/var/lib', 'var/lib'], + ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'], + ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'], + ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'], + ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'], + ['/baz-quux', '/baz', '../baz'], + ['/baz', '/baz-quux', '../baz-quux'] + ] + ] + ]; + relativeTests.forEach((test) => { + const relative = test[0]; + //@ts-ignore + test[1].forEach((test) => { + //@ts-ignore + const actual = relative(test[0], test[1]); + const expected = test[2]; + const os = relative === path.win32.relative ? 'win32' : 'posix'; + const message = `path.${os}.relative(${ + test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) { + failures.push(`\n${message}`); + } + }); + }); + assert.strictEqual(failures.length, 0, failures.join('')); + }); + + test('normalize', () => { + assert.strictEqual(path.win32.normalize('./fixtures///b/../b/c.js'), + 'fixtures\\b\\c.js'); + assert.strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar'); + assert.strictEqual(path.win32.normalize('a//b//../b'), 'a\\b'); + assert.strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c'); + assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b'); + assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'), + '\\\\server\\share\\dir\\file.ext'); + assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z'); + assert.strictEqual(path.win32.normalize('C:'), 'C:.'); + assert.strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc'); + assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'), + 'C:..\\..\\def'); + assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\'); + assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream'); + assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\'); + assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar'); + assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz'); + assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\'); + assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..'); + assert.strictEqual(path.win32.normalize('..\\foo..\\..\\..\\bar'), + '..\\..\\bar'); + assert.strictEqual(path.win32.normalize('..\\...\\..\\.\\...\\..\\..\\bar'), + '..\\..\\bar'); + assert.strictEqual(path.win32.normalize('../../../foo/../../../bar'), + '..\\..\\..\\..\\..\\bar'); + assert.strictEqual(path.win32.normalize('../../../foo/../../../bar/../../'), + '..\\..\\..\\..\\..\\..\\'); + assert.strictEqual( + path.win32.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '..\\..\\' + ); + assert.strictEqual( + path.win32.normalize('../.../../foobar/../../../bar/../../baz'), + '..\\..\\..\\..\\baz' + ); + assert.strictEqual(path.win32.normalize('foo/bar\\baz'), 'foo\\bar\\baz'); + + assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), + 'fixtures/b/c.js'); + assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar'); + assert.strictEqual(path.posix.normalize('a//b//../b'), 'a/b'); + assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c'); + assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b'); + assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z'); + assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar'); + assert.strictEqual(path.posix.normalize('bar/foo../../'), 'bar/'); + assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar'); + assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz'); + assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../'); + assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..'); + assert.strictEqual(path.posix.normalize('../foo../../../bar'), '../../bar'); + assert.strictEqual(path.posix.normalize('../.../.././.../../../bar'), + '../../bar'); + assert.strictEqual(path.posix.normalize('../../../foo/../../../bar'), + '../../../../../bar'); + assert.strictEqual(path.posix.normalize('../../../foo/../../../bar/../../'), + '../../../../../../'); + assert.strictEqual( + path.posix.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '../../' + ); + assert.strictEqual( + path.posix.normalize('../.../../foobar/../../../bar/../../baz'), + '../../../../baz' + ); + assert.strictEqual(path.posix.normalize('foo/bar\\baz'), 'foo/bar\\baz'); + }); + + test('isAbsolute', () => { + assert.strictEqual(path.win32.isAbsolute('/'), true); + assert.strictEqual(path.win32.isAbsolute('//'), true); + assert.strictEqual(path.win32.isAbsolute('//server'), true); + assert.strictEqual(path.win32.isAbsolute('//server/file'), true); + assert.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true); + assert.strictEqual(path.win32.isAbsolute('\\\\server'), true); + assert.strictEqual(path.win32.isAbsolute('\\\\'), true); + assert.strictEqual(path.win32.isAbsolute('c'), false); + assert.strictEqual(path.win32.isAbsolute('c:'), false); + assert.strictEqual(path.win32.isAbsolute('c:\\'), true); + assert.strictEqual(path.win32.isAbsolute('c:/'), true); + assert.strictEqual(path.win32.isAbsolute('c://'), true); + assert.strictEqual(path.win32.isAbsolute('C:/Users/'), true); + assert.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true); + assert.strictEqual(path.win32.isAbsolute('C:cwd/another'), false); + assert.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false); + assert.strictEqual(path.win32.isAbsolute('directory/directory'), false); + assert.strictEqual(path.win32.isAbsolute('directory\\directory'), false); + + assert.strictEqual(path.posix.isAbsolute('/home/foo'), true); + assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true); + assert.strictEqual(path.posix.isAbsolute('bar/'), false); + assert.strictEqual(path.posix.isAbsolute('./baz'), false); + + // Tests from VSCode: + + // Absolute Paths + [ + 'C:/', + 'C:\\', + 'C:/foo', + 'C:\\foo', + 'z:/foo/bar.txt', + 'z:\\foo\\bar.txt', + + '\\\\localhost\\c$\\foo', + + '/', + '/foo' + ].forEach(absolutePath => { + assert.ok(path.win32.isAbsolute(absolutePath), absolutePath); + }); + + [ + '/', + '/foo', + '/foo/bar.txt' + ].forEach(absolutePath => { + assert.ok(path.posix.isAbsolute(absolutePath), absolutePath); + }); + + // Relative Paths + [ + '', + 'foo', + 'foo/bar', + './foo', + 'http://foo.com/bar' + ].forEach(nonAbsolutePath => { + assert.ok(!path.win32.isAbsolute(nonAbsolutePath), nonAbsolutePath); + }); + + [ + '', + 'foo', + 'foo/bar', + './foo', + 'http://foo.com/bar', + 'z:/foo/bar.txt', + ].forEach(nonAbsolutePath => { + assert.ok(!path.posix.isAbsolute(nonAbsolutePath), nonAbsolutePath); + }); + }); + + test('path', () => { + // path.sep tests + // windows + assert.strictEqual(path.win32.sep, '\\'); + // posix + assert.strictEqual(path.posix.sep, '/'); + + // path.delimiter tests + // windows + assert.strictEqual(path.win32.delimiter, ';'); + // posix + assert.strictEqual(path.posix.delimiter, ':'); + + // if (isWindows) { + // assert.strictEqual(path, path.win32); + // } else { + // assert.strictEqual(path, path.posix); + // } + }); + + // test('perf', () => { + // const folderNames = [ + // 'abc', + // 'Users', + // 'reallylongfoldername', + // 's', + // 'reallyreallyreallylongfoldername', + // 'home' + // ]; + + // const basePaths = [ + // 'C:', + // '', + // ]; + + // const separators = [ + // '\\', + // '/' + // ]; + + // function randomInt(ciel: number): number { + // return Math.floor(Math.random() * ciel); + // } + + // let pathsToNormalize = []; + // let pathsToJoin = []; + // let i; + // for (i = 0; i < 1000000; i++) { + // const basePath = basePaths[randomInt(basePaths.length)]; + // let lengthOfPath = randomInt(10) + 2; + + // let pathToNormalize = basePath + separators[randomInt(separators.length)]; + // while (lengthOfPath-- > 0) { + // pathToNormalize = pathToNormalize + folderNames[randomInt(folderNames.length)] + separators[randomInt(separators.length)]; + // } + + // pathsToNormalize.push(pathToNormalize); + + // let pathToJoin = ''; + // lengthOfPath = randomInt(10) + 2; + // while (lengthOfPath-- > 0) { + // pathToJoin = pathToJoin + folderNames[randomInt(folderNames.length)] + separators[randomInt(separators.length)]; + // } + + // pathsToJoin.push(pathToJoin + '.ts'); + // } + + // let newTime = 0; + + // let j; + // for(j = 0; j < pathsToJoin.length; j++) { + // const path1 = pathsToNormalize[j]; + // const path2 = pathsToNormalize[j]; + + // const newStart = performance.now(); + // path.join(path1, path2); + // newTime += performance.now() - newStart; + // } + + // assert.ok(false, `Time: ${newTime}ms.`); + // }); +}); diff --git a/src/vs/base/test/common/paths.test.ts b/src/vs/base/test/common/paths.test.ts deleted file mode 100644 index e0cf182e378..00000000000 --- a/src/vs/base/test/common/paths.test.ts +++ /dev/null @@ -1,228 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import * as paths from 'vs/base/common/paths'; -import * as platform from 'vs/base/common/platform'; - -suite('Paths', () => { - - test('dirname', () => { - assert.equal(paths.dirname('foo/bar'), 'foo'); - assert.equal(paths.dirname('foo\\bar'), 'foo'); - assert.equal(paths.dirname('/foo/bar'), '/foo'); - assert.equal(paths.dirname('\\foo\\bar'), '\\foo'); - assert.equal(paths.dirname('/foo'), '/'); - assert.equal(paths.dirname('\\foo'), '\\'); - assert.equal(paths.dirname('/'), '/'); - assert.equal(paths.dirname('\\'), '\\'); - assert.equal(paths.dirname('foo'), '.'); - assert.equal(paths.dirname('/folder/'), '/'); - if (platform.isWindows) { - assert.equal(paths.dirname('c:\\some\\file.txt'), 'c:\\some'); - assert.equal(paths.dirname('c:\\some'), 'c:\\'); - } - }); - - test('normalize', () => { - assert.equal(paths.normalize(''), '.'); - assert.equal(paths.normalize('.'), '.'); - assert.equal(paths.normalize('.'), '.'); - assert.equal(paths.normalize('../../far'), '../../far'); - assert.equal(paths.normalize('../bar'), '../bar'); - assert.equal(paths.normalize('../far'), '../far'); - assert.equal(paths.normalize('./'), './'); - assert.equal(paths.normalize('./././'), './'); - assert.equal(paths.normalize('./ff/./'), 'ff/'); - assert.equal(paths.normalize('./foo'), 'foo'); - assert.equal(paths.normalize('/'), '/'); - assert.equal(paths.normalize('/..'), '/'); - assert.equal(paths.normalize('///'), '/'); - assert.equal(paths.normalize('//foo'), '/foo'); - assert.equal(paths.normalize('//foo//'), '/foo/'); - assert.equal(paths.normalize('/foo'), '/foo'); - assert.equal(paths.normalize('/foo/bar.test'), '/foo/bar.test'); - assert.equal(paths.normalize('\\\\\\'), '/'); - assert.equal(paths.normalize('c:/../ff'), 'c:/ff'); - assert.equal(paths.normalize('c:\\./'), 'c:/'); - assert.equal(paths.normalize('foo/'), 'foo/'); - assert.equal(paths.normalize('foo/../../bar'), '../bar'); - assert.equal(paths.normalize('foo/./'), 'foo/'); - assert.equal(paths.normalize('foo/./bar'), 'foo/bar'); - assert.equal(paths.normalize('foo//'), 'foo/'); - assert.equal(paths.normalize('foo//'), 'foo/'); - assert.equal(paths.normalize('foo//bar'), 'foo/bar'); - assert.equal(paths.normalize('foo//bar/far'), 'foo/bar/far'); - assert.equal(paths.normalize('foo/bar/../../far'), 'far'); - assert.equal(paths.normalize('foo/bar/../far'), 'foo/far'); - assert.equal(paths.normalize('foo/far/../../bar'), 'bar'); - assert.equal(paths.normalize('foo/far/../../bar'), 'bar'); - assert.equal(paths.normalize('foo/xxx/..'), 'foo'); - assert.equal(paths.normalize('foo/xxx/../bar'), 'foo/bar'); - assert.equal(paths.normalize('foo/xxx/./..'), 'foo'); - assert.equal(paths.normalize('foo/xxx/./../bar'), 'foo/bar'); - assert.equal(paths.normalize('foo/xxx/./bar'), 'foo/xxx/bar'); - assert.equal(paths.normalize('foo\\bar'), 'foo/bar'); - assert.equal(paths.normalize(null), null); - assert.equal(paths.normalize(undefined), undefined); - - // https://github.com/Microsoft/vscode/issues/7234 - assert.equal(paths.join('/home/aeschli/workspaces/vscode/extensions/css', './syntaxes/css.plist'), '/home/aeschli/workspaces/vscode/extensions/css/syntaxes/css.plist'); - }); - - test('getRootLength', () => { - - assert.equal(paths.getRoot('/user/far'), '/'); - assert.equal(paths.getRoot('\\\\server\\share\\some\\path'), '//server/share/'); - assert.equal(paths.getRoot('//server/share/some/path'), '//server/share/'); - assert.equal(paths.getRoot('//server/share'), '/'); - assert.equal(paths.getRoot('//server'), '/'); - assert.equal(paths.getRoot('//server//'), '/'); - assert.equal(paths.getRoot('c:/user/far'), 'c:/'); - assert.equal(paths.getRoot('c:user/far'), 'c:'); - assert.equal(paths.getRoot('http://www'), ''); - assert.equal(paths.getRoot('http://www/'), 'http://www/'); - assert.equal(paths.getRoot('file:///foo'), 'file:///'); - assert.equal(paths.getRoot('file://foo'), ''); - - }); - - test('basename', () => { - assert.equal(paths.basename('foo/bar'), 'bar'); - assert.equal(paths.basename('foo\\bar'), 'bar'); - assert.equal(paths.basename('/foo/bar'), 'bar'); - assert.equal(paths.basename('\\foo\\bar'), 'bar'); - assert.equal(paths.basename('./bar'), 'bar'); - assert.equal(paths.basename('.\\bar'), 'bar'); - assert.equal(paths.basename('/bar'), 'bar'); - assert.equal(paths.basename('\\bar'), 'bar'); - assert.equal(paths.basename('bar/'), 'bar'); - assert.equal(paths.basename('bar\\'), 'bar'); - assert.equal(paths.basename('bar'), 'bar'); - assert.equal(paths.basename('////////'), ''); - assert.equal(paths.basename('\\\\\\\\'), ''); - }); - - test('join', () => { - assert.equal(paths.join('.', 'bar'), 'bar'); - assert.equal(paths.join('../../foo/bar', '../../foo'), '../../foo'); - assert.equal(paths.join('../../foo/bar', '../bar/foo'), '../../foo/bar/foo'); - assert.equal(paths.join('../foo/bar', '../bar/foo'), '../foo/bar/foo'); - assert.equal(paths.join('/', 'bar'), '/bar'); - assert.equal(paths.join('//server/far/boo', '../file.txt'), '//server/far/file.txt'); - assert.equal(paths.join('/foo/', '/bar'), '/foo/bar'); - assert.equal(paths.join('\\\\server\\far\\boo', '../file.txt'), '//server/far/file.txt'); - assert.equal(paths.join('\\\\server\\far\\boo', './file.txt'), '//server/far/boo/file.txt'); - assert.equal(paths.join('\\\\server\\far\\boo', '.\\file.txt'), '//server/far/boo/file.txt'); - assert.equal(paths.join('\\\\server\\far\\boo', 'file.txt'), '//server/far/boo/file.txt'); - assert.equal(paths.join('file:///c/users/test', 'test'), 'file:///c/users/test/test'); - assert.equal(paths.join('file://localhost/c$/GitDevelopment/express', './settings'), 'file://localhost/c$/GitDevelopment/express/settings'); // unc - assert.equal(paths.join('file://localhost/c$/GitDevelopment/express', '.settings'), 'file://localhost/c$/GitDevelopment/express/.settings'); // unc - assert.equal(paths.join('foo', '/bar'), 'foo/bar'); - assert.equal(paths.join('foo', 'bar'), 'foo/bar'); - assert.equal(paths.join('foo', 'bar/'), 'foo/bar/'); - assert.equal(paths.join('foo/', '/bar'), 'foo/bar'); - assert.equal(paths.join('foo/', '/bar/'), 'foo/bar/'); - assert.equal(paths.join('foo/', 'bar'), 'foo/bar'); - assert.equal(paths.join('foo/bar', '../bar/foo'), 'foo/bar/foo'); - assert.equal(paths.join('foo/bar', './bar/foo'), 'foo/bar/bar/foo'); - assert.equal(paths.join('http://localhost/test', '../next'), 'http://localhost/next'); - assert.equal(paths.join('http://localhost/test', 'test'), 'http://localhost/test/test'); - }); - - test('extname', () => { - assert.equal(paths.extname('far.boo'), '.boo'); - assert.equal(paths.extname('far.b'), '.b'); - assert.equal(paths.extname('far.'), '.'); - assert.equal(paths.extname('far.boo/boo.far'), '.far'); - assert.equal(paths.extname('far.boo/boo'), ''); - }); - - test('isUNC', () => { - if (platform.isWindows) { - assert.ok(!paths.isUNC('foo')); - assert.ok(!paths.isUNC('/foo')); - assert.ok(!paths.isUNC('\\foo')); - assert.ok(!paths.isUNC('\\\\foo')); - assert.ok(paths.isUNC('\\\\a\\b')); - assert.ok(!paths.isUNC('//a/b')); - assert.ok(paths.isUNC('\\\\server\\share')); - assert.ok(paths.isUNC('\\\\server\\share\\')); - assert.ok(paths.isUNC('\\\\server\\share\\path')); - } - }); - - test('isValidBasename', () => { - assert.ok(!paths.isValidBasename(null)); - assert.ok(!paths.isValidBasename('')); - assert.ok(paths.isValidBasename('test.txt')); - assert.ok(!paths.isValidBasename('/test.txt')); - assert.ok(!paths.isValidBasename('\\test.txt')); - - if (platform.isWindows) { - assert.ok(!paths.isValidBasename('aux')); - assert.ok(!paths.isValidBasename('Aux')); - assert.ok(!paths.isValidBasename('LPT0')); - assert.ok(!paths.isValidBasename('test.txt.')); - assert.ok(!paths.isValidBasename('test.txt..')); - assert.ok(!paths.isValidBasename('test.txt ')); - assert.ok(!paths.isValidBasename('test.txt\t')); - assert.ok(!paths.isValidBasename('tes:t.txt')); - assert.ok(!paths.isValidBasename('tes"t.txt')); - } - }); - - test('isAbsolute_win', () => { - // Absolute paths - [ - 'C:/', - 'C:\\', - 'C:/foo', - 'C:\\foo', - 'z:/foo/bar.txt', - 'z:\\foo\\bar.txt', - - '\\\\localhost\\c$\\foo', - - '/', - '/foo' - ].forEach(absolutePath => { - assert.ok(paths.isAbsolute_win32(absolutePath), absolutePath); - }); - - // Not absolute paths - [ - '', - 'foo', - 'foo/bar', - './foo', - 'http://foo.com/bar' - ].forEach(nonAbsolutePath => { - assert.ok(!paths.isAbsolute_win32(nonAbsolutePath), nonAbsolutePath); - }); - }); - - test('isAbsolute_posix', () => { - // Absolute paths - [ - '/', - '/foo', - '/foo/bar.txt' - ].forEach(absolutePath => { - assert.ok(paths.isAbsolute_posix(absolutePath), absolutePath); - }); - - // Not absolute paths - [ - '', - 'foo', - 'foo/bar', - './foo', - 'http://foo.com/bar', - 'z:/foo/bar.txt', - ].forEach(nonAbsolutePath => { - assert.ok(!paths.isAbsolute_posix(nonAbsolutePath), nonAbsolutePath); - }); - }); -}); diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts new file mode 100644 index 00000000000..cd54b6fd724 --- /dev/null +++ b/src/vs/base/test/common/processes.test.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as processes from 'vs/base/common/processes'; + +suite('Processes', () => { + test('sanitizeProcessEnvironment', () => { + let env = { + FOO: 'bar', + ELECTRON_ENABLE_STACK_DUMPING: 'x', + ELECTRON_ENABLE_LOGGING: 'x', + ELECTRON_NO_ASAR: 'x', + ELECTRON_NO_ATTACH_CONSOLE: 'x', + ELECTRON_RUN_AS_NODE: 'x', + GOOGLE_API_KEY: 'x', + VSCODE_CLI: 'x', + VSCODE_DEV: 'x', + VSCODE_IPC_HOOK: 'x', + VSCODE_LOGS: 'x', + VSCODE_NLS_CONFIG: 'x', + VSCODE_PORTABLE: 'x', + VSCODE_PID: 'x', + VSCODE_NODE_CACHED_DATA_DIR: 'x', + VSCODE_NEW_VAR: 'x' + }; + processes.sanitizeProcessEnvironment(env); + assert.equal(env['FOO'], 'bar'); + assert.equal(Object.keys(env).length, 1); + }); +}); diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 0716d2748c5..b9d1a99d539 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -3,9 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, isMalformedFileUri } from 'vs/base/common/resources'; -import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; +import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath, addTrailingPathSeparator } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; +import { toSlashes } from 'vs/base/common/extpath'; +import { startsWith } from 'vs/base/common/strings'; +import { isAbsolute } from 'vs/base/common/path'; + suite('Resources', () => { @@ -47,6 +51,7 @@ suite('Resources', () => { 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/'); + assert.equal(dirname(URI.file('c:\\')).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'); @@ -56,6 +61,8 @@ suite('Resources', () => { 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/'); + assert.equal(dirname(URI.parse('foo://a/')).toString(), 'foo://a/'); + assert.equal(dirname(URI.parse('foo://a')).toString(), 'foo://a'); // does not explode (https://github.com/Microsoft/vscode/issues/41987) dirname(URI.from({ scheme: 'file', authority: '/users/someone/portal.h' })); @@ -135,6 +142,7 @@ suite('Resources', () => { 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.file('/f')).toString(), 'file:///f'); } 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'); @@ -145,6 +153,8 @@ suite('Resources', () => { 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'); + assert.equal(normalizePath(URI.parse('foo://a')).toString(), 'foo://a'); + assert.equal(normalizePath(URI.parse('foo://a/')).toString(), 'foo://a/'); }); test('isAbsolute', () => { @@ -160,6 +170,168 @@ suite('Resources', () => { assert.equal(isAbsolutePath(URI.parse('foo://a/foo/.')), true); }); + function assertTrailingSeparator(u1: URI, expected: boolean) { + assert.equal(hasTrailingPathSeparator(u1), expected, u1.toString()); + } + + function assertRemoveTrailingSeparator(u1: URI, expected: URI) { + assertEqualURI(removeTrailingPathSeparator(u1), expected, u1.toString()); + } + + function assertAddTrailingSeparator(u1: URI, expected: URI) { + assertEqualURI(addTrailingPathSeparator(u1), expected, u1.toString()); + } + + test('trailingPathSeparator', () => { + assertTrailingSeparator(URI.parse('foo://a/foo'), false); + assertTrailingSeparator(URI.parse('foo://a/foo/'), true); + assertTrailingSeparator(URI.parse('foo://a/'), false); + assertTrailingSeparator(URI.parse('foo://a'), false); + + assertRemoveTrailingSeparator(URI.parse('foo://a/foo'), URI.parse('foo://a/foo')); + assertRemoveTrailingSeparator(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo')); + assertRemoveTrailingSeparator(URI.parse('foo://a/'), URI.parse('foo://a/')); + assertRemoveTrailingSeparator(URI.parse('foo://a'), URI.parse('foo://a')); + + assertAddTrailingSeparator(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/')); + assertAddTrailingSeparator(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo/')); + assertAddTrailingSeparator(URI.parse('foo://a/'), URI.parse('foo://a/')); + assertAddTrailingSeparator(URI.parse('foo://a'), URI.parse('foo://a/')); + + if (isWindows) { + assertTrailingSeparator(URI.file('c:\\a\\foo'), false); + assertTrailingSeparator(URI.file('c:\\a\\foo\\'), true); + assertTrailingSeparator(URI.file('c:\\'), false); + assertTrailingSeparator(URI.file('\\\\server\\share\\some\\'), true); + assertTrailingSeparator(URI.file('\\\\server\\share\\'), false); + + assertRemoveTrailingSeparator(URI.file('c:\\a\\foo'), URI.file('c:\\a\\foo')); + assertRemoveTrailingSeparator(URI.file('c:\\a\\foo\\'), URI.file('c:\\a\\foo')); + assertRemoveTrailingSeparator(URI.file('c:\\'), URI.file('c:\\')); + assertRemoveTrailingSeparator(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some')); + assertRemoveTrailingSeparator(URI.file('\\\\server\\share\\'), URI.file('\\\\server\\share\\')); + + assertAddTrailingSeparator(URI.file('c:\\a\\foo'), URI.file('c:\\a\\foo\\')); + assertAddTrailingSeparator(URI.file('c:\\a\\foo\\'), URI.file('c:\\a\\foo\\')); + assertAddTrailingSeparator(URI.file('c:\\'), URI.file('c:\\')); + assertAddTrailingSeparator(URI.file('\\\\server\\share\\some'), URI.file('\\\\server\\share\\some\\')); + assertAddTrailingSeparator(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some\\')); + } else { + assertTrailingSeparator(URI.file('/foo/bar'), false); + assertTrailingSeparator(URI.file('/foo/bar/'), true); + assertTrailingSeparator(URI.file('/'), false); + + assertRemoveTrailingSeparator(URI.file('/foo/bar'), URI.file('/foo/bar')); + assertRemoveTrailingSeparator(URI.file('/foo/bar/'), URI.file('/foo/bar')); + assertRemoveTrailingSeparator(URI.file('/'), URI.file('/')); + + assertAddTrailingSeparator(URI.file('/foo/bar'), URI.file('/foo/bar/')); + assertAddTrailingSeparator(URI.file('/foo/bar/'), URI.file('/foo/bar/')); + assertAddTrailingSeparator(URI.file('/'), URI.file('/')); + } + }); + + function assertEqualURI(actual: URI, expected: URI, message?: string) { + if (!isEqual(expected, actual)) { + assert.equal(actual.toString(), expected.toString(), message); + } + } + + function assertRelativePath(u1: URI, u2: URI, expectedPath: string | undefined, ignoreJoin?: boolean) { + assert.equal(relativePath(u1, u2), expectedPath, `from ${u1.toString()} to ${u2.toString()}`); + if (expectedPath !== undefined && !ignoreJoin) { + assertEqualURI(removeTrailingPathSeparator(joinPath(u1, expectedPath)), removeTrailingPathSeparator(u2), 'joinPath on relativePath should be equal'); + } + } + + test('relativePath', () => { + assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/bar'), 'bar'); + assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/bar/'), 'bar'); + assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/bar/goo'), 'bar/goo'); + assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a/foo/bar/goo'), 'foo/bar/goo'); + assertRelativePath(URI.parse('foo://a/foo/xoo'), URI.parse('foo://a/foo/bar'), '../bar'); + assertRelativePath(URI.parse('foo://a/foo/xoo/yoo'), URI.parse('foo://a'), '../../..'); + assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/'), ''); + assertRelativePath(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo'), ''); + assertRelativePath(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo/'), ''); + assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo'), ''); + assertRelativePath(URI.parse('foo://a'), URI.parse('foo://a'), ''); + assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a/'), ''); + assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a'), ''); + assertRelativePath(URI.parse('foo://a/foo?q'), URI.parse('foo://a/foo/bar#h'), 'bar'); + assertRelativePath(URI.parse('foo://'), URI.parse('foo://a/b'), undefined); + assertRelativePath(URI.parse('foo://a2/b'), URI.parse('foo://a/b'), undefined); + assertRelativePath(URI.parse('goo://a/b'), URI.parse('foo://a/b'), undefined); + + if (isWindows) { + assertRelativePath(URI.file('c:\\foo\\bar'), URI.file('c:\\foo\\bar'), ''); + assertRelativePath(URI.file('c:\\foo\\bar\\huu'), URI.file('c:\\foo\\bar'), '..'); + assertRelativePath(URI.file('c:\\foo\\bar\\a1\\a2'), URI.file('c:\\foo\\bar'), '../..'); + assertRelativePath(URI.file('c:\\foo\\bar\\'), URI.file('c:\\foo\\bar\\a1\\a2'), 'a1/a2'); + assertRelativePath(URI.file('c:\\foo\\bar\\'), URI.file('c:\\foo\\bar\\a1\\a2\\'), 'a1/a2'); + assertRelativePath(URI.file('c:\\'), URI.file('c:\\foo\\bar'), 'foo/bar'); + assertRelativePath(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some\\path'), 'path'); + assertRelativePath(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share2\\some\\path'), '../../share2/some/path', true); // ignore joinPath assert: path.join is not root aware + } else { + assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/bar'), 'bar'); + assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/bar/'), 'bar'); + assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/bar/goo'), 'bar/goo'); + assertRelativePath(URI.file('/a/'), URI.file('/a/foo/bar/goo'), 'foo/bar/goo'); + assertRelativePath(URI.file('/'), URI.file('/a/foo/bar/goo'), 'a/foo/bar/goo'); + assertRelativePath(URI.file('/a/foo/xoo'), URI.file('/a/foo/bar'), '../bar'); + assertRelativePath(URI.file('/a/foo/xoo/yoo'), URI.file('/a'), '../../..'); + assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/'), ''); + assertRelativePath(URI.file('/a/foo'), URI.file('/b/foo/'), '../../b/foo'); + } + }); + + function assertResolve(u1: URI, path: string, expected: URI) { + const actual = resolvePath(u1, path); + assertEqualURI(actual, expected, `from ${u1.toString()} and ${path}`); + + if (!isAbsolute(path)) { + let expectedPath = isWindows ? toSlashes(path) : path; + expectedPath = startsWith(expectedPath, './') ? expectedPath.substr(2) : expectedPath; + assert.equal(relativePath(u1, actual), expectedPath, `relativePath (${u1.toString()}) on actual (${actual.toString()}) should be to path (${expectedPath})`); + } + } + + test('resolve', () => { + if (isWindows) { + assertResolve(URI.file('c:\\foo\\bar'), 'file.js', URI.file('c:\\foo\\bar\\file.js')); + assertResolve(URI.file('c:\\foo\\bar'), 't\\file.js', URI.file('c:\\foo\\bar\\t\\file.js')); + assertResolve(URI.file('c:\\foo\\bar'), '.\\t\\file.js', URI.file('c:\\foo\\bar\\t\\file.js')); + assertResolve(URI.file('c:\\foo\\bar'), 'a1/file.js', URI.file('c:\\foo\\bar\\a1\\file.js')); + assertResolve(URI.file('c:\\foo\\bar'), './a1/file.js', URI.file('c:\\foo\\bar\\a1\\file.js')); + assertResolve(URI.file('c:\\foo\\bar'), '\\b1\\file.js', URI.file('c:\\b1\\file.js')); + assertResolve(URI.file('c:\\foo\\bar'), '/b1/file.js', URI.file('c:\\b1\\file.js')); + assertResolve(URI.file('c:\\foo\\bar\\'), 'file.js', URI.file('c:\\foo\\bar\\file.js')); + + assertResolve(URI.file('c:\\'), 'file.js', URI.file('c:\\file.js')); + assertResolve(URI.file('c:\\'), '\\b1\\file.js', URI.file('c:\\b1\\file.js')); + assertResolve(URI.file('c:\\'), '/b1/file.js', URI.file('c:\\b1\\file.js')); + assertResolve(URI.file('c:\\'), 'd:\\foo\\bar.txt', URI.file('d:\\foo\\bar.txt')); + + assertResolve(URI.file('\\\\server\\share\\some\\'), 'b1\\file.js', URI.file('\\\\server\\share\\some\\b1\\file.js')); + assertResolve(URI.file('\\\\server\\share\\some\\'), '\\file.js', URI.file('\\\\server\\share\\file.js')); + + assertResolve(URI.file('c:\\'), '\\\\server\\share\\some\\', URI.file('\\\\server\\share\\some')); + assertResolve(URI.file('\\\\server\\share\\some\\'), 'c:\\', URI.file('c:\\')); + } else { + assertResolve(URI.file('/foo/bar'), 'file.js', URI.file('/foo/bar/file.js')); + assertResolve(URI.file('/foo/bar'), './file.js', URI.file('/foo/bar/file.js')); + assertResolve(URI.file('/foo/bar'), '/file.js', URI.file('/file.js')); + assertResolve(URI.file('/foo/bar/'), 'file.js', URI.file('/foo/bar/file.js')); + assertResolve(URI.file('/'), 'file.js', URI.file('/file.js')); + assertResolve(URI.file(''), './file.js', URI.file('/file.js')); + assertResolve(URI.file(''), '/file.js', URI.file('/file.js')); + } + + assertResolve(URI.parse('foo://server/foo/bar'), 'file.js', URI.parse('foo://server/foo/bar/file.js')); + assertResolve(URI.parse('foo://server/foo/bar'), './file.js', URI.parse('foo://server/foo/bar/file.js')); + assertResolve(URI.parse('foo://server/foo/bar'), './file.js', URI.parse('foo://server/foo/bar/file.js')); + }); + 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'); @@ -178,6 +350,8 @@ suite('Resources', () => { assert.equal(isEqual(fileURI3, fileURI4, false), false); assert.equal(isEqual(fileURI, fileURI3, true), false); + + assert.equal(isEqual(URI.parse('foo://server'), URI.parse('foo://server/')), true); }); test('isEqualOrParent', () => { @@ -204,27 +378,4 @@ suite('Resources', () => { assert.equal(isEqualOrParent(fileURI3, fileURI, true), false, '15'); assert.equal(isEqualOrParent(fileURI5, fileURI5, true), true, '16'); }); - - function assertMalformedFileUri(path: string, expected: string) { - const old = setUriThrowOnMissingScheme(false); - const newURI = isMalformedFileUri(URI.parse(path)); - assert.equal(newURI && newURI.toString(), expected); - setUriThrowOnMissingScheme(old); - } - - 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); - }); }); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 2e8ee07797b..015075a90a9 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -320,7 +320,7 @@ suite('Strings', () => { }); test('fuzzyContains', () => { - assert.ok(!strings.fuzzyContains(void 0, null)); + assert.ok(!strings.fuzzyContains((undefined)!, null!)); assert.ok(strings.fuzzyContains('hello world', 'h')); assert.ok(!strings.fuzzyContains('hello world', 'q')); assert.ok(strings.fuzzyContains('hello world', 'hw')); @@ -403,4 +403,59 @@ suite('Strings', () => { assert.equal(strings.getNLines('foo\nbar\nsomething', 2), 'foo\nbar'); assert.equal(strings.getNLines('foo', 0), ''); }); + + test('removeAccents', function () { + assert.equal(strings.removeAccents('joào'), 'joao'); + assert.equal(strings.removeAccents('joáo'), 'joao'); + assert.equal(strings.removeAccents('joâo'), 'joao'); + assert.equal(strings.removeAccents('joäo'), 'joao'); + // assert.equal(strings.removeAccents('joæo'), 'joao'); // not an accent + assert.equal(strings.removeAccents('joão'), 'joao'); + assert.equal(strings.removeAccents('joåo'), 'joao'); + assert.equal(strings.removeAccents('joåo'), 'joao'); + assert.equal(strings.removeAccents('joāo'), 'joao'); + + assert.equal(strings.removeAccents('fôo'), 'foo'); + assert.equal(strings.removeAccents('föo'), 'foo'); + assert.equal(strings.removeAccents('fòo'), 'foo'); + assert.equal(strings.removeAccents('fóo'), 'foo'); + // assert.equal(strings.removeAccents('fœo'), 'foo'); + // assert.equal(strings.removeAccents('føo'), 'foo'); + assert.equal(strings.removeAccents('fōo'), 'foo'); + assert.equal(strings.removeAccents('fõo'), 'foo'); + + assert.equal(strings.removeAccents('andrè'), 'andre'); + assert.equal(strings.removeAccents('andré'), 'andre'); + assert.equal(strings.removeAccents('andrê'), 'andre'); + assert.equal(strings.removeAccents('andrë'), 'andre'); + assert.equal(strings.removeAccents('andrē'), 'andre'); + assert.equal(strings.removeAccents('andrė'), 'andre'); + assert.equal(strings.removeAccents('andrę'), 'andre'); + + assert.equal(strings.removeAccents('hvîc'), 'hvic'); + assert.equal(strings.removeAccents('hvïc'), 'hvic'); + assert.equal(strings.removeAccents('hvíc'), 'hvic'); + assert.equal(strings.removeAccents('hvīc'), 'hvic'); + assert.equal(strings.removeAccents('hvįc'), 'hvic'); + assert.equal(strings.removeAccents('hvìc'), 'hvic'); + + assert.equal(strings.removeAccents('ûdo'), 'udo'); + assert.equal(strings.removeAccents('üdo'), 'udo'); + assert.equal(strings.removeAccents('ùdo'), 'udo'); + assert.equal(strings.removeAccents('údo'), 'udo'); + assert.equal(strings.removeAccents('ūdo'), 'udo'); + + assert.equal(strings.removeAccents('heÿ'), 'hey'); + + // assert.equal(strings.removeAccents('gruß'), 'grus'); + assert.equal(strings.removeAccents('gruś'), 'grus'); + assert.equal(strings.removeAccents('gruš'), 'grus'); + + assert.equal(strings.removeAccents('çool'), 'cool'); + assert.equal(strings.removeAccents('ćool'), 'cool'); + assert.equal(strings.removeAccents('čool'), 'cool'); + + assert.equal(strings.removeAccents('ñice'), 'nice'); + assert.equal(strings.removeAccents('ńice'), 'nice'); + }); }); diff --git a/src/vs/base/test/common/types.test.ts b/src/vs/base/test/common/types.test.ts index 27927725a27..43241b9a9c2 100644 --- a/src/vs/base/test/common/types.test.ts +++ b/src/vs/base/test/common/types.test.ts @@ -176,42 +176,17 @@ suite('Types', () => { types.validateConstraints([undefined], [types.isUndefined]); types.validateConstraints([1], [types.isNumber]); - function foo() { } - types.validateConstraints([new foo()], [foo]); + class Foo { } + types.validateConstraints([new Foo()], [Foo]); - function isFoo(f) { } - assert.throws(() => types.validateConstraints([new foo()], [isFoo])); + function isFoo(f: any) { } + assert.throws(() => types.validateConstraints([new Foo()], [isFoo])); - function isFoo2(f) { return true; } - types.validateConstraints([new foo()], [isFoo2]); + function isFoo2(f: any) { return true; } + types.validateConstraints([new Foo()], [isFoo2]); assert.throws(() => types.validateConstraints([1, true], [types.isNumber, types.isString])); assert.throws(() => types.validateConstraints(['2'], [types.isNumber])); assert.throws(() => types.validateConstraints([1, 'test', true], [Number, String, Number])); }); - - test('create', () => { - let zeroConstructor = function () { /**/ }; - - assert(types.create(zeroConstructor) instanceof zeroConstructor); - assert(types.isObject(types.create(zeroConstructor))); - - let manyArgConstructor = function (this: any, foo, bar) { - this.foo = foo; - this.bar = bar; - }; - - let foo = {}; - let bar = 'foo'; - - assert(types.create(manyArgConstructor) instanceof manyArgConstructor); - assert(types.isObject(types.create(manyArgConstructor))); - - assert(types.create(manyArgConstructor, foo, bar) instanceof manyArgConstructor); - assert(types.isObject(types.create(manyArgConstructor, foo, bar))); - - let obj = types.create(manyArgConstructor, foo, bar); - assert.strictEqual(obj.foo, foo); - assert.strictEqual(obj.bar, bar); - }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 77351bee75e..85f2ad691ac 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { URI } from 'vs/base/common/uri'; -import { normalize } from 'vs/base/common/paths'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; @@ -73,15 +72,15 @@ suite('URI', () => { 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({ 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'); + const value = URI.parse('file://shares/pröjects/c%23/#l12'); assert.equal(value.authority, 'shares'); assert.equal(value.path, '/pröjects/c#/'); assert.equal(value.fragment, 'l12'); assert.equal(value.toString(), 'file://shares/pr%C3%B6jects/c%23/#l12'); assert.equal(value.toString(true), 'file://shares/pröjects/c%23/#l12'); - var uri2 = URI.parse(value.toString(true)); - var uri3 = URI.parse(value.toString()); + const uri2 = URI.parse(value.toString(true)); + const uri3 = URI.parse(value.toString()); assert.equal(uri2.authority, uri3.authority); assert.equal(uri2.path, uri3.path); assert.equal(uri2.query, uri3.query); @@ -91,9 +90,9 @@ suite('URI', () => { test('with, identity', () => { let uri = URI.parse('foo:bar/path'); - let uri2 = uri.with(null); + let uri2 = uri.with(null!); assert.ok(uri === uri2); - uri2 = uri.with(undefined); + uri2 = uri.with(undefined!); assert.ok(uri === uri2); uri2 = uri.with({}); assert.ok(uri === uri2); @@ -130,7 +129,7 @@ suite('URI', () => { }); test('parse', () => { - var value = URI.parse('http:/api/files/test.me?t=1234'); + let value = URI.parse('http:/api/files/test.me?t=1234'); assert.equal(value.scheme, 'http'); assert.equal(value.authority, ''); assert.equal(value.path, '/api/files/test.me'); @@ -141,7 +140,6 @@ suite('URI', () => { assert.equal(value.scheme, 'http'); assert.equal(value.authority, 'api'); assert.equal(value.path, '/files/test.me'); - assert.equal(value.fsPath, normalize('/files/test.me', true)); assert.equal(value.query, 't=1234'); assert.equal(value.fragment, ''); @@ -151,7 +149,7 @@ suite('URI', () => { assert.equal(value.path, '/c:/test/me'); assert.equal(value.fragment, ''); assert.equal(value.query, ''); - assert.equal(value.fsPath, normalize('c:/test/me', true)); + assert.equal(value.fsPath, isWindows ? 'c:\\test\\me' : 'c:/test/me'); value = URI.parse('file://shares/files/c%23/p.cs'); assert.equal(value.scheme, 'file'); @@ -159,7 +157,7 @@ suite('URI', () => { assert.equal(value.path, '/files/c#/p.cs'); assert.equal(value.fragment, ''); assert.equal(value.query, ''); - assert.equal(value.fsPath, normalize('//shares/files/c#/p.cs', true)); + assert.equal(value.fsPath, isWindows ? '\\\\shares\\files\\c#\\p.cs' : '//shares/files/c#/p.cs'); value = URI.parse('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins/c%23/plugin.json'); assert.equal(value.scheme, 'file'); @@ -238,7 +236,7 @@ suite('URI', () => { test('URI#file, win-speciale', () => { if (isWindows) { - var value = URI.file('c:\\test\\drive'); + let value = URI.file('c:\\test\\drive'); assert.equal(value.path, '/c:/test/drive'); assert.equal(value.toString(), 'file:///c%3A/test/drive'); @@ -298,7 +296,7 @@ suite('URI', () => { test('URI#file, always slash', () => { - var value = URI.file('a.file'); + let value = URI.file('a.file'); assert.equal(value.scheme, 'file'); assert.equal(value.authority, ''); assert.equal(value.path, '/a.file'); @@ -312,12 +310,12 @@ suite('URI', () => { }); test('URI.toString, only scheme and query', () => { - var value = URI.parse('stuff:?qüery'); + const value = URI.parse('stuff:?qüery'); assert.equal(value.toString(), 'stuff:?q%C3%BCery'); }); test('URI#toString, upper-case percent espaces', () => { - var value = URI.parse('file://sh%c3%a4res/path'); + const value = URI.parse('file://sh%c3%a4res/path'); assert.equal(value.toString(), 'file://sh%C3%A4res/path'); }); @@ -328,12 +326,12 @@ suite('URI', () => { test('URI#toString, escape all the bits', () => { - var value = URI.file('/Users/jrieken/Code/_samples/18500/Mödel + Other Thîngß/model.js'); + const value = URI.file('/Users/jrieken/Code/_samples/18500/Mödel + Other Thîngß/model.js'); assert.equal(value.toString(), 'file:///Users/jrieken/Code/_samples/18500/M%C3%B6del%20%2B%20Other%20Th%C3%AEng%C3%9F/model.js'); }); test('URI#toString, don\'t encode port', () => { - var value = URI.parse('http://localhost:8080/far'); + let value = URI.parse('http://localhost:8080/far'); assert.equal(value.toString(), 'http://localhost:8080/far'); value = URI.from({ scheme: 'http', authority: 'löcalhost:8080', path: '/far', query: undefined, fragment: undefined }); @@ -341,7 +339,7 @@ suite('URI', () => { }); test('URI#toString, user information in authority', () => { - var value = URI.parse('http://foo:bar@localhost/far'); + let value = URI.parse('http://foo:bar@localhost/far'); assert.equal(value.toString(), 'http://foo:bar@localhost/far'); value = URI.parse('http://foo@localhost/far'); @@ -359,19 +357,18 @@ suite('URI', () => { test('correctFileUriToFilePath2', () => { - var test = (input: string, expected: string) => { - expected = normalize(expected, true); - var value = URI.parse(input); + const test = (input: string, expected: string) => { + const value = URI.parse(input); assert.equal(value.fsPath, expected, 'Result for ' + input); - var value2 = URI.file(value.fsPath); + const value2 = URI.file(value.fsPath); assert.equal(value2.fsPath, expected, 'Result for ' + input); assert.equal(value.toString(), value2.toString()); }; - test('file:///c:/alex.txt', 'c:\\alex.txt'); - test('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', 'c:\\Source\\Zürich or Zurich (ˈzjʊərɪk,\\Code\\resources\\app\\plugins'); - test('file://monacotools/folder/isi.txt', '\\\\monacotools\\folder\\isi.txt'); - test('file://monacotools1/certificates/SSL/', '\\\\monacotools1\\certificates\\SSL\\'); + test('file:///c:/alex.txt', isWindows ? 'c:\\alex.txt' : 'c:/alex.txt'); + test('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', isWindows ? 'c:\\Source\\Zürich or Zurich (ˈzjʊərɪk,\\Code\\resources\\app\\plugins' : 'c:/Source/Zürich or Zurich (ˈzjʊərɪk,/Code/resources/app/plugins'); + test('file://monacotools/folder/isi.txt', isWindows ? '\\\\monacotools\\folder\\isi.txt' : '//monacotools/folder/isi.txt'); + test('file://monacotools1/certificates/SSL/', isWindows ? '\\\\monacotools1\\certificates\\SSL\\' : '//monacotools1/certificates/SSL/'); }); test('URI - http, query & toString', function () { @@ -431,7 +428,7 @@ suite('URI', () => { test('URI - (de)serialize', function () { - var values = [ + const values = [ URI.parse('http://localhost:8080/far'), URI.file('c:\\test with %25\\c#code'), URI.file('\\\\shäres\\path\\c#\\plugin.json'), @@ -444,7 +441,7 @@ suite('URI', () => { // let c = 100000; // while (c-- > 0) { for (let value of values) { - let data = value.toJSON(); + let data = value.toJSON() as UriComponents; let clone = URI.revive(data); assert.equal(clone.scheme, value.scheme); diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index dae0e054b00..0fb02a0193d 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as paths from 'vs/base/common/paths'; +import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { canceled } from 'vs/base/common/errors'; +import { isWindows } from 'vs/base/common/platform'; -export type ValueCallback = (value: T | Thenable) => void; +export type ValueCallback = (value: T | Promise) => void; export class DeferredPromise { @@ -49,7 +50,11 @@ export class DeferredPromise { } export function toResource(this: any, path: string) { - return URI.file(paths.join('C:\\', Buffer.from(this.test.fullTitle()).toString('base64'), path)); + if (isWindows) { + return URI.file(join('C:\\', Buffer.from(this.test.fullTitle()).toString('base64'), path)); + } + + return URI.file(join('/', Buffer.from(this.test.fullTitle()).toString('base64'), path)); } export function suiteRepeat(n: number, description: string, callback: (this: any) => void): void { diff --git a/src/vs/base/test/common/uuid.test.ts b/src/vs/base/test/common/uuid.test.ts index 2ed2042e054..ef2cd78be28 100644 --- a/src/vs/base/test/common/uuid.test.ts +++ b/src/vs/base/test/common/uuid.test.ts @@ -7,16 +7,16 @@ import * as uuid from 'vs/base/common/uuid'; suite('UUID', () => { test('generation', () => { - var asHex = uuid.v4().asHex(); + const asHex = uuid.v4().asHex(); assert.equal(asHex.length, 36); assert.equal(asHex[14], '4'); assert.ok(asHex[19] === '8' || asHex[19] === '9' || asHex[19] === 'a' || asHex[19] === 'b'); }); test('parse', () => { - var id = uuid.v4(); - var asHext = id.asHex(); - var id2 = uuid.parse(asHext); + const id = uuid.v4(); + const asHext = id.asHex(); + const id2 = uuid.parse(asHext); assert.equal(id.asHex(), id2.asHex()); }); }); diff --git a/src/vs/base/test/common/winjs.polyfill.promise.test.ts b/src/vs/base/test/common/winjs.polyfill.promise.test.ts deleted file mode 100644 index cab8b41c297..00000000000 --- a/src/vs/base/test/common/winjs.polyfill.promise.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import { Promise as WinJSPromise } from 'vs/base/common/winjs.base'; -import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; - -suite('Polyfill Promise', function () { - - test('sync-resolve, NativePromise', function () { - // native promise behaviour - const actual: string[] = []; - const promise = new Promise(resolve => { - actual.push('inCtor'); - resolve(null); - }).then(() => actual.push('inThen')); - actual.push('afterCtor'); - return promise.then(() => { - assert.deepEqual(actual, ['inCtor', 'afterCtor', 'inThen']); - }); - }); - - test('sync-resolve, WinJSPromise', function () { - - // winjs promise behaviour - const actual: string[] = []; - const promise = new WinJSPromise(resolve => { - actual.push('inCtor'); - resolve(null); - }).then(() => actual.push('inThen')); - actual.push('afterCtor'); - return promise.then(() => { - assert.deepEqual(actual, ['inCtor', 'inThen', 'afterCtor']); - }); - }); - - test('sync-resolve, PolyfillPromise', function () { - - // winjs promise behaviour - const actual: string[] = []; - const promise = new PolyfillPromise(resolve => { - actual.push('inCtor'); - resolve(null); - }).then(() => actual.push('inThen')); - actual.push('afterCtor'); - return promise.then(() => { - assert.deepEqual(actual, ['inCtor', 'afterCtor', 'inThen']); - }); - }); - - test('sync-then, NativePromise', function () { - const actual: string[] = []; - const promise = Promise.resolve(123).then(() => actual.push('inThen')); - actual.push('afterThen'); - return promise.then(() => { - assert.deepEqual(actual, ['afterThen', 'inThen']); - }); - }); - - test('sync-then, WinJSPromise', function () { - const actual: string[] = []; - const promise = WinJSPromise.as(123).then(() => actual.push('inThen')); - actual.push('afterThen'); - return promise.then(() => { - assert.deepEqual(actual, ['inThen', 'afterThen']); - }); - }); - - test('sync-then, PolyfillPromise', function () { - const actual: string[] = []; - const promise = PolyfillPromise.resolve(123).then(() => actual.push('inThen')); - actual.push('afterThen'); - return promise.then(() => { - assert.deepEqual(actual, ['afterThen', 'inThen']); - }); - }); - - test('PolyfillPromise, executor has two params', function () { - return new PolyfillPromise(function () { - assert.equal(arguments.length, 2); - assert.equal(typeof arguments[0], 'function'); - assert.equal(typeof arguments[1], 'function'); - - arguments[0](); - }); - }); - - test('Promises polyfill does not support chaining then and catch #57722', function () { - return PolyfillPromise.resolve(1).then(function (x) { return x + 1; }).then(function (x) { - assert.equal(x, 2); - }); - }); - - // run the same tests for the native and polyfill promise - ([Promise, PolyfillPromise]).forEach(PromiseCtor => { - - test(PromiseCtor.name + ', resolved value', function () { - return new PromiseCtor((resolve: Function) => resolve(1)).then((value: number) => assert.equal(value, 1)); - }); - - test(PromiseCtor.name + ', rejected value', function () { - return new PromiseCtor((_: Function, reject: Function) => reject(1)).then(null, (value: number) => assert.equal(value, 1)); - }); - - test(PromiseCtor.name + ', catch', function () { - return new PromiseCtor((_: Function, reject: Function) => reject(1)).catch((value: number) => assert.equal(value, 1)); - }); - - test(PromiseCtor.name + ', static-resolve', function () { - return PromiseCtor.resolve(42).then((value: number) => assert.equal(value, 42)); - }); - - test(PromiseCtor.name + ', static-reject', function () { - return PromiseCtor.reject(42).then(null, (value: number) => assert.equal(value, 42)); - }); - - test(PromiseCtor.name + ', static-all, 1', function () { - return PromiseCtor.all([ - PromiseCtor.resolve(1), - PromiseCtor.resolve(2) - ]).then((values: number[]) => { - assert.deepEqual(values, [1, 2]); - }); - }); - - test(PromiseCtor.name + ', static-all, 2', function () { - return PromiseCtor.all([ - PromiseCtor.resolve(1), - 3, - PromiseCtor.resolve(2) - ]).then((values: number[]) => { - assert.deepEqual(values, [1, 3, 2]); - }); - }); - - test(PromiseCtor.name + ', static-all, 3', function () { - return PromiseCtor.all([ - PromiseCtor.resolve(1), - PromiseCtor.reject(13), - PromiseCtor.reject(12), - ]).catch((values: number) => { - assert.deepEqual(values, 13); - }); - }); - - test(PromiseCtor.name + ', static-race, 1', function () { - return PromiseCtor.race([ - PromiseCtor.resolve(1), - PromiseCtor.resolve(2), - ]).then((value: number) => { - assert.deepEqual(value, 1); - }); - }); - - test(PromiseCtor.name + ', static-race, 2', function () { - return PromiseCtor.race([ - PromiseCtor.reject(-1), - PromiseCtor.resolve(2), - ]).catch((value: number) => { - assert.deepEqual(value, -1); - }); - }); - - test(PromiseCtor.name + ', static-race, 3', function () { - return PromiseCtor.race([ - PromiseCtor.resolve(1), - PromiseCtor.reject(2), - ]).then((value: number) => { - assert.deepEqual(value, 1); - }); - }); - - test(PromiseCtor.name + ', throw in ctor', function () { - return new PromiseCtor(() => { - throw new Error('sooo bad'); - }).catch((err: Error) => { - assert.equal(err.message, 'sooo bad'); - }); - }); - - }); -}); diff --git a/src/vs/base/test/common/winjs.promise.test.ts b/src/vs/base/test/common/winjs.promise.test.ts deleted file mode 100644 index 6296496c304..00000000000 --- a/src/vs/base/test/common/winjs.promise.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import * as winjs from 'vs/base/common/winjs.base'; - -suite('WinJS and ES6 Promises', function () { - - test('Promise.resolve', () => { - let resolveTPromise; - const tPromise = new winjs.Promise((c, e) => { - resolveTPromise = c; - }); - - const es6Promise = Promise.resolve(tPromise); - - const done = es6Promise.then(function (result) { - assert.equal(result, 'passed'); - }); - - resolveTPromise('passed'); - - return done; - }); - - test('new Promise', function () { - let resolveTPromise; - const tPromise = new winjs.Promise((c, e) => { - resolveTPromise = c; - }); - - const es6Promise = new Promise(function (c, e) { - c(tPromise); - }); - - const done = es6Promise.then(function (result) { - assert.equal(result, 'passed'); - }); - - resolveTPromise('passed'); - - return done; - }); - - test('1. Uncaught TypeError: this._state.then is not a function', () => { - let p1 = winjs.Promise.wrap(new Promise(function (c, e) { c(1); })); - Promise.all([p1]); - }); - - test('2. Uncaught TypeError: this._state.then is not a function', () => { - let p1 = winjs.Promise.wrap(new Promise(function (c, e) { c(1); })); - let thenFunc = p1.then.bind(p1); - setTimeout(() => { - thenFunc(() => { }); - }, 0); - }); - - test('3. Uncaught TypeError: this._state.then is not a function', () => { - let c; - let p1 = new winjs.Promise(function (_c, e) { c = _c; }); - let thenFunc = p1.then.bind(p1); - setTimeout(() => { - c(1); - thenFunc(() => { }); - }, 0); - }); -}); diff --git a/src/vs/base/test/node/config.test.ts b/src/vs/base/test/node/config.test.ts index ccb0a27ddc7..3fa8f78de68 100644 --- a/src/vs/base/test/node/config.test.ts +++ b/src/vs/base/test/node/config.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import * as os from 'os'; -import * as path from 'path'; +import * as path from 'vs/base/common/path'; import * as fs from 'fs'; import * as uuid from 'vs/base/common/uuid'; import { ConfigWatcher } from 'vs/base/node/config'; @@ -20,7 +20,7 @@ suite('Config', () => { const newDir = path.join(parentDir, 'config', id); const testFile = path.join(newDir, 'config.json'); - let watcher = new ConfigWatcher(testFile); + let watcher = new ConfigWatcher<{}>(testFile); let config = watcher.getConfig(); assert.ok(config); @@ -46,9 +46,6 @@ suite('Config', () => { let config = watcher.getConfig(); assert.ok(config); assert.equal(config.foo, 'bar'); - assert.equal(watcher.getValue('foo'), 'bar'); - assert.equal(watcher.getValue('bar'), void 0); - assert.equal(watcher.getValue('bar', 'fallback'), 'fallback'); assert.ok(!watcher.hasParseErrors); watcher.dispose(); @@ -144,20 +141,18 @@ suite('Config', () => { testFile('config', 'config.json').then(res => { fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "bar" }'); - let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error }); + let watcher = new ConfigWatcher<{ foo: string; }>(res.testFile, { changeBufferDelay: 100, onError: console.error, defaultConfig: { foo: 'bar' } }); watcher.getConfig(); // ensure we are in sync fs.writeFileSync(res.testFile, '// my comment\n{ "foo": "changed" }'); // still old values because change is not bubbling yet assert.equal(watcher.getConfig().foo, 'bar'); - assert.equal(watcher.getValue('foo'), 'bar'); // force a load from disk watcher.reload(config => { assert.equal(config.foo, 'changed'); assert.equal(watcher.getConfig().foo, 'changed'); - assert.equal(watcher.getValue('foo'), 'changed'); watcher.dispose(); diff --git a/src/vs/base/test/node/console.test.ts b/src/vs/base/test/node/console.test.ts index 90f62271489..e7a243a2d42 100644 --- a/src/vs/base/test/node/console.test.ts +++ b/src/vs/base/test/node/console.test.ts @@ -4,46 +4,45 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { getFirstFrame } from 'vs/base/node/console'; -import { normalize } from 'path'; +import { getFirstFrame } from 'vs/base/common/console'; +import { normalize } from 'vs/base/common/path'; suite('Console', () => { test('getFirstFrame', () => { let stack = 'at vscode.commands.registerCommand (/Users/someone/Desktop/test-ts/out/src/extension.js:18:17)'; - let frame = getFirstFrame(stack); + let frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js')); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at /Users/someone/Desktop/test-ts/out/src/extension.js:18:17'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js')); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at c:\\Users\\someone\\Desktop\\end-js\\extension.js:18:17'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, 'c:\\Users\\someone\\Desktop\\end-js\\extension.js'); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at e.$executeContributedCommand(c:\\Users\\someone\\Desktop\\end-js\\extension.js:18:17)'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, 'c:\\Users\\someone\\Desktop\\end-js\\extension.js'); assert.equal(frame.line, 18); assert.equal(frame.column, 17); stack = 'at /Users/someone/Desktop/test-ts/out/src/extension.js:18:17\nat /Users/someone/Desktop/test-ts/out/src/other.js:28:27\nat /Users/someone/Desktop/test-ts/out/src/more.js:38:37'; - frame = getFirstFrame(stack); + frame = getFirstFrame(stack)!; assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js')); assert.equal(frame.line, 18); assert.equal(frame.column, 17); - }); }); \ No newline at end of file diff --git a/src/vs/base/test/node/encoding/encoding.test.ts b/src/vs/base/test/node/encoding/encoding.test.ts index db9cc560e5a..77ad22264cf 100644 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ b/src/vs/base/test/node/encoding/encoding.test.ts @@ -6,43 +6,114 @@ import * as assert from 'assert'; 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'; +export async function detectEncodingByBOM(file: string): Promise { + try { + const { buffer, bytesRead } = await readExactlyByFile(file, 3); + + return encoding.detectEncodingByBOMFromBuffer(buffer, bytesRead); + } catch (error) { + return null; // ignore errors (like file not found) + } +} + +interface ReadResult { + buffer: Buffer | null; + bytesRead: number; +} + +function readExactlyByFile(file: string, totalBytes: number): Promise { + return new Promise((resolve, reject) => { + fs.open(file, 'r', null, (err, fd) => { + if (err) { + return reject(err); + } + + function end(err: Error | null, resultBuffer: Buffer | null, bytesRead: number): void { + fs.close(fd, closeError => { + if (closeError) { + return reject(closeError); + } + + if (err && (err).code === 'EISDIR') { + return reject(err); // we want to bubble this error up (file is actually a folder) + } + + return resolve({ buffer: resultBuffer, bytesRead }); + }); + } + + const buffer = Buffer.allocUnsafe(totalBytes); + let offset = 0; + + function readChunk(): void { + fs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => { + if (err) { + return end(err, null, 0); + } + + if (bytesRead === 0) { + return end(null, buffer, offset); + } + + offset += bytesRead; + + if (offset === totalBytes) { + return end(null, buffer, offset); + } + + return readChunk(); + }); + } + + readChunk(); + }); + }); +} + suite('Encoding', () => { + + test('detectBOM does not return error for non existing file', async () => { + const file = getPathFromAmdModule(require, './fixtures/not-exist.css'); + + const detectedEncoding = await detectEncodingByBOM(file); + assert.equal(detectedEncoding, null); + }); + test('detectBOM UTF-8', async () => { const file = getPathFromAmdModule(require, './fixtures/some_utf8.css'); - const detectedEncoding = await encoding.detectEncodingByBOM(file); + const detectedEncoding = await detectEncodingByBOM(file); assert.equal(detectedEncoding, 'utf8'); }); test('detectBOM UTF-16 LE', async () => { const file = getPathFromAmdModule(require, './fixtures/some_utf16le.css'); - const detectedEncoding = await encoding.detectEncodingByBOM(file); + const detectedEncoding = await detectEncodingByBOM(file); assert.equal(detectedEncoding, 'utf16le'); }); test('detectBOM UTF-16 BE', async () => { const file = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); - const detectedEncoding = await encoding.detectEncodingByBOM(file); + const detectedEncoding = await detectEncodingByBOM(file); assert.equal(detectedEncoding, 'utf16be'); }); test('detectBOM ANSI', async function () { const file = getPathFromAmdModule(require, './fixtures/some_ansi.css'); - const detectedEncoding = await encoding.detectEncodingByBOM(file); + const detectedEncoding = await detectEncodingByBOM(file); assert.equal(detectedEncoding, null); }); test('detectBOM ANSI', async function () { const file = getPathFromAmdModule(require, './fixtures/empty.txt'); - const detectedEncoding = await encoding.detectEncodingByBOM(file); + const detectedEncoding = await detectEncodingByBOM(file); assert.equal(detectedEncoding, null); }); @@ -132,13 +203,13 @@ suite('Encoding', () => { assert.equal(mimes.encoding, 'windows1252'); }); - async function readAndDecodeFromDisk(path, _encoding) { + async function readAndDecodeFromDisk(path: string, fileEncoding: string | null) { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) { reject(err); } else { - resolve(encoding.decode(data, _encoding)); + resolve(encoding.decode(data, fileEncoding!)); } }); }); @@ -169,7 +240,7 @@ suite('Encoding', () => { } }); - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4 }); + let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); assert.ok(detected); assert.ok(stream); @@ -189,7 +260,7 @@ suite('Encoding', () => { } }); - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64 }); + let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); assert.ok(detected); assert.ok(stream); @@ -206,7 +277,7 @@ suite('Encoding', () => { } }); - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 512 }); + let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 512, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); assert.ok(detected); assert.ok(stream); @@ -221,7 +292,7 @@ suite('Encoding', () => { let path = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); let source = fs.createReadStream(path); - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64 }); + let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); assert.equal(detected.encoding, 'utf16be'); assert.equal(detected.seemsBinary, false); @@ -236,7 +307,7 @@ suite('Encoding', () => { let path = getPathFromAmdModule(require, './fixtures/empty.txt'); let source = fs.createReadStream(path); - let { detected, stream } = await encoding.toDecodeStream(source, {}); + let { detected, stream } = await encoding.toDecodeStream(source, { guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); let expected = await readAndDecodeFromDisk(path, detected.encoding); let actual = await readAllAsString(stream); diff --git a/src/vs/base/test/node/extfs/extfs.test.ts b/src/vs/base/test/node/extfs/extfs.test.ts deleted file mode 100644 index 4031d3080da..00000000000 --- a/src/vs/base/test/node/extfs/extfs.test.ts +++ /dev/null @@ -1,617 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { Readable } from 'stream'; -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 = () => { }; - -const mkdirp = (path: string, mode: number, callback: (error) => void) => { - extfs.mkdirp(path, mode).then(() => callback(null), error => callback(error)); -}; - -const chunkSize = 64 * 1024; -const readError = 'Error while reading'; -function toReadable(value: string, throwError?: boolean): Readable { - const totalChunks = Math.ceil(value.length / chunkSize); - const stringChunks: string[] = []; - - for (let i = 0, j = 0; i < totalChunks; ++i, j += chunkSize) { - stringChunks[i] = value.substr(j, chunkSize); - } - - let counter = 0; - return new Readable({ - read: function () { - if (throwError) { - this.emit('error', new Error(readError)); - } - - let res: string; - let canPush = true; - while (canPush && (res = stringChunks[counter++])) { - canPush = this.push(res); - } - - // EOS - if (!res) { - this.push(null); - } - }, - encoding: 'utf8' - }); -} - -suite('Extfs', () => { - - test('mkdirp', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); // 493 = 0755 - }); - - test('stat link', function (done) { - if (isWindows) { - // Symlinks are not the same on win, and we can not create them programitically without admin privileges - return done(); - } - - const id1 = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id1); - const directory = path.join(parentDir, 'extfs', id1); - - const id2 = uuid.generateUuid(); - const symbolicLink = path.join(parentDir, 'extfs', id2); - - mkdirp(directory, 493, error => { - if (error) { - return done(error); - } - - fs.symlinkSync(directory, symbolicLink); - - extfs.statLink(directory, (error, statAndIsLink) => { - if (error) { - return done(error); - } - - assert.ok(!statAndIsLink.isSymbolicLink); - - extfs.statLink(symbolicLink, (error, statAndIsLink) => { - if (error) { - return done(error); - } - - assert.ok(statAndIsLink.isSymbolicLink); - extfs.delSync(directory); - done(); - }); - }); - }); - }); - - test('delSync - swallows file not found error', function () { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - extfs.delSync(newDir); - - assert.ok(!fs.existsSync(newDir)); - }); - - test('delSync - simple', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); - fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); - - extfs.delSync(newDir); - - assert.ok(!fs.existsSync(newDir)); - done(); - }); // 493 = 0755 - }); - - test('delSync - recursive folder structure', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); - fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); - - fs.mkdirSync(path.join(newDir, 'somefolder')); - fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents'); - - extfs.delSync(newDir); - - assert.ok(!fs.existsSync(newDir)); - done(); - }); // 493 = 0755 - }); - - test('copy, move and delete', function (done) { - const id = uuid.generateUuid(); - const id2 = uuid.generateUuid(); - 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); - - extfs.copy(sourceDir, targetDir, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(targetDir)); - assert.ok(fs.existsSync(path.join(targetDir, 'index.html'))); - assert.ok(fs.existsSync(path.join(targetDir, 'site.css'))); - assert.ok(fs.existsSync(path.join(targetDir, 'examples'))); - assert.ok(fs.statSync(path.join(targetDir, 'examples')).isDirectory()); - assert.ok(fs.existsSync(path.join(targetDir, 'examples', 'small.jxs'))); - - extfs.mv(targetDir, targetDir2, error => { - if (error) { - return done(error); - } - - assert.ok(!fs.existsSync(targetDir)); - assert.ok(fs.existsSync(targetDir2)); - assert.ok(fs.existsSync(path.join(targetDir2, 'index.html'))); - assert.ok(fs.existsSync(path.join(targetDir2, 'site.css'))); - assert.ok(fs.existsSync(path.join(targetDir2, 'examples'))); - assert.ok(fs.statSync(path.join(targetDir2, 'examples')).isDirectory()); - assert.ok(fs.existsSync(path.join(targetDir2, 'examples', 'small.jxs'))); - - extfs.mv(path.join(targetDir2, 'index.html'), path.join(targetDir2, 'index_moved.html'), error => { - if (error) { - return done(error); - } - - assert.ok(!fs.existsSync(path.join(targetDir2, 'index.html'))); - assert.ok(fs.existsSync(path.join(targetDir2, 'index_moved.html'))); - - extfs.del(parentDir, os.tmpdir(), error => { - if (error) { - return done(error); - } - }, error => { - if (error) { - return done(error); - } - assert.ok(!fs.existsSync(parentDir)); - done(); - }); - }); - }); - }); - }); - - test('readdir', function (done) { - if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id, 'öäü'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - extfs.readdir(path.join(parentDir, 'extfs', id), (error, children) => { - assert.equal(children.some(n => n === 'öäü'), true); // Mac always converts to NFD, so - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); // 493 = 0755 - } else { - done(); - } - }); - - test('writeFileAndFlush (string)', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { - if (error) { - return done(error); - } - - assert.equal(fs.readFileSync(testFile), 'Hello World'); - - const largeString = (new Array(100 * 1024)).join('Large String\n'); - - extfs.writeFileAndFlush(testFile, largeString, null, error => { - if (error) { - return done(error); - } - - assert.equal(fs.readFileSync(testFile), largeString); - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - }); - - test('writeFileAndFlush (stream)', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { - if (error) { - return done(error); - } - - assert.equal(fs.readFileSync(testFile), 'Hello World'); - - const largeString = (new Array(100 * 1024)).join('Large String\n'); - - extfs.writeFileAndFlush(testFile, toReadable(largeString), null, error => { - if (error) { - return done(error); - } - - assert.equal(fs.readFileSync(testFile), largeString); - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - }); - - test('writeFileAndFlush (file stream)', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { - if (error) { - return done(error); - } - - assert.equal(fs.readFileSync(testFile).toString(), fs.readFileSync(sourceFile).toString()); - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - - test('writeFileAndFlush (string, error handling)', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - - extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { - if (!error) { - return done(new Error('Expected error for writing to readonly file')); - } - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - - test('writeFileAndFlush (stream, error handling EISDIR)', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - - const readable = toReadable('Hello World'); - extfs.writeFileAndFlush(testFile, readable, null, error => { - if (!error || (error).code !== 'EISDIR') { - return done(new Error('Expected EISDIR error for writing to folder but got: ' + (error ? (error).code : 'no error'))); - } - - // verify that the stream is still consumable (for https://github.com/Microsoft/vscode/issues/42542) - assert.equal(readable.read(), 'Hello World'); - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - - test('writeFileAndFlush (stream, error handling READERROR)', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - extfs.writeFileAndFlush(testFile, toReadable('Hello World', true /* throw error */), null, error => { - if (!error || error.message !== readError) { - return done(new Error('Expected error for writing to folder')); - } - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - - test('writeFileAndFlush (stream, error handling EACCES)', function (done) { - if (isLinux) { - return done(); // somehow this test fails on Linux in our TFS builds - } - - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - fs.writeFileSync(testFile, ''); - fs.chmodSync(testFile, 33060); // make readonly - - extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { - if (!error || !((error).code !== 'EACCES' || (error).code !== 'EPERM')) { - return done(new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (error ? (error).code : 'no error'))); - } - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - - test('writeFileAndFlush (file stream, error handling)', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! - - extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { - if (!error) { - return done(new Error('Expected error for writing to folder')); - } - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - - test('writeFileAndFlushSync', function (done) { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - const testFile = path.join(newDir, 'flushed.txt'); - - mkdirp(newDir, 493, error => { - if (error) { - return done(error); - } - - assert.ok(fs.existsSync(newDir)); - - extfs.writeFileAndFlushSync(testFile, 'Hello World', null); - assert.equal(fs.readFileSync(testFile), 'Hello World'); - - const largeString = (new Array(100 * 1024)).join('Large String\n'); - - extfs.writeFileAndFlushSync(testFile, largeString, null); - assert.equal(fs.readFileSync(testFile), largeString); - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - - test('realcase', (done) => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - mkdirp(newDir, 493, error => { - - // assume case insensitive file system - if (process.platform === 'win32' || process.platform === 'darwin') { - const upper = newDir.toUpperCase(); - const real = extfs.realcaseSync(upper); - - if (real) { // can be null in case of permission errors - assert.notEqual(real, upper); - assert.equal(real.toUpperCase(), upper); - assert.equal(real, newDir); - } - } - - // linux, unix, etc. -> assume case sensitive file system - else { - const real = extfs.realcaseSync(newDir); - assert.equal(real, newDir); - } - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - - test('realpath', (done) => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - mkdirp(newDir, 493, error => { - - extfs.realpath(newDir, (error, realpath) => { - assert.ok(realpath); - assert.ok(!error); - - extfs.del(parentDir, os.tmpdir(), done, ignore); - }); - }); - }); - - test('realpathSync', (done) => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - mkdirp(newDir, 493, error => { - let realpath: string; - try { - realpath = extfs.realpathSync(newDir); - } catch (error) { - assert.ok(!error); - } - assert.ok(realpath); - - 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); - }); - }); - - test('sanitizeFilePath', () => { - if (isWindows) { - assert.equal(extfs.sanitizeFilePath('.', 'C:\\the\\cwd'), 'C:\\the\\cwd'); - assert.equal(extfs.sanitizeFilePath('', 'C:\\the\\cwd'), 'C:\\the\\cwd'); - - assert.equal(extfs.sanitizeFilePath('C:', 'C:\\the\\cwd'), 'C:\\'); - assert.equal(extfs.sanitizeFilePath('C:\\', 'C:\\the\\cwd'), 'C:\\'); - assert.equal(extfs.sanitizeFilePath('C:\\\\', 'C:\\the\\cwd'), 'C:\\'); - - assert.equal(extfs.sanitizeFilePath('C:\\folder\\my.txt', 'C:\\the\\cwd'), 'C:\\folder\\my.txt'); - assert.equal(extfs.sanitizeFilePath('C:\\folder\\my', 'C:\\the\\cwd'), 'C:\\folder\\my'); - assert.equal(extfs.sanitizeFilePath('C:\\folder\\..\\my', 'C:\\the\\cwd'), 'C:\\my'); - assert.equal(extfs.sanitizeFilePath('C:\\folder\\my\\', 'C:\\the\\cwd'), 'C:\\folder\\my'); - assert.equal(extfs.sanitizeFilePath('C:\\folder\\my\\\\\\', 'C:\\the\\cwd'), 'C:\\folder\\my'); - - assert.equal(extfs.sanitizeFilePath('my.txt', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt'); - assert.equal(extfs.sanitizeFilePath('my.txt\\', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt'); - - assert.equal(extfs.sanitizeFilePath('\\\\localhost\\folder\\my', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my'); - assert.equal(extfs.sanitizeFilePath('\\\\localhost\\folder\\my\\', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my'); - } else { - assert.equal(extfs.sanitizeFilePath('.', '/the/cwd'), '/the/cwd'); - assert.equal(extfs.sanitizeFilePath('', '/the/cwd'), '/the/cwd'); - assert.equal(extfs.sanitizeFilePath('/', '/the/cwd'), '/'); - - assert.equal(extfs.sanitizeFilePath('/folder/my.txt', '/the/cwd'), '/folder/my.txt'); - assert.equal(extfs.sanitizeFilePath('/folder/my', '/the/cwd'), '/folder/my'); - assert.equal(extfs.sanitizeFilePath('/folder/../my', '/the/cwd'), '/my'); - assert.equal(extfs.sanitizeFilePath('/folder/my/', '/the/cwd'), '/folder/my'); - assert.equal(extfs.sanitizeFilePath('/folder/my///', '/the/cwd'), '/folder/my'); - - assert.equal(extfs.sanitizeFilePath('my.txt', '/the/cwd'), '/the/cwd/my.txt'); - assert.equal(extfs.sanitizeFilePath('my.txt/', '/the/cwd'), '/the/cwd/my.txt'); - } - }); -}); diff --git a/src/vs/base/test/node/extpath.test.ts b/src/vs/base/test/node/extpath.test.ts new file mode 100644 index 00000000000..e435d624801 --- /dev/null +++ b/src/vs/base/test/node/extpath.test.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as os from 'os'; +import * as path from 'vs/base/common/path'; +import * as uuid from 'vs/base/common/uuid'; +import * as pfs from 'vs/base/node/pfs'; +import { realcaseSync, realpath, realpathSync } from 'vs/base/node/extpath'; + +suite('Extpath', () => { + + test('realcase', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extpath', id); + + await pfs.mkdirp(newDir, 493); + + // assume case insensitive file system + if (process.platform === 'win32' || process.platform === 'darwin') { + const upper = newDir.toUpperCase(); + const real = realcaseSync(upper); + + if (real) { // can be null in case of permission errors + assert.notEqual(real, upper); + assert.equal(real.toUpperCase(), upper); + assert.equal(real, newDir); + } + } + + // linux, unix, etc. -> assume case sensitive file system + else { + const real = realcaseSync(newDir); + assert.equal(real, newDir); + } + + await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); + + test('realpath', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extpath', id); + + await pfs.mkdirp(newDir, 493); + + const realpathVal = await realpath(newDir); + assert.ok(realpathVal); + + await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); + + test('realpathSync', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extpath', id); + + await pfs.mkdirp(newDir, 493); + + let realpath!: string; + try { + realpath = realpathSync(newDir); + } catch (error) { + assert.ok(!error); + } + assert.ok(realpath!); + + await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); +}); diff --git a/src/vs/base/test/node/flow.test.ts b/src/vs/base/test/node/flow.test.ts deleted file mode 100644 index 7aa79a5019b..00000000000 --- a/src/vs/base/test/node/flow.test.ts +++ /dev/null @@ -1,488 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as flow from 'vs/base/node/flow'; - -const loop = flow.loop; -const sequence = flow.sequence; -const parallel = flow.parallel; - -suite('Flow', () => { - function assertCounterEquals(counter: number, expected: number): void { - assert.ok(counter === expected, 'Expected ' + expected + ' assertions, but got ' + counter); - } - - function syncThrowsError(callback: any): void { - callback(new Error('foo'), null); - } - - function syncSequenceGetThrowsError(value: any, callback: any) { - sequence( - function onError(error) { - callback(error, null); - }, - - function getFirst(this: any) { - syncThrowsError(this); - }, - - function handleFirst(first: number) { - //Foo - } - ); - } - - function syncGet(value: any, callback: any): void { - callback(null, value); - } - - function syncGetError(value: any, callback: any): void { - callback(new Error(''), null); - } - - function asyncGet(value: any, callback: any): void { - process.nextTick(function () { - callback(null, value); - }); - } - - function asyncGetError(value: any, callback: any): void { - process.nextTick(function () { - callback(new Error(''), null); - }); - } - - test('loopSync', function (done: () => void) { - const elements = ['1', '2', '3']; - loop(elements, function (element, callback, index, total) { - assert.ok(index === 0 || index === 1 || index === 2); - assert.deepEqual(3, total); - callback(null, element); - }, function (error, result) { - assert.equal(error, null); - assert.deepEqual(result, elements); - - done(); - }); - }); - - test('loopByFunctionSync', function (done: () => void) { - const elements = function (callback: Function) { - callback(null, ['1', '2', '3']); - }; - - loop(elements, function (element, callback) { - callback(null, element); - }, function (error, result) { - assert.equal(error, null); - assert.deepEqual(result, ['1', '2', '3']); - - done(); - }); - }); - - test('loopByFunctionAsync', function (done: () => void) { - const elements = function (callback: Function) { - process.nextTick(function () { - callback(null, ['1', '2', '3']); - }); - }; - - loop(elements, function (element, callback) { - callback(null, element); - }, function (error, result) { - assert.equal(error, null); - assert.deepEqual(result, ['1', '2', '3']); - - done(); - }); - }); - - test('loopSyncErrorByThrow', function (done: () => void) { - const elements = ['1', '2', '3']; - loop(elements, function (element, callback) { - if (element === '2') { - throw new Error('foo'); - } else { - callback(null, element); - } - }, function (error, result) { - assert.ok(error); - assert.ok(!result); - - done(); - }); - }); - - test('loopSyncErrorByCallback', function (done: () => void) { - const elements = ['1', '2', '3']; - loop(elements, function (element, callback) { - if (element === '2') { - callback(new Error('foo'), null); - } else { - callback(null, element); - } - }, function (error, result) { - assert.ok(error); - assert.ok(!result); - - done(); - }); - }); - - test('loopAsync', function (done: () => void) { - const elements = ['1', '2', '3']; - loop(elements, function (element, callback) { - process.nextTick(function () { - callback(null, element); - }); - }, function (error, result) { - assert.equal(error, null); - assert.deepEqual(result, elements); - - done(); - }); - }); - - test('loopAsyncErrorByCallback', function (done: () => void) { - const elements = ['1', '2', '3']; - loop(elements, function (element, callback) { - process.nextTick(function () { - if (element === '2') { - callback(new Error('foo'), null); - } else { - callback(null, element); - } - }); - }, function (error, result) { - assert.ok(error); - assert.ok(!result); - - done(); - }); - }); - - test('sequenceSync', function (done: () => void) { - let assertionCount = 0; - let errorCount = 0; - - sequence( - function onError(error) { - errorCount++; - }, - - function getFirst(this: any) { - syncGet('1', this); - }, - - function handleFirst(this: any, first: number) { - assert.deepEqual('1', first); - assertionCount++; - syncGet('2', this); - }, - - function handleSecond(this: any, second: any) { - assert.deepEqual('2', second); - assertionCount++; - syncGet(null, this); - }, - - function handleThird(third: any) { - assert.ok(!third); - assertionCount++; - - assertCounterEquals(assertionCount, 3); - assertCounterEquals(errorCount, 0); - done(); - } - ); - }); - - test('sequenceAsync', function (done: () => void) { - let assertionCount = 0; - let errorCount = 0; - - sequence( - function onError(error) { - errorCount++; - }, - - function getFirst(this: any) { - asyncGet('1', this); - }, - - function handleFirst(this: any, first: number) { - assert.deepEqual('1', first); - assertionCount++; - asyncGet('2', this); - }, - - function handleSecond(this: any, second: number) { - assert.deepEqual('2', second); - assertionCount++; - asyncGet(null, this); - }, - - function handleThird(third: number) { - assert.ok(!third); - assertionCount++; - - assertCounterEquals(assertionCount, 3); - assertCounterEquals(errorCount, 0); - done(); - } - ); - }); - - test('sequenceSyncErrorByThrow', function (done: () => void) { - let assertionCount = 0; - let errorCount = 0; - - sequence( - function onError(error) { - errorCount++; - - assertCounterEquals(assertionCount, 1); - assertCounterEquals(errorCount, 1); - done(); - }, - - function getFirst(this: any) { - syncGet('1', this); - }, - - function handleFirst(this: any, first: number) { - assert.deepEqual('1', first); - assertionCount++; - syncGet('2', this); - }, - - function handleSecond(second: number) { - if (true) { - throw new Error(''); - } - // assertionCount++; - // syncGet(null, this); - }, - - function handleThird(third: number) { - throw new Error('We should not be here'); - } - ); - }); - - test('sequenceSyncErrorByCallback', function (done: () => void) { - let assertionCount = 0; - let errorCount = 0; - - sequence( - function onError(error) { - errorCount++; - - assertCounterEquals(assertionCount, 1); - assertCounterEquals(errorCount, 1); - done(); - }, - - function getFirst(this: any) { - syncGet('1', this); - }, - - function handleFirst(this: any, first: number) { - assert.deepEqual('1', first); - assertionCount++; - syncGetError('2', this); - }, - - function handleSecond(second: number) { - throw new Error('We should not be here'); - } - ); - }); - - test('sequenceAsyncErrorByThrow', function (done: () => void) { - let assertionCount = 0; - let errorCount = 0; - - sequence( - function onError(error) { - errorCount++; - - assertCounterEquals(assertionCount, 1); - assertCounterEquals(errorCount, 1); - done(); - }, - - function getFirst(this: any) { - asyncGet('1', this); - }, - - function handleFirst(this: any, first: number) { - assert.deepEqual('1', first); - assertionCount++; - asyncGet('2', this); - }, - - function handleSecond(second: number) { - if (true) { - throw new Error(''); - } - // assertionCount++; - // asyncGet(null, this); - }, - - function handleThird(third: number) { - throw new Error('We should not be here'); - } - ); - }); - - test('sequenceAsyncErrorByCallback', function (done: () => void) { - let assertionCount = 0; - let errorCount = 0; - - sequence( - function onError(error) { - errorCount++; - - assertCounterEquals(assertionCount, 1); - assertCounterEquals(errorCount, 1); - done(); - }, - - function getFirst(this: any) { - asyncGet('1', this); - }, - - function handleFirst(this: any, first: number) { - assert.deepEqual('1', first); - assertionCount++; - asyncGetError('2', this); - }, - - function handleSecond(second: number) { - throw new Error('We should not be here'); - } - ); - }); - - test('syncChainedSequenceError', function (done: () => void) { - sequence( - function onError(error) { - done(); - }, - - function getFirst(this: any) { - syncSequenceGetThrowsError('1', this); - } - ); - }); - - test('tolerateBooleanResults', function (done: () => void) { - let assertionCount = 0; - let errorCount = 0; - - sequence( - function onError(error) { - errorCount++; - }, - - function getFirst(this: any) { - this(true); - }, - - function getSecond(this: any, result: boolean) { - assert.equal(result, true); - this(false); - }, - - function last(result: boolean) { - assert.equal(result, false); - assertionCount++; - - assertCounterEquals(assertionCount, 1); - assertCounterEquals(errorCount, 0); - done(); - } - ); - }); - - test('loopTolerateBooleanResults', function (done: () => void) { - let elements = ['1', '2', '3']; - loop(elements, function (element, callback) { - process.nextTick(function () { - (callback)(true); - }); - }, function (error, result) { - assert.equal(error, null); - assert.deepEqual(result, [true, true, true]); - - done(); - }); - }); - - test('parallel', function (done: () => void) { - let elements = [1, 2, 3, 4, 5]; - let sum = 0; - - parallel(elements, function (element, callback) { - sum += element; - callback(null, element * element); - }, function (errors, result) { - assert.ok(!errors); - - assert.deepEqual(sum, 15); - assert.deepEqual(result, [1, 4, 9, 16, 25]); - - done(); - }); - }); - - test('parallel - setTimeout', function (done: () => void) { - let elements = [1, 2, 3, 4, 5]; - let timeouts = [10, 30, 5, 0, 4]; - let sum = 0; - - parallel(elements, function (element, callback) { - setTimeout(function () { - sum += element; - callback(null, element * element); - }, timeouts.pop()); - }, function (errors, result) { - assert.ok(!errors); - - assert.deepEqual(sum, 15); - assert.deepEqual(result, [1, 4, 9, 16, 25]); - - done(); - }); - }); - - test('parallel - with error', function (done: () => void) { - const elements = [1, 2, 3, 4, 5]; - const timeouts = [10, 30, 5, 0, 4]; - let sum = 0; - - parallel(elements, function (element, callback) { - setTimeout(function () { - if (element === 4) { - callback(new Error('error!'), null); - } else { - sum += element; - callback(null, element * element); - } - }, timeouts.pop()); - }, function (errors, result) { - assert.ok(errors); - assert.deepEqual(errors, [null, null, null, new Error('error!'), null]); - - assert.deepEqual(sum, 11); - assert.deepEqual(result, [1, 4, 9, null, 25]); - - done(); - }); - }); -}); \ No newline at end of file diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index 4f851a4ca4f..53170961b90 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as path from 'path'; +import * as path from 'vs/base/common/path'; import * as glob from 'vs/base/common/glob'; import { isWindows } from 'vs/base/common/platform'; @@ -102,6 +102,9 @@ suite('Glob', () => { p = 'C:/DNXConsoleApp/**/*.cs'; assertGlobMatch(p, 'C:\\DNXConsoleApp\\Program.cs'); assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); + + p = '*'; + assertGlobMatch(p, ''); }); test('dot hidden', function () { @@ -428,7 +431,7 @@ suite('Glob', () => { test('expression support (single)', function () { let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; // { "**/*.js": { "when": "$(basename).ts" } } let expression: glob.IExpression = { @@ -452,7 +455,7 @@ suite('Glob', () => { expression = { '**/*.js': { - } + } as any }; assert.strictEqual('**/*.js', glob.match(expression, 'test.js', hasSibling)); @@ -464,14 +467,14 @@ suite('Glob', () => { test('expression support (multiple)', function () { let siblings = ['test.html', 'test.txt', 'test.ts', 'test.js']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; // { "**/*.js": { "when": "$(basename).ts" } } let expression: glob.IExpression = { '**/*.js': { when: '$(basename).ts' }, '**/*.as': true, '**/*.foo': false, - '**/*.bananas': { bananas: true } + '**/*.bananas': { bananas: true } as any }; assert.strictEqual('**/*.js', glob.match(expression, 'test.js', hasSibling)); @@ -688,7 +691,7 @@ suite('Glob', () => { }); test('expression with other falsy value', function () { - let expr = { '**/*.js': 0 }; + let expr = { '**/*.js': 0 } as any; assert.strictEqual(glob.match(expr, 'foo.js'), '**/*.js'); }); @@ -714,12 +717,15 @@ suite('Glob', () => { }; let siblings = ['foo.ts', 'foo.js', 'foo', 'bar']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; assert.strictEqual(glob.match(expr, 'bar', hasSibling), '**/bar'); assert.strictEqual(glob.match(expr, 'foo', hasSibling), null); assert.strictEqual(glob.match(expr, 'foo/bar', hasSibling), '**/bar'); - assert.strictEqual(glob.match(expr, 'foo\\bar', hasSibling), '**/bar'); + if (isWindows) { + // backslash is a valid file name character on posix + assert.strictEqual(glob.match(expr, 'foo\\bar', hasSibling), '**/bar'); + } assert.strictEqual(glob.match(expr, 'foo/foo', hasSibling), null); assert.strictEqual(glob.match(expr, 'foo.js', hasSibling), '**/*.js'); assert.strictEqual(glob.match(expr, 'bar.js', hasSibling), null); @@ -738,24 +744,24 @@ suite('Glob', () => { }); test('falsy expression/pattern', function () { - assert.strictEqual(glob.match(null, 'foo'), false); + assert.strictEqual(glob.match(null!, 'foo'), false); assert.strictEqual(glob.match('', 'foo'), false); - assert.strictEqual(glob.parse(null)('foo'), false); + assert.strictEqual(glob.parse(null!)('foo'), false); assert.strictEqual(glob.parse('')('foo'), false); }); test('falsy path', function () { - assert.strictEqual(glob.parse('foo')(null), false); + assert.strictEqual(glob.parse('foo')(null!), false); assert.strictEqual(glob.parse('foo')(''), false); - assert.strictEqual(glob.parse('**/*.j?')(null), false); + assert.strictEqual(glob.parse('**/*.j?')(null!), false); assert.strictEqual(glob.parse('**/*.j?')(''), false); - assert.strictEqual(glob.parse('**/*.foo')(null), false); + assert.strictEqual(glob.parse('**/*.foo')(null!), false); assert.strictEqual(glob.parse('**/*.foo')(''), false); - assert.strictEqual(glob.parse('**/foo')(null), false); + assert.strictEqual(glob.parse('**/foo')(null!), false); assert.strictEqual(glob.parse('**/foo')(''), false); - assert.strictEqual(glob.parse('{**/baz,**/foo}')(null), false); + assert.strictEqual(glob.parse('{**/baz,**/foo}')(null!), false); assert.strictEqual(glob.parse('{**/baz,**/foo}')(''), false); - assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(null), false); + assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(null!), false); assert.strictEqual(glob.parse('{**/*.baz,**/*.foo}')(''), false); }); @@ -768,7 +774,7 @@ suite('Glob', () => { let expr = { '**/*.js': { when: '$(basename).ts' } }; let siblings = ['foo.ts', 'foo.js']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; assert.strictEqual(glob.parse(expr)('bar/baz.js', 'baz.js', hasSibling), null); assert.strictEqual(glob.parse(expr)('bar/foo.js', 'foo.js', hasSibling), '**/*.js'); @@ -808,21 +814,21 @@ suite('Glob', () => { }, ['foo', 'bar', 'baz'], [ ['bar/foo', '**/foo/**'], ['foo/bar', '{**/bar/**,**/baz/**}'], - ['bar/nope', null] + ['bar/nope', null!] ]); const siblings = ['baz', 'baz.zip', 'nope']; - const hasSibling = name => siblings.indexOf(name) !== -1; + const hasSibling = (name: string) => siblings.indexOf(name) !== -1; testOptimizationForBasenames({ '**/foo/**': { when: '$(basename).zip' }, '**/bar/**': true }, ['bar'], [ - ['bar/foo', null], - ['bar/foo/baz', null], - ['bar/foo/nope', null], + ['bar/foo', null!], + ['bar/foo/baz', null!], + ['bar/foo/nope', null!], ['foo/bar', '**/bar/**'], ], [ - null, + null!, hasSibling, hasSibling ]); @@ -832,7 +838,7 @@ suite('Glob', () => { const parsed = glob.parse(pattern, { trimForExclusions: true }); assert.deepStrictEqual(glob.getBasenameTerms(parsed), basenameTerms); matches.forEach(([text, result], i) => { - assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + assert.strictEqual(parsed(text, null!, siblingsFns[i]), result); }); } @@ -914,21 +920,21 @@ suite('Glob', () => { [nativeSep('bar/foo/bar'), '**/foo/bar/**'], // Not supported // [nativeSep('foo/bar/bar'), '{**/bar/bar/**,**/baz/bar/**}'], - [nativeSep('/foo/bar/nope'), null] + [nativeSep('/foo/bar/nope'), null!] ]); const siblings = ['baz', 'baz.zip', 'nope']; - let hasSibling = name => siblings.indexOf(name) !== -1; + let hasSibling = (name: string) => siblings.indexOf(name) !== -1; testOptimizationForPaths({ '**/foo/123/**': { when: '$(basename).zip' }, '**/bar/123/**': true }, ['*/bar/123'], [ - [nativeSep('bar/foo/123'), null], - [nativeSep('bar/foo/123/baz'), null], - [nativeSep('bar/foo/123/nope'), null], + [nativeSep('bar/foo/123'), null!], + [nativeSep('bar/foo/123/baz'), null!], + [nativeSep('bar/foo/123/nope'), null!], [nativeSep('foo/bar/123'), '**/bar/123/**'], ], [ - null, + null!, hasSibling, hasSibling ]); @@ -938,7 +944,7 @@ suite('Glob', () => { const parsed = glob.parse(pattern, { trimForExclusions: true }); assert.deepStrictEqual(glob.getPathTerms(parsed), pathTerms); matches.forEach(([text, result], i) => { - assert.strictEqual(parsed(text, null, siblingsFns[i]), result); + assert.strictEqual(parsed(text, null!, siblingsFns[i]), result); }); } @@ -948,14 +954,14 @@ suite('Glob', () => { test('relative pattern - glob star', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '**/*.cs', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '**/*.cs' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\bar\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.ts'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\Program.cs'); assertNoGlobMatch(p, 'C:\\other\\DNXConsoleApp\\foo\\Program.ts'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '**/*.cs', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '**/*.cs' }; assertGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); assertGlobMatch(p, '/DNXConsoleApp/foo/bar/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.ts'); @@ -966,14 +972,14 @@ suite('Glob', () => { test('relative pattern - single star', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '*.cs', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: '*.cs' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\bar\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.ts'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\Program.cs'); assertNoGlobMatch(p, 'C:\\other\\DNXConsoleApp\\foo\\Program.ts'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '*.cs', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: '*.cs' }; assertGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/bar/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.ts'); @@ -984,11 +990,11 @@ suite('Glob', () => { test('relative pattern - single star with path', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'something/*.cs', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'something/*.cs' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\something\\Program.cs'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'something/*.cs' }; assertGlobMatch(p, '/DNXConsoleApp/foo/something/Program.cs'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } @@ -1000,11 +1006,11 @@ suite('Glob', () => { test('relative pattern - #57475', function () { if (isWindows) { - let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'styles/style.css', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'styles/style.css' }; assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\styles\\style.css'); assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); } else { - let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'styles/style.css', pathToRelative: (from, to) => path.relative(from, to) }; + let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo', pattern: 'styles/style.css' }; assertGlobMatch(p, '/DNXConsoleApp/foo/styles/style.css'); assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } diff --git a/src/vs/base/test/node/id.test.ts b/src/vs/base/test/node/id.test.ts index 319edca07f9..689bb126d6b 100644 --- a/src/vs/base/test/node/id.test.ts +++ b/src/vs/base/test/node/id.test.ts @@ -8,7 +8,8 @@ import { getMachineId } from 'vs/base/node/id'; suite('ID', () => { - test('getMachineId', () => { + test('getMachineId', function () { + this.timeout(20000); return getMachineId().then(id => { assert.ok(id); }); diff --git a/src/vs/platform/credentials/test/node/keytar.test.ts b/src/vs/base/test/node/keytar.test.ts similarity index 95% rename from src/vs/platform/credentials/test/node/keytar.test.ts rename to src/vs/base/test/node/keytar.test.ts index 1d3e782c59c..141e6ab904f 100644 --- a/src/vs/platform/credentials/test/node/keytar.test.ts +++ b/src/vs/base/test/node/keytar.test.ts @@ -26,6 +26,7 @@ suite('Keytar', () => { try { await keytar.deletePassword(name, 'foo'); } finally { + // tslint:disable-next-line: no-unsafe-finally throw err; } } diff --git a/src/vs/base/test/node/pfs.test.ts b/src/vs/base/test/node/pfs.test.ts deleted file mode 100644 index 283f03d66f9..00000000000 --- a/src/vs/base/test/node/pfs.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as os from 'os'; - -import * as path from 'path'; -import * as fs from 'fs'; - -import * as uuid from 'vs/base/common/uuid'; -import * as pfs from 'vs/base/node/pfs'; -import { timeout } from 'vs/base/common/async'; - -suite('PFS', () => { - - test('writeFile', () => { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'pfs', id); - const testFile = path.join(newDir, 'writefile.txt'); - - return pfs.mkdirp(newDir, 493).then(() => { - assert.ok(fs.existsSync(newDir)); - - return pfs.writeFile(testFile, 'Hello World', null).then(() => { - assert.equal(fs.readFileSync(testFile), 'Hello World'); - - return pfs.del(parentDir, os.tmpdir()); - }); - }); - }); - - test('writeFile - parallel write on different files works', function () { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'pfs', id); - const testFile1 = path.join(newDir, 'writefile1.txt'); - const testFile2 = path.join(newDir, 'writefile2.txt'); - const testFile3 = path.join(newDir, 'writefile3.txt'); - const testFile4 = path.join(newDir, 'writefile4.txt'); - const testFile5 = path.join(newDir, 'writefile5.txt'); - - return pfs.mkdirp(newDir, 493).then(() => { - assert.ok(fs.existsSync(newDir)); - - return Promise.all([ - pfs.writeFile(testFile1, 'Hello World 1', null), - pfs.writeFile(testFile2, 'Hello World 2', null), - pfs.writeFile(testFile3, 'Hello World 3', null), - pfs.writeFile(testFile4, 'Hello World 4', null), - pfs.writeFile(testFile5, 'Hello World 5', null) - ]).then(() => { - assert.equal(fs.readFileSync(testFile1), 'Hello World 1'); - assert.equal(fs.readFileSync(testFile2), 'Hello World 2'); - assert.equal(fs.readFileSync(testFile3), 'Hello World 3'); - assert.equal(fs.readFileSync(testFile4), 'Hello World 4'); - assert.equal(fs.readFileSync(testFile5), 'Hello World 5'); - - return pfs.del(parentDir, os.tmpdir()); - }); - }); - }); - - test('writeFile - parallel write on same files works and is sequentalized', function () { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'pfs', id); - const testFile = path.join(newDir, 'writefile.txt'); - - return pfs.mkdirp(newDir, 493).then(() => { - assert.ok(fs.existsSync(newDir)); - - return Promise.all([ - pfs.writeFile(testFile, 'Hello World 1', null), - pfs.writeFile(testFile, 'Hello World 2', null), - timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 3', null)), - pfs.writeFile(testFile, 'Hello World 4', null), - timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 5', null)) - ]).then(() => { - assert.equal(fs.readFileSync(testFile), 'Hello World 5'); - - return pfs.del(parentDir, os.tmpdir()); - }); - }); - }); - - test('rimraf - simple', function () { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - return pfs.mkdirp(newDir, 493).then(() => { - fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); - fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); - - return pfs.rimraf(newDir).then(() => { - assert.ok(!fs.existsSync(newDir)); - }); - }); - }); - - test('rimraf - recursive folder structure', function () { - const id = uuid.generateUuid(); - const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const newDir = path.join(parentDir, 'extfs', id); - - return pfs.mkdirp(newDir, 493).then(() => { - fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); - fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); - - fs.mkdirSync(path.join(newDir, 'somefolder')); - fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents'); - - return pfs.rimraf(newDir).then(() => { - assert.ok(!fs.existsSync(newDir)); - }); - }); - }); -}); diff --git a/src/vs/base/test/node/extfs/fixtures/examples/company.jxs b/src/vs/base/test/node/pfs/fixtures/examples/company.jxs similarity index 82% rename from src/vs/base/test/node/extfs/fixtures/examples/company.jxs rename to src/vs/base/test/node/pfs/fixtures/examples/company.jxs index ca4a62bf248..b65b52ade69 100644 --- a/src/vs/base/test/node/extfs/fixtures/examples/company.jxs +++ b/src/vs/base/test/node/pfs/fixtures/examples/company.jxs @@ -8,8 +8,8 @@ var Workforce; return Company; })(); (function (property, Workforce, IEmployee) { - if (property === void 0) { property = employees; } - if (IEmployee === void 0) { IEmployee = []; } + if (property === undefined) { property = employees; } + if (IEmployee === undefined) { IEmployee = []; } property; calculateMonthlyExpenses(); { diff --git a/src/vs/base/test/node/extfs/fixtures/examples/conway.jxs b/src/vs/base/test/node/pfs/fixtures/examples/conway.jxs similarity index 95% rename from src/vs/base/test/node/extfs/fixtures/examples/conway.jxs rename to src/vs/base/test/node/pfs/fixtures/examples/conway.jxs index 306c906ce6f..248f26be205 100644 --- a/src/vs/base/test/node/extfs/fixtures/examples/conway.jxs +++ b/src/vs/base/test/node/pfs/fixtures/examples/conway.jxs @@ -7,9 +7,9 @@ var Conway; return Cell; })(); (function (property, number, property, number, property, boolean) { - if (property === void 0) { property = row; } - if (property === void 0) { property = col; } - if (property === void 0) { property = live; } + if (property === undefined) { property = row; } + if (property === undefined) { property = col; } + if (property === undefined) { property = live; } }); var GameOfLife = (function () { function GameOfLife() { diff --git a/src/vs/base/test/node/extfs/fixtures/examples/employee.jxs b/src/vs/base/test/node/pfs/fixtures/examples/employee.jxs similarity index 100% rename from src/vs/base/test/node/extfs/fixtures/examples/employee.jxs rename to src/vs/base/test/node/pfs/fixtures/examples/employee.jxs diff --git a/src/vs/base/test/node/extfs/fixtures/examples/small.jxs b/src/vs/base/test/node/pfs/fixtures/examples/small.jxs similarity index 87% rename from src/vs/base/test/node/extfs/fixtures/examples/small.jxs rename to src/vs/base/test/node/pfs/fixtures/examples/small.jxs index 5e57b4c9439..2fb478319a5 100644 --- a/src/vs/base/test/node/extfs/fixtures/examples/small.jxs +++ b/src/vs/base/test/node/pfs/fixtures/examples/small.jxs @@ -7,10 +7,10 @@ var M; return C; })(); (function (x, property, number) { - if (property === void 0) { property = w; } + if (property === undefined) { property = w; } var local = 1; // unresolved symbol because x is local - //self.x++; + //self.x++; self.w--; // ok because w is a property property; f = function (y) { diff --git a/src/vs/base/test/node/extfs/fixtures/index.html b/src/vs/base/test/node/pfs/fixtures/index.html similarity index 100% rename from src/vs/base/test/node/extfs/fixtures/index.html rename to src/vs/base/test/node/pfs/fixtures/index.html diff --git a/src/vs/base/test/node/extfs/fixtures/site.css b/src/vs/base/test/node/pfs/fixtures/site.css similarity index 100% rename from src/vs/base/test/node/extfs/fixtures/site.css rename to src/vs/base/test/node/pfs/fixtures/site.css diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts new file mode 100644 index 00000000000..4f09ad2b56d --- /dev/null +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -0,0 +1,612 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as os from 'os'; +import * as path from 'vs/base/common/path'; +import * as fs from 'fs'; +import { Readable } from 'stream'; +import * as uuid from 'vs/base/common/uuid'; +import * as pfs from 'vs/base/node/pfs'; +import { timeout } from 'vs/base/common/async'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { isWindows, isLinux } from 'vs/base/common/platform'; +import { canNormalize } from 'vs/base/common/normalization'; +import { VSBuffer } from 'vs/base/common/buffer'; + +const chunkSize = 64 * 1024; +const readError = 'Error while reading'; +function toReadable(value: string, throwError?: boolean): Readable { + const totalChunks = Math.ceil(value.length / chunkSize); + const stringChunks: string[] = []; + + for (let i = 0, j = 0; i < totalChunks; ++i, j += chunkSize) { + stringChunks[i] = value.substr(j, chunkSize); + } + + let counter = 0; + return new Readable({ + read: function () { + if (throwError) { + this.emit('error', new Error(readError)); + } + + let res!: string; + let canPush = true; + while (canPush && (res = stringChunks[counter++])) { + canPush = this.push(res); + } + + // EOS + if (!res) { + this.push(null); + } + }, + encoding: 'utf8' + }); +} + +suite('PFS', () => { + + test('writeFile', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'writefile.txt'); + + await pfs.mkdirp(newDir, 493); + assert.ok(fs.existsSync(newDir)); + + await pfs.writeFile(testFile, 'Hello World', (null!)); + assert.equal(fs.readFileSync(testFile), 'Hello World'); + + await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); + + test('writeFile - parallel write on different files works', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile1 = path.join(newDir, 'writefile1.txt'); + const testFile2 = path.join(newDir, 'writefile2.txt'); + const testFile3 = path.join(newDir, 'writefile3.txt'); + const testFile4 = path.join(newDir, 'writefile4.txt'); + const testFile5 = path.join(newDir, 'writefile5.txt'); + + await pfs.mkdirp(newDir, 493); + assert.ok(fs.existsSync(newDir)); + + await Promise.all([ + pfs.writeFile(testFile1, 'Hello World 1', (null!)), + pfs.writeFile(testFile2, 'Hello World 2', (null!)), + pfs.writeFile(testFile3, 'Hello World 3', (null!)), + pfs.writeFile(testFile4, 'Hello World 4', (null!)), + pfs.writeFile(testFile5, 'Hello World 5', (null!)) + ]); + assert.equal(fs.readFileSync(testFile1), 'Hello World 1'); + assert.equal(fs.readFileSync(testFile2), 'Hello World 2'); + assert.equal(fs.readFileSync(testFile3), 'Hello World 3'); + assert.equal(fs.readFileSync(testFile4), 'Hello World 4'); + assert.equal(fs.readFileSync(testFile5), 'Hello World 5'); + + await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); + + test('writeFile - parallel write on same files works and is sequentalized', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'writefile.txt'); + + await pfs.mkdirp(newDir, 493); + assert.ok(fs.existsSync(newDir)); + + await Promise.all([ + pfs.writeFile(testFile, 'Hello World 1', undefined), + pfs.writeFile(testFile, 'Hello World 2', undefined), + timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 3', undefined)), + pfs.writeFile(testFile, 'Hello World 4', undefined), + timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 5', undefined)) + ]); + assert.equal(fs.readFileSync(testFile), 'Hello World 5'); + + await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); + + test('rimraf - simple - unlink', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + await pfs.rimraf(newDir); + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimraf - simple - move', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + await pfs.rimraf(newDir, pfs.RimRafMode.MOVE); + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimraf - recursive folder structure - unlink', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + fs.mkdirSync(path.join(newDir, 'somefolder')); + fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents'); + + await pfs.rimraf(newDir); + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimraf - recursive folder structure - move', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + fs.mkdirSync(path.join(newDir, 'somefolder')); + fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents'); + + await pfs.rimraf(newDir, pfs.RimRafMode.MOVE); + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimraf - simple ends with dot - move', async () => { + const id = `${uuid.generateUuid()}.`; + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + await pfs.rimraf(newDir, pfs.RimRafMode.MOVE); + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimraf - simple ends with dot slash/backslash - move', async () => { + const id = `${uuid.generateUuid()}.`; + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + await pfs.rimraf(`${newDir}${path.sep}`, pfs.RimRafMode.MOVE); + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimrafSync - swallows file not found error', function () { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + pfs.rimrafSync(newDir); + + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimrafSync - simple', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + pfs.rimrafSync(newDir); + + assert.ok(!fs.existsSync(newDir)); + }); + + test('rimrafSync - recursive folder structure', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + fs.mkdirSync(path.join(newDir, 'somefolder')); + fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents'); + + pfs.rimrafSync(newDir); + + assert.ok(!fs.existsSync(newDir)); + }); + + test('moveIgnoreError', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + try { + await pfs.renameIgnoreError(path.join(newDir, 'foo'), path.join(newDir, 'bar')); + return pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + } + catch (error) { + assert.fail(error); + return Promise.reject(error); + } + }); + + test('copy, move and delete', async () => { + const id = uuid.generateUuid(); + const id2 = uuid.generateUuid(); + const sourceDir = getPathFromAmdModule(require, './fixtures'); + const parentDir = path.join(os.tmpdir(), 'vsctests', 'pfs'); + const targetDir = path.join(parentDir, id); + const targetDir2 = path.join(parentDir, id2); + + await pfs.copy(sourceDir, targetDir); + + assert.ok(fs.existsSync(targetDir)); + assert.ok(fs.existsSync(path.join(targetDir, 'index.html'))); + assert.ok(fs.existsSync(path.join(targetDir, 'site.css'))); + assert.ok(fs.existsSync(path.join(targetDir, 'examples'))); + assert.ok(fs.statSync(path.join(targetDir, 'examples')).isDirectory()); + assert.ok(fs.existsSync(path.join(targetDir, 'examples', 'small.jxs'))); + + await pfs.move(targetDir, targetDir2); + + assert.ok(!fs.existsSync(targetDir)); + assert.ok(fs.existsSync(targetDir2)); + assert.ok(fs.existsSync(path.join(targetDir2, 'index.html'))); + assert.ok(fs.existsSync(path.join(targetDir2, 'site.css'))); + assert.ok(fs.existsSync(path.join(targetDir2, 'examples'))); + assert.ok(fs.statSync(path.join(targetDir2, 'examples')).isDirectory()); + assert.ok(fs.existsSync(path.join(targetDir2, 'examples', 'small.jxs'))); + + await pfs.move(path.join(targetDir2, 'index.html'), path.join(targetDir2, 'index_moved.html')); + + assert.ok(!fs.existsSync(path.join(targetDir2, 'index.html'))); + assert.ok(fs.existsSync(path.join(targetDir2, 'index_moved.html'))); + + await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + + assert.ok(!fs.existsSync(parentDir)); + }); + + test('mkdirp', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + + assert.ok(fs.existsSync(newDir)); + + return pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); + + test('mkdirp cancellation', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + const source = new CancellationTokenSource(); + + const mkdirpPromise = pfs.mkdirp(newDir, 493, source.token); + source.cancel(); + + await mkdirpPromise; + + assert.ok(!fs.existsSync(newDir)); + + return pfs.rimraf(parentDir, pfs.RimRafMode.MOVE); + }); + + test('readDirsInDir', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + + await pfs.mkdirp(newDir, 493); + + fs.mkdirSync(path.join(newDir, 'somefolder1')); + fs.mkdirSync(path.join(newDir, 'somefolder2')); + fs.mkdirSync(path.join(newDir, 'somefolder3')); + fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); + fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); + + const result = await pfs.readDirsInDir(newDir); + assert.equal(result.length, 3); + assert.ok(result.indexOf('somefolder1') !== -1); + assert.ok(result.indexOf('somefolder2') !== -1); + assert.ok(result.indexOf('somefolder3') !== -1); + + await pfs.rimraf(newDir); + }); + + test('stat link', async () => { + if (isWindows) { + return Promise.resolve(); // Symlinks are not the same on win, and we can not create them programitically without admin privileges + } + + const id1 = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id1); + const directory = path.join(parentDir, 'pfs', id1); + + const id2 = uuid.generateUuid(); + const symbolicLink = path.join(parentDir, 'pfs', id2); + + await pfs.mkdirp(directory, 493); + + fs.symlinkSync(directory, symbolicLink); + + let statAndIsLink = await pfs.statLink(directory); + assert.ok(!statAndIsLink!.isSymbolicLink); + + statAndIsLink = await pfs.statLink(symbolicLink); + assert.ok(statAndIsLink!.isSymbolicLink); + + pfs.rimrafSync(directory); + }); + + test('readdir', async () => { + if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id, 'öäü'); + + await pfs.mkdirp(newDir, 493); + + assert.ok(fs.existsSync(newDir)); + + const children = await pfs.readdir(path.join(parentDir, 'pfs', id)); + assert.equal(children.some(n => n === 'öäü'), true); // Mac always converts to NFD, so + + await pfs.rimraf(parentDir); + } + }); + + test('writeFile (string)', async () => { + const smallData = 'Hello World'; + const bigData = (new Array(100 * 1024)).join('Large String\n'); + + return testWriteFileAndFlush(smallData, smallData, bigData, bigData); + }); + + test('writeFile (Buffer)', async () => { + const smallData = 'Hello World'; + const bigData = (new Array(100 * 1024)).join('Large String\n'); + + return testWriteFileAndFlush(Buffer.from(smallData), smallData, Buffer.from(bigData), bigData); + }); + + test('writeFile (UInt8Array)', async () => { + const smallData = 'Hello World'; + const bigData = (new Array(100 * 1024)).join('Large String\n'); + + return testWriteFileAndFlush(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData); + }); + + test('writeFile (stream)', async () => { + const smallData = 'Hello World'; + const bigData = (new Array(100 * 1024)).join('Large String\n'); + + return testWriteFileAndFlush(toReadable(smallData), smallData, toReadable(bigData), bigData); + }); + + async function testWriteFileAndFlush( + smallData: string | Buffer | NodeJS.ReadableStream | Uint8Array, + smallDataValue: string, + bigData: string | Buffer | NodeJS.ReadableStream | Uint8Array, + bigDataValue: string + ): Promise { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + assert.ok(fs.existsSync(newDir)); + + await pfs.writeFile(testFile, smallData); + assert.equal(fs.readFileSync(testFile), smallDataValue); + + await pfs.writeFile(testFile, bigData); + assert.equal(fs.readFileSync(testFile), bigDataValue); + + await pfs.rimraf(parentDir); + } + + test('writeFile (file stream)', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + assert.ok(fs.existsSync(newDir)); + + await pfs.writeFile(testFile, fs.createReadStream(sourceFile)); + assert.equal(fs.readFileSync(testFile).toString(), fs.readFileSync(sourceFile).toString()); + + await pfs.rimraf(parentDir); + }); + + test('writeFile (string, error handling)', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + + assert.ok(fs.existsSync(newDir)); + + fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! + + let expectedError: Error | undefined; + try { + await pfs.writeFile(testFile, 'Hello World'); + } catch (error) { + expectedError = error; + } + + assert.ok(expectedError); + + await pfs.rimraf(parentDir); + }); + + test('writeFile (stream, error handling EISDIR)', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + + assert.ok(fs.existsSync(newDir)); + + fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! + + const readable = toReadable('Hello World'); + + let expectedError: Error | undefined; + try { + await pfs.writeFile(testFile, readable); + } catch (error) { + expectedError = error; + } + + if (!expectedError || (expectedError).code !== 'EISDIR') { + return Promise.reject(new Error('Expected EISDIR error for writing to folder but got: ' + (expectedError ? (expectedError).code : 'no error'))); + } + + // verify that the stream is still consumable (for https://github.com/Microsoft/vscode/issues/42542) + assert.equal(readable.read(), 'Hello World'); + + await pfs.rimraf(parentDir); + }); + + test('writeFile (stream, error handling READERROR)', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + assert.ok(fs.existsSync(newDir)); + + let expectedError: Error | undefined; + try { + await pfs.writeFile(testFile, toReadable('Hello World', true /* throw error */)); + } catch (error) { + expectedError = error; + } + + if (!expectedError || expectedError.message !== readError) { + return Promise.reject(new Error('Expected error for writing to folder')); + } + + await pfs.rimraf(parentDir); + }); + + test('writeFile (stream, error handling EACCES)', async () => { + if (isLinux) { + return Promise.resolve(); // somehow this test fails on Linux in our TFS builds + } + + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + + assert.ok(fs.existsSync(newDir)); + + fs.writeFileSync(testFile, ''); + fs.chmodSync(testFile, 33060); // make readonly + + let expectedError: Error | undefined; + try { + await pfs.writeFile(testFile, toReadable('Hello World')); + } catch (error) { + expectedError = error; + } + + if (!expectedError || !((expectedError).code !== 'EACCES' || (expectedError).code !== 'EPERM')) { + return Promise.reject(new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (expectedError ? (expectedError).code : 'no error'))); + } + + await pfs.rimraf(parentDir); + }); + + test('writeFile (file stream, error handling)', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + + assert.ok(fs.existsSync(newDir)); + + fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! + + let expectedError: Error | undefined; + try { + await pfs.writeFile(testFile, fs.createReadStream(sourceFile)); + } catch (error) { + expectedError = error; + } + + if (!expectedError) { + return Promise.reject(new Error('Expected error for writing to folder')); + } + + await pfs.rimraf(parentDir); + }); + + test('writeFileSync', async () => { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'pfs', id); + const testFile = path.join(newDir, 'flushed.txt'); + + await pfs.mkdirp(newDir, 493); + + assert.ok(fs.existsSync(newDir)); + + pfs.writeFileSync(testFile, 'Hello World'); + assert.equal(fs.readFileSync(testFile), 'Hello World'); + + const largeString = (new Array(100 * 1024)).join('Large String\n'); + + pfs.writeFileSync(testFile, largeString); + assert.equal(fs.readFileSync(testFile), largeString); + + await pfs.rimraf(parentDir); + }); +}); diff --git a/src/vs/base/test/node/port.test.ts b/src/vs/base/test/node/port.test.ts index 3ff60f50f95..87d8eca9ebf 100644 --- a/src/vs/base/test/node/port.test.ts +++ b/src/vs/base/test/node/port.test.ts @@ -21,7 +21,7 @@ suite('Ports', () => { // create a server to block this port const server = net.createServer(); - server.listen(initialPort, null, null, () => { + server.listen(initialPort, undefined, undefined, () => { // once listening, find another free port and assert that the port is different from the opened one ports.findFreePort(7000, 50, 300000).then(freePort => { diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 9ebf3517c65..784a797b895 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -3,13 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Storage, SQLiteStorageImpl, IStorageOptions } from 'vs/base/node/storage'; +import { Storage, SQLiteStorageDatabase, IStorageDatabase, ISQLiteStorageDatabaseOptions, IStorageItemsChangeEvent } from 'vs/base/node/storage'; import { generateUuid } from 'vs/base/common/uuid'; -import { join } from 'path'; +import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; import { equal, ok } from 'assert'; -import { mkdirp, del, exists, unlink, writeFile } from 'vs/base/node/pfs'; +import { mkdirp, writeFile, exists, unlink, rimraf, RimRafMode } from 'vs/base/node/pfs'; import { timeout } from 'vs/base/common/async'; +import { Event, Emitter } from 'vs/base/common/event'; +import { isWindows } from 'vs/base/common/platform'; suite('Storage Library', () => { @@ -23,13 +25,13 @@ suite('Storage Library', () => { const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - const storage = new Storage({ path: join(storageDir, 'storage.db') }); + const storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); // Empty fallbacks equal(storage.get('foo', 'bar'), 'bar'); - equal(storage.getInteger('foo', 55), 55); + equal(storage.getNumber('foo', 55), 55); equal(storage.getBoolean('foo', true), true); let changes = new Set(); @@ -43,7 +45,7 @@ suite('Storage Library', () => { const set3Promise = storage.set('barBoolean', true); equal(storage.get('bar'), 'foo'); - equal(storage.getInteger('barNumber'), 55); + equal(storage.getNumber('barNumber'), 55); equal(storage.getBoolean('barBoolean'), true); equal(changes.size, 3); @@ -69,7 +71,7 @@ suite('Storage Library', () => { const delete3Promise = storage.delete('barBoolean'); ok(!storage.get('bar')); - ok(!storage.getInteger('barNumber')); + ok(!storage.getNumber('barNumber')); ok(!storage.getBoolean('barBoolean')); equal(changes.size, 3); @@ -90,14 +92,70 @@ suite('Storage Library', () => { equal(deletePromiseResolved, true); await storage.close(); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('external changes', async () => { + const storageDir = uniqueStorageDir(); + await mkdirp(storageDir); + + class TestSQLiteStorageDatabase extends SQLiteStorageDatabase { + private _onDidChangeItemsExternal = new Emitter(); + get onDidChangeItemsExternal(): Event { return this._onDidChangeItemsExternal.event; } + + fireDidChangeItemsExternal(event: IStorageItemsChangeEvent): void { + this._onDidChangeItemsExternal.fire(event); + } + } + + const database = new TestSQLiteStorageDatabase(join(storageDir, 'storage.db')); + const storage = new Storage(database); + + let changes = new Set(); + storage.onDidChangeStorage(key => { + changes.add(key); + }); + + await storage.init(); + + await storage.set('foo', 'bar'); + ok(changes.has('foo')); + changes.clear(); + + // Nothing happens if changing to same value + const change = new Map(); + change.set('foo', 'bar'); + database.fireDidChangeItemsExternal({ items: change }); + equal(changes.size, 0); + + // Change is accepted if valid + change.set('foo', 'bar1'); + database.fireDidChangeItemsExternal({ items: change }); + ok(changes.has('foo')); + equal(storage.get('foo'), 'bar1'); + changes.clear(); + + // Delete is accepted + change.set('foo', undefined); + database.fireDidChangeItemsExternal({ items: change }); + ok(changes.has('foo')); + equal(storage.get('foo', null!), null); + changes.clear(); + + // Nothing happens if changing to same value + change.set('foo', undefined); + database.fireDidChangeItemsExternal({ items: change }); + equal(changes.size, 0); + + await storage.close(); + await rimraf(storageDir, RimRafMode.MOVE); }); test('close flushes data', async () => { const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - let storage = new Storage({ path: join(storageDir, 'storage.db') }); + let storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); const set1Promise = storage.set('foo', 'bar'); @@ -113,7 +171,7 @@ suite('Storage Library', () => { equal(setPromiseResolved, true); - storage = new Storage({ path: join(storageDir, 'storage.db') }); + storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); equal(storage.get('foo'), 'bar'); @@ -121,7 +179,7 @@ suite('Storage Library', () => { await storage.close(); - storage = new Storage({ path: join(storageDir, 'storage.db') }); + storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); const delete1Promise = storage.delete('foo'); @@ -137,21 +195,21 @@ suite('Storage Library', () => { equal(deletePromiseResolved, true); - storage = new Storage({ path: join(storageDir, 'storage.db') }); + storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); ok(!storage.get('foo')); ok(!storage.get('bar')); await storage.close(); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); }); test('conflicting updates', async () => { const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - let storage = new Storage({ path: join(storageDir, 'storage.db') }); + let storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); let changes = new Set(); @@ -186,7 +244,37 @@ suite('Storage Library', () => { ok(setAndDeletePromiseResolved); await storage.close(); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('corrupt DB recovers', async () => { + const storageDir = uniqueStorageDir(); + await mkdirp(storageDir); + + const storageFile = join(storageDir, 'storage.db'); + + let storage = new Storage(new SQLiteStorageDatabase(storageFile)); + await storage.init(); + + await storage.set('bar', 'foo'); + + await writeFile(storageFile, 'This is a broken DB'); + + await storage.set('foo', 'bar'); + + equal(storage.get('bar'), 'foo'); + equal(storage.get('foo'), 'bar'); + + await storage.close(); + + storage = new Storage(new SQLiteStorageDatabase(storageFile)); + await storage.init(); + + equal(storage.get('bar'), 'foo'); + equal(storage.get('foo'), 'bar'); + + await storage.close(); + await rimraf(storageDir, RimRafMode.MOVE); }); }); @@ -205,15 +293,17 @@ suite('SQLite Storage Library', () => { return set; } - async function testDBBasics(path, logError?: (error) => void) { - const options: IStorageOptions = { path }; + async function testDBBasics(path: string, logError?: (error: Error) => void) { + let options!: ISQLiteStorageDatabaseOptions; if (logError) { - options.logging = { - logError + options = { + logging: { + logError + } }; } - const storage = new SQLiteStorageImpl(options); + const storage = new SQLiteStorageDatabase(path, options); const items = new Map(); items.set('foo', 'bar'); @@ -265,7 +355,14 @@ suite('SQLite Storage Library', () => { storedItems = await storage.getItems(); equal(storedItems.size, 0); - await storage.close(); + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); } test('basics', async () => { @@ -273,9 +370,9 @@ suite('SQLite Storage Library', () => { await mkdirp(storageDir); - testDBBasics(join(storageDir, 'storage.db')); + await testDBBasics(join(storageDir, 'storage.db')); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); }); test('basics (open multiple times)', async () => { @@ -286,35 +383,171 @@ suite('SQLite Storage Library', () => { await testDBBasics(join(storageDir, 'storage.db')); await testDBBasics(join(storageDir, 'storage.db')); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); }); - test('basics (broken DB falls back to empty DB)', async () => { - let expectedError: any; - - const brokenDBPath = join(__dirname, 'broken.db'); - if (await exists(brokenDBPath)) { - await unlink(brokenDBPath); // cleanup previous run - } - - await writeFile(brokenDBPath, 'This is a broken DB'); - - await testDBBasics(brokenDBPath, error => { - expectedError = error; - }); - - ok(expectedError); - }); - - test('real world example', async () => { + test('basics (corrupt DB falls back to empty DB)', async () => { const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - let storage = new SQLiteStorageImpl({ - path: join(storageDir, 'storage.db') + const corruptDBPath = join(storageDir, 'broken.db'); + await writeFile(corruptDBPath, 'This is a broken DB'); + + let expectedError: any; + await testDBBasics(corruptDBPath, error => { + expectedError = error; }); + ok(expectedError); + + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('basics (corrupt DB restores from previous backup)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storagePath = join(storageDir, 'storage.db'); + let storage = new SQLiteStorageDatabase(storagePath); + + const items = new Map(); + items.set('foo', 'bar'); + items.set('some/foo/path', 'some/bar/path'); + items.set(JSON.stringify({ foo: 'bar' }), JSON.stringify({ bar: 'foo' })); + + await storage.updateItems({ insert: items }); + await storage.close(); + + await writeFile(storagePath, 'This is now a broken DB'); + + storage = new SQLiteStorageDatabase(storagePath); + + const storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + equal(storedItems.get('foo'), 'bar'); + equal(storedItems.get('some/foo/path'), 'some/bar/path'); + equal(storedItems.get(JSON.stringify({ foo: 'bar' })), JSON.stringify({ bar: 'foo' })); + + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); + + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('basics (corrupt DB falls back to empty DB if backup is corrupt)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storagePath = join(storageDir, 'storage.db'); + let storage = new SQLiteStorageDatabase(storagePath); + + const items = new Map(); + items.set('foo', 'bar'); + items.set('some/foo/path', 'some/bar/path'); + items.set(JSON.stringify({ foo: 'bar' }), JSON.stringify({ bar: 'foo' })); + + await storage.updateItems({ insert: items }); + await storage.close(); + + await writeFile(storagePath, 'This is now a broken DB'); + await writeFile(`${storagePath}.backup`, 'This is now also a broken DB'); + + storage = new SQLiteStorageDatabase(storagePath); + + const storedItems = await storage.getItems(); + equal(storedItems.size, 0); + + await testDBBasics(storagePath); + + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('basics (DB that becomes corrupt during runtime stores all state from cache on close)', async () => { + if (isWindows) { + await Promise.resolve(); // Windows will fail to write to open DB due to locking + + return; + } + + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storagePath = join(storageDir, 'storage.db'); + let storage = new SQLiteStorageDatabase(storagePath); + + const items = new Map(); + items.set('foo', 'bar'); + items.set('some/foo/path', 'some/bar/path'); + items.set(JSON.stringify({ foo: 'bar' }), JSON.stringify({ bar: 'foo' })); + + await storage.updateItems({ insert: items }); + await storage.close(); + + const backupPath = `${storagePath}.backup`; + equal(await exists(backupPath), true); + + storage = new SQLiteStorageDatabase(storagePath); + await storage.getItems(); + + await writeFile(storagePath, 'This is now a broken DB'); + + // we still need to trigger a check to the DB so that we get to know that + // the DB is corrupt. We have no extra code on shutdown that checks for the + // health of the DB. This is an optimization to not perform too many tasks + // on shutdown. + await storage.checkIntegrity(true).then(null, error => { } /* error is expected here but we do not want to fail */); + + await unlink(backupPath); // also test that the recovery DB is backed up properly + + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return items; + }); + + equal(recoveryCalled, true); + equal(await exists(backupPath), true); + + storage = new SQLiteStorageDatabase(storagePath); + + const storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + equal(storedItems.get('foo'), 'bar'); + equal(storedItems.get('some/foo/path'), 'some/bar/path'); + equal(storedItems.get(JSON.stringify({ foo: 'bar' })), JSON.stringify({ bar: 'foo' })); + + recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); + + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('real world example', async function () { + this.timeout(20000); + + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + let storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); + const items1 = new Map(); items1.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#09885a"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#09885a"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#09885a"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#09885a"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#09885a"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","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","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); items1.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); @@ -322,7 +555,7 @@ suite('SQLite Storage Library', () => { items1.set('debug.actionswidgetposition', '0.6880952380952381'); const items2 = new Map(); - items2.set('workbench.editors.files.textfileeditor', '{"textEditorViewState":[["file:///Users/dummy/Documents/ticino-playground/play.htm",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":6,"column":16},"position":{"lineNumber":6,"column":16}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":0},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}],["file:///Users/dummy/Documents/ticino-playground/nakefile.js",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":7,"column":81},"position":{"lineNumber":7,"column":81}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":20},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}],["file:///Users/dummy/Desktop/vscode2/.gitattributes",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":9,"column":12},"position":{"lineNumber":9,"column":12}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":20},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}],["file:///Users/dummy/Desktop/vscode2/src/vs/workbench/parts/search/browser/openAnythingHandler.ts",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":1,"column":1},"position":{"lineNumber":1,"column":1}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":0},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}]]}'); + items2.set('workbench.editors.files.textfileeditor', '{"textEditorViewState":[["file:///Users/dummy/Documents/ticino-playground/play.htm",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":6,"column":16},"position":{"lineNumber":6,"column":16}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":0},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}],["file:///Users/dummy/Documents/ticino-playground/nakefile.js",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":7,"column":81},"position":{"lineNumber":7,"column":81}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":20},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}],["file:///Users/dummy/Desktop/vscode2/.gitattributes",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":9,"column":12},"position":{"lineNumber":9,"column":12}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":20},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}],["file:///Users/dummy/Desktop/vscode2/src/vs/workbench/contrib/search/browser/openAnythingHandler.ts",{"0":{"cursorState":[{"inSelectionMode":false,"selectionStart":{"lineNumber":1,"column":1},"position":{"lineNumber":1,"column":1}}],"viewState":{"scrollLeft":0,"firstPosition":{"lineNumber":1,"column":1},"firstPositionDeltaTop":0},"contributionsState":{"editor.contrib.folding":{},"editor.contrib.wordHighlighter":false}}}]]}'); const items3 = new Map(); items3.set('nps/iscandidate', 'false'); @@ -387,26 +620,24 @@ suite('SQLite Storage Library', () => { await storage.close(); - storage = new SQLiteStorageImpl({ - path: join(storageDir, 'storage.db') - }); + storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); storedItems = await storage.getItems(); equal(storedItems.size, items1.size + items2.size + items3.size); await storage.close(); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); }); - test('very large item value', async () => { + test('very large item value', async function () { + this.timeout(20000); + const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - let storage = new SQLiteStorageImpl({ - path: join(storageDir, 'storage.db') - }); + let storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); const items = new Map(); items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#09885a"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#09885a"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#09885a"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#09885a"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#09885a"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","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","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); @@ -451,14 +682,20 @@ suite('SQLite Storage Library', () => { await storage.close(); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); }); test('multiple concurrent writes execute in sequence', async () => { const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - const storage = new Storage({ path: join(storageDir, 'storage.db') }); + class TestStorage extends Storage { + getStorage(): IStorageDatabase { + return this.database; + } + } + + const storage = new TestStorage(new SQLiteStorageDatabase(join(storageDir, 'storage.db'))); await storage.init(); @@ -490,7 +727,7 @@ suite('SQLite Storage Library', () => { storage.set('foo3', 'bar'); await storage.set('some/foo3/path', 'some/bar/path'); - const items = await storage.getItems(); + const items = await storage.getStorage().getItems(); equal(items.get('foo'), 'bar'); equal(items.get('some/foo/path'), 'some/bar/path'); equal(items.has('foo1'), false); @@ -502,6 +739,70 @@ suite('SQLite Storage Library', () => { await storage.close(); - await del(storageDir, tmpdir()); + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('lots of INSERT & DELETE (below inline max)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); + + const items = new Map(); + const keys: Set = new Set(); + for (let i = 0; i < 200; i++) { + const uuid = generateUuid(); + const key = `key: ${uuid}`; + + items.set(key, `value: ${uuid}`); + keys.add(key); + } + + await storage.updateItems({ insert: items }); + + let storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + + await storage.updateItems({ delete: keys }); + + storedItems = await storage.getItems(); + equal(storedItems.size, 0); + + await storage.close(); + + await rimraf(storageDir, RimRafMode.MOVE); + }); + + test('lots of INSERT & DELETE (above inline max)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); + + const items = new Map(); + const keys: Set = new Set(); + for (let i = 0; i < 400; i++) { + const uuid = generateUuid(); + const key = `key: ${uuid}`; + + items.set(key, `value: ${uuid}`); + keys.add(key); + } + + await storage.updateItems({ insert: items }); + + let storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + + await storage.updateItems({ delete: keys }); + + storedItems = await storage.getItems(); + equal(storedItems.size, 0); + + await storage.close(); + + await rimraf(storageDir, RimRafMode.MOVE); }); }); diff --git a/src/vs/base/test/node/stream/stream.test.ts b/src/vs/base/test/node/stream/stream.test.ts index 43f1b3e05d1..f813b0a4893 100644 --- a/src/vs/base/test/node/stream/stream.test.ts +++ b/src/vs/base/test/node/stream/stream.test.ts @@ -9,23 +9,6 @@ import * as stream from 'vs/base/node/stream'; import { getPathFromAmdModule } from 'vs/base/common/amd'; suite('Stream', () => { - test('readExactlyByFile - ANSI', function () { - const file = getPathFromAmdModule(require, './fixtures/file.css'); - - return stream.readExactlyByFile(file, 10).then(({ buffer, bytesRead }) => { - assert.equal(bytesRead, 10); - assert.equal(buffer.toString(), '/*--------'); - }); - }); - - test('readExactlyByFile - empty', function () { - const file = getPathFromAmdModule(require, './fixtures/empty.txt'); - - return stream.readExactlyByFile(file, 10).then(({ bytesRead }) => { - assert.equal(bytesRead, 0); - }); - }); - test('readToMatchingString - ANSI', function () { const file = getPathFromAmdModule(require, './fixtures/file.css'); diff --git a/src/vs/base/test/node/testUtils.ts b/src/vs/base/test/node/testUtils.ts new file mode 100644 index 00000000000..452e8ae0768 --- /dev/null +++ b/src/vs/base/test/node/testUtils.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { join } from 'vs/base/common/path'; +import { generateUuid } from 'vs/base/common/uuid'; + +export function getRandomTestPath(tmpdir: string, ...segments: string[]): string { + return join(tmpdir, ...segments, generateUuid()); +} diff --git a/src/vs/base/test/node/utils.ts b/src/vs/base/test/node/utils.ts index 2ee7de39d71..58e77924fe6 100644 --- a/src/vs/base/test/node/utils.ts +++ b/src/vs/base/test/node/utils.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { generateUuid } from 'vs/base/common/uuid'; -import { join } from 'path'; +import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; -import { mkdirp, del } from 'vs/base/node/pfs'; +import { mkdirp, rimraf, RimRafMode } from 'vs/base/node/pfs'; export interface ITestFileResult { testFile: string; - cleanUp: () => Thenable; + cleanUp: () => Promise; } -export function testFile(folder: string, file: string): Thenable { +export function testFile(folder: string, file: string): Promise { const id = generateUuid(); const parentDir = join(tmpdir(), 'vsctests', id); const newDir = join(parentDir, 'config', id); @@ -22,7 +22,7 @@ export function testFile(folder: string, file: string): Thenable { return { testFile, - cleanUp: () => del(parentDir, tmpdir()) - } as ITestFileResult; + cleanUp: () => rimraf(parentDir, RimRafMode.MOVE) + }; }); } diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index 30ad9bf632f..eccaa81f139 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -6,7 +6,7 @@ import { globals } from 'vs/base/common/platform'; import { IWorker, IWorkerCallback, IWorkerFactory, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; -function getWorker(workerId: string, label: string): Worker { +function getWorker(workerId: string, label: string): Worker | Promise { // Option for hosts to overwrite the worker script (used in the standalone editor) if (globals.MonacoEnvironment) { if (typeof globals.MonacoEnvironment.getWorker === 'function') { @@ -18,12 +18,34 @@ function getWorker(workerId: string, label: string): Worker { } // ESM-comment-begin if (typeof require === 'function') { - return new Worker(require.toUrl('./' + workerId) + '#' + label); + // check if the JS lives on a different origin + + const workerMain = require.toUrl('./' + workerId); + if (/^(http:)|(https:)|(file:)/.test(workerMain)) { + const currentUrl = String(window.location); + const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length); + if (workerMain.substring(0, currentOrigin.length) !== currentOrigin) { + // this is the cross-origin case + // i.e. the webpage is running at a different origin than where the scripts are loaded from + const workerBaseUrl = workerMain.substr(0, workerMain.length - 'vs/base/worker/workerMain.js'.length); + const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${workerMain}');/*${label}*/`; + const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; + return new Worker(url); + } + } + return new Worker(workerMain + '#' + label); } // ESM-comment-end throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`); } +function isPromiseLike(obj: any): obj is PromiseLike { + if (typeof obj.then === 'function') { + return true; + } + return false; +} + /** * A worker that uses HTML5 web workers so that is has * its own global scope and its own thread. @@ -31,18 +53,26 @@ function getWorker(workerId: string, label: string): Worker { class WebWorker implements IWorker { private id: number; - private worker: Worker | null; + private worker: Promise | null; constructor(moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) { this.id = id; - this.worker = getWorker('workerMain.js', label); - this.postMessage(moduleId); - this.worker.onmessage = function (ev: any) { - onMessageCallback(ev.data); - }; - if (typeof this.worker.addEventListener === 'function') { - this.worker.addEventListener('error', onErrorCallback); + const workerOrPromise = getWorker('workerMain.js', label); + if (isPromiseLike(workerOrPromise)) { + this.worker = workerOrPromise; + } else { + this.worker = Promise.resolve(workerOrPromise); } + this.postMessage(moduleId); + this.worker.then((w) => { + w.onmessage = function (ev: any) { + onMessageCallback(ev.data); + }; + (w).onmessageerror = onErrorCallback; + if (typeof w.addEventListener === 'function') { + w.addEventListener('error', onErrorCallback); + } + }); } public getId(): number { @@ -51,13 +81,13 @@ class WebWorker implements IWorker { public postMessage(msg: string): void { if (this.worker) { - this.worker.postMessage(msg); + this.worker.then(w => w.postMessage(msg)); } } public dispose(): void { if (this.worker) { - this.worker.terminate(); + this.worker.then(w => w.terminate()); } this.worker = null; } diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html new file mode 100644 index 00000000000..832b6033bf7 --- /dev/null +++ b/src/vs/code/browser/workbench/workbench.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js new file mode 100644 index 00000000000..76e66c5c8d2 --- /dev/null +++ b/src/vs/code/browser/workbench/workbench.js @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +(function () { + + function loadScript(path, callback) { + let script = document.createElement('script'); + script.onload = callback; + script.async = true; + script.type = 'text/javascript'; + script.src = path; + document.head.appendChild(script); + } + + loadScript('../../../../../out/vs/loader.js', function () { + + // @ts-ignore + require.config({ + baseUrl: `${window.location.origin}/out` + }); + + // @ts-ignore + require([ + 'vs/workbench/workbench.web.main', + 'vs/nls!vs/workbench/workbench.web.main', + 'vs/css!vs/workbench/workbench.web.main' + ], + // @ts-ignore + function () { + + // @ts-ignore + require('vs/workbench/browser/web.main').main().then(undefined, console.error); + }); + }); +})(); \ No newline at end of file diff --git a/src/vs/code/buildfile.js b/src/vs/code/buildfile.js index 1a39a0a0c2c..221bea21443 100644 --- a/src/vs/code/buildfile.js +++ b/src/vs/code/buildfile.js @@ -5,8 +5,9 @@ 'use strict'; function createModuleDescription(name, exclude) { - var result = {}; - var excludes = ['vs/css', 'vs/nls']; + const result = {}; + + let excludes = ['vs/css', 'vs/nls']; result.name = name; if (Array.isArray(exclude) && exclude.length > 0) { excludes = excludes.concat(exclude); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 5cb0d8887f3..90767e1a856 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -10,14 +10,13 @@ import { $ } from 'vs/base/browser/dom'; import * as collections from 'vs/base/common/collections'; import * as browser from 'vs/base/browser/browser'; import { escape } from 'vs/base/common/strings'; -import product from 'vs/platform/node/product'; -import pkg from 'vs/platform/node/package'; +import product from 'vs/platform/product/node/product'; +import pkg from 'vs/platform/product/node/package'; import * as os from 'os'; 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/node/ipc'; +import { getDelayedChannel } from 'vs/base/parts/ipc/common/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'; @@ -28,9 +27,10 @@ import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; +import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService'; +import { MainProcessService, IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; +import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel'; import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; @@ -39,6 +39,8 @@ import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; import { Button } from 'vs/base/browser/ui/button/button'; +import { withUndefinedAsNull } from 'vs/base/common/types'; +import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; const MAX_URL_LENGTH = platform.isWindows ? 2081 : 5400; @@ -58,37 +60,45 @@ export function startup(configuration: IssueReporterConfiguration) { const issueReporter = new IssueReporter(configuration); issueReporter.render(); document.body.style.display = 'block'; + issueReporter.setInitialFocus(); } export class IssueReporter extends Disposable { private environmentService: IEnvironmentService; private telemetryService: ITelemetryService; private logService: ILogService; - private issueReporterModel: IssueReporterModel; + private readonly issueReporterModel: IssueReporterModel; private numberOfSearchResultsDisplayed = 0; private receivedSystemInfo = false; private receivedPerformanceInfo = false; private shouldQueueSearch = false; + private hasBeenSubmitted = false; - private previewButton: Button; + private readonly previewButton: Button; constructor(configuration: IssueReporterConfiguration) { super(); this.initServices(configuration); + const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION; this.issueReporterModel = new IssueReporterModel({ issueType: configuration.data.issueType || IssueType.Bug, versionInfo: { vscodeVersion: `${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`, - os: `${os.type()} ${os.arch()} ${os.release()}` + os: `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}` }, extensionsDisabled: !!this.environmentService.disableExtensions, + fileOnExtension: configuration.data.extensionId ? true : undefined, + selectedExtension: configuration.data.extensionId ? configuration.data.enabledExtensions.filter(extension => extension.id === configuration.data.extensionId)[0] : undefined }); - this.previewButton = new Button(document.getElementById('issue-reporter')); + const issueReporterElement = this.getElementById('issue-reporter'); + if (issueReporterElement) { + this.previewButton = new Button(issueReporterElement); + } - ipcRenderer.on('vscode:issuePerformanceInfoResponse', (event, info) => { + ipcRenderer.on('vscode:issuePerformanceInfoResponse', (_: unknown, info: Partial) => { this.logService.trace('issueReporter: Received performance data'); this.issueReporterModel.update(info); this.receivedPerformanceInfo = true; @@ -99,7 +109,7 @@ export class IssueReporter extends Disposable { this.updatePreviewButtonState(); }); - ipcRenderer.on('vscode:issueSystemInfoResponse', (event, info) => { + ipcRenderer.on('vscode:issueSystemInfoResponse', (_: unknown, info: SystemInfo) => { this.logService.trace('issueReporter: Received system data'); this.issueReporterModel.update({ systemInfo: info }); this.receivedSystemInfo = true; @@ -115,7 +125,7 @@ export class IssueReporter extends Disposable { this.logService.trace('issueReporter: Sent data requests'); if (window.document.documentElement.lang !== 'en') { - show(document.getElementById('english')); + show(this.getElementById('english')); } this.setUpTypes(); @@ -133,6 +143,21 @@ export class IssueReporter extends Disposable { this.renderBlocks(); } + setInitialFocus() { + const { fileOnExtension } = this.issueReporterModel.getData(); + if (fileOnExtension) { + const issueTitle = document.getElementById('issue-title'); + if (issueTitle) { + issueTitle.focus(); + } + } else { + const issueType = document.getElementById('issue-type'); + if (issueType) { + issueType.focus(); + } + } + } + private applyZoom(zoomLevel: number) { webFrame.setZoomLevel(zoomLevel); browser.setZoomFactor(webFrame.getZoomFactor()); @@ -207,7 +232,7 @@ export class IssueReporter extends Disposable { styleTag.innerHTML = content.join('\n'); document.head.appendChild(styleTag); - document.body.style.color = styles.color; + document.body.style.color = withUndefinedAsNull(styles.color); } private handleExtensionData(extensions: IssueReporterExtensionData[]) { @@ -220,7 +245,7 @@ export class IssueReporter extends Disposable { this.updateExtensionTable(nonThemes, numberOfThemeExtesions); if (this.environmentService.disableExtensions || extensions.length === 0) { - (document.getElementById('disableExtensions')).disabled = true; + (this.getElementById('disableExtensions')).disabled = true; } this.updateExtensionSelector(extensions); @@ -238,44 +263,45 @@ export class IssueReporter extends Disposable { private updateSettingsSearchDetails(data: ISettingsSearchIssueReporterData): void { const target = document.querySelector('.block-settingsSearchResults .block-info'); - - const details = ` + if (target) { + const details = `
Query: "${data.query}"
Literal match count: ${data.filterResultCount}
- `; + `; - let table = ` - - Setting - Extension - Score - `; + let table = ` + + Setting + Extension + Score + `; - data.actualSearchResults - .forEach(setting => { - table += ` - - ${setting.key} - ${setting.extensionId} - ${String(setting.score).slice(0, 5)} - `; - }); + data.actualSearchResults + .forEach(setting => { + table += ` + + ${setting.key} + ${setting.extensionId} + ${String(setting.score).slice(0, 5)} + `; + }); - target.innerHTML = `${details}${table}
`; + target.innerHTML = `${details}${table}
`; + } } private initServices(configuration: IWindowConfiguration): void { const serviceCollection = new ServiceCollection(); - const mainProcessClient = new ElectronIPCClient(String(`window${configuration.windowId}`)); + const mainProcessService = new MainProcessService(configuration.windowId); + serviceCollection.set(IMainProcessService, mainProcessService); - const windowsChannel = mainProcessClient.getChannel('windows'); - serviceCollection.set(IWindowsService, new WindowsChannelClient(windowsChannel)); + serviceCollection.set(IWindowsService, new WindowsService(mainProcessService)); this.environmentService = new EnvironmentService(configuration, configuration.execPath); const logService = createSpdLogService(`issuereporter${configuration.windowId}`, getLogLevel(this.environmentService), this.environmentService.logsPath); - const logLevelClient = new LogLevelSetterChannelClient(mainProcessClient.getChannel('loglevel')); + const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); this.logService = new FollowerLogService(logLevelClient, logService); const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() @@ -285,7 +311,7 @@ export class IssueReporter extends Disposable { if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)); - const commonProperties = resolveCommonProperties(product.commit, pkg.version, configuration.machineId, this.environmentService.installSourcePath); + const commonProperties = resolveCommonProperties(product.commit || 'Commit unknown', pkg.version, configuration.machineId, this.environmentService.installSourcePath); const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; @@ -306,6 +332,7 @@ export class IssueReporter extends Disposable { ipcRenderer.send('vscode:issuePerformanceInfoRequest'); } this.updatePreviewButtonState(); + this.setSourceOptions(); this.render(); }); @@ -319,27 +346,41 @@ export class IssueReporter extends Disposable { const showInfoElements = document.getElementsByClassName('showInfo'); for (let i = 0; i < showInfoElements.length; i++) { const showInfo = showInfoElements.item(i); - showInfo.addEventListener('click', (e) => { + showInfo!.addEventListener('click', (e) => { e.preventDefault(); const label = (e.target); - const containingElement = label.parentElement.parentElement; - const info = containingElement.lastElementChild; - if (info.classList.contains('hidden')) { - show(info); - label.textContent = localize('hide', "hide"); - } else { - hide(info); - label.textContent = localize('show', "show"); + if (label) { + const containingElement = label.parentElement && label.parentElement.parentElement; + const info = containingElement && containingElement.lastElementChild; + if (info && info.classList.contains('hidden')) { + show(info); + label.textContent = localize('hide', "hide"); + } else { + hide(info); + label.textContent = localize('show', "show"); + } } }); } - this.addEventListener('issue-source', 'change', (event: Event) => { - const fileOnExtension = JSON.parse((event.target).value); - this.issueReporterModel.update({ fileOnExtension: fileOnExtension, includeExtensions: !fileOnExtension }); + this.addEventListener('issue-source', 'change', (e: Event) => { + const value = (e.target).value; + const problemSourceHelpText = this.getElementById('problem-source-help-text')!; + if (value === '') { + this.issueReporterModel.update({ fileOnExtension: undefined }); + show(problemSourceHelpText); + this.clearSearchResults(); + this.render(); + return; + } else { + hide(problemSourceHelpText); + } + + const fileOnExtension = JSON.parse(value); + this.issueReporterModel.update({ fileOnExtension: fileOnExtension }); this.render(); - const title = (document.getElementById('issue-title')).value; + const title = (this.getElementById('issue-title')).value; if (fileOnExtension) { this.searchExtensionIssues(title); } else { @@ -348,27 +389,32 @@ export class IssueReporter extends Disposable { } }); - this.addEventListener('description', 'input', (event: Event) => { - const issueDescription = (event.target).value; + this.addEventListener('description', 'input', (e: Event) => { + const issueDescription = (e.target).value; this.issueReporterModel.update({ issueDescription }); // Only search for extension issues on title change - if (!this.issueReporterModel.fileOnExtension()) { - const title = (document.getElementById('issue-title')).value; + if (this.issueReporterModel.fileOnExtension() === false) { + const title = (this.getElementById('issue-title')).value; this.searchVSCodeIssues(title, issueDescription); } }); - this.addEventListener('issue-title', 'input', (e) => { - const title = (event.target).value; - const lengthValidationMessage = document.getElementById('issue-title-length-validation-error'); + this.addEventListener('issue-title', 'input', (e: Event) => { + const title = (e.target).value; + const lengthValidationMessage = this.getElementById('issue-title-length-validation-error'); if (title && this.getIssueUrlWithTitle(title).length > MAX_URL_LENGTH) { show(lengthValidationMessage); } else { hide(lengthValidationMessage); } - if (this.issueReporterModel.fileOnExtension()) { + const fileOnExtension = this.issueReporterModel.fileOnExtension(); + if (fileOnExtension === undefined) { + return; + } + + if (fileOnExtension) { this.searchExtensionIssues(title); } else { const description = this.issueReporterModel.getData().issueDescription; @@ -378,15 +424,19 @@ export class IssueReporter extends Disposable { this.previewButton.onDidClick(() => this.createIssue()); + function sendWorkbenchCommand(commandId: string) { + ipcRenderer.send('vscode:workbenchCommand', { id: commandId, from: 'issueReporter' }); + } + this.addEventListener('disableExtensions', 'click', () => { - ipcRenderer.send('vscode:workbenchCommand', 'workbench.action.reloadWindowWithExtensionsDisabled'); + sendWorkbenchCommand('workbench.action.reloadWindowWithExtensionsDisabled'); }); this.addEventListener('disableExtensions', 'keydown', (e: KeyboardEvent) => { e.stopPropagation(); if (e.keyCode === 13 || e.keyCode === 32) { - ipcRenderer.send('vscode:workbenchCommand', 'workbench.extensions.action.disableAll'); - ipcRenderer.send('vscode:workbenchCommand', 'workbench.action.reloadWindow'); + sendWorkbenchCommand('workbench.extensions.action.disableAll'); + sendWorkbenchCommand('workbench.action.reloadWindow'); } }); @@ -399,6 +449,20 @@ export class IssueReporter extends Disposable { } } + // Cmd/Ctrl + w closes issue window + if (cmdOrCtrlKey && e.keyCode === 87) { + e.stopPropagation(); + e.preventDefault(); + + const issueTitle = (this.getElementById('issue-title'))!.value; + const { issueDescription } = this.issueReporterModel.getData(); + if (!this.hasBeenSubmitted && (issueTitle || issueDescription)) { + ipcRenderer.send('vscode:issueReporterConfirmClose'); + } else { + ipcRenderer.send('vscode:closeIssueReporter'); + } + } + // Cmd/Ctrl + zooms in if (cmdOrCtrlKey && e.keyCode === 187) { this.applyZoom(webFrame.getZoomLevel() + 1); @@ -452,17 +516,17 @@ export class IssueReporter extends Disposable { return false; } - private getExtensionRepositoryUrl(): string { + private getExtensionRepositoryUrl(): string | undefined { const selectedExtension = this.issueReporterModel.getData().selectedExtension; return selectedExtension && selectedExtension.repositoryUrl; } - private getExtensionBugsUrl(): string { + private getExtensionBugsUrl(): string | undefined { const selectedExtension = this.issueReporterModel.getData().selectedExtension; return selectedExtension && selectedExtension.bugsUrl; } - private searchVSCodeIssues(title: string, issueDescription: string): void { + private searchVSCodeIssues(title: string, issueDescription?: string): void { if (title) { this.searchDuplicates(title, issueDescription); } else { @@ -491,7 +555,7 @@ export class IssueReporter extends Disposable { } private clearSearchResults(): void { - const similarIssues = document.getElementById('similar-issues'); + const similarIssues = this.getElementById('similar-issues')!; similarIssues.innerHTML = ''; this.numberOfSearchResultsDisplayed = 0; } @@ -499,7 +563,7 @@ export class IssueReporter extends Disposable { @debounce(300) private searchGitHub(repo: string, title: string): void { const query = `is:issue+repo:${repo}+${title}`; - const similarIssues = document.getElementById('similar-issues'); + const similarIssues = this.getElementById('similar-issues')!; window.fetch(`https://api.github.com/search/issues?q=${query}`).then((response) => { response.json().then(result => { @@ -513,7 +577,7 @@ export class IssueReporter extends Disposable { similarIssues.appendChild(message); const resetTime = response.headers.get('X-RateLimit-Reset'); - const timeToWait = parseInt(resetTime) - Math.floor(Date.now() / 1000); + const timeToWait = resetTime ? parseInt(resetTime) - Math.floor(Date.now() / 1000) : 1; if (this.shouldQueueSearch) { this.shouldQueueSearch = false; setTimeout(() => { @@ -531,7 +595,7 @@ export class IssueReporter extends Disposable { } @debounce(300) - private searchDuplicates(title: string, body: string): void { + private searchDuplicates(title: string, body?: string): void { const url = 'https://vscode-probot.westus.cloudapp.azure.com:7890/duplicate_candidates'; const init = { method: 'POST', @@ -562,7 +626,7 @@ export class IssueReporter extends Disposable { } private displaySearchResults(results: SearchResult[]) { - const similarIssues = document.getElementById('similar-issues'); + const similarIssues = this.getElementById('similar-issues')!; if (results.length) { const issues = $('div.issues-container'); const issuesText = $('div.list-title'); @@ -578,6 +642,7 @@ export class IssueReporter extends Disposable { link.addEventListener('auxclick', (e) => this.openLink(e)); let issueState: HTMLElement; + let item: HTMLElement; if (issue.state) { issueState = $('span.issue-state'); @@ -591,9 +656,12 @@ export class IssueReporter extends Disposable { issueState.title = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); issueState.appendChild(issueIcon); issueState.appendChild(issueStateLabel); + + item = $('div.issue', {}, issueState, link); + } else { + item = $('div.issue', {}, link); } - const item = $('div.issue', {}, issueState, link); issues.appendChild(item); } @@ -619,7 +687,7 @@ export class IssueReporter extends Disposable { private setUpTypes(): void { const makeOption = (issueType: IssueType, description: string) => ``; - const typeSelect = (document.getElementById('issue-type')); + const typeSelect = this.getElementById('issue-type')! as HTMLSelectElement; const { issueType } = this.issueReporterModel.getData(); if (issueType === IssueType.SettingsSearchIssue) { typeSelect.innerHTML = makeOption(IssueType.SettingsSearchIssue, localize('settingsSearchIssue', "Settings Search Issue")); @@ -633,12 +701,55 @@ export class IssueReporter extends Disposable { } typeSelect.value = issueType.toString(); + + this.setSourceOptions(); + } + + private makeOption(value: string, description: string, disabled: boolean): HTMLOptionElement { + const option: HTMLOptionElement = document.createElement('option'); + option.disabled = disabled; + option.value = value; + option.textContent = description; + + return option; + } + + private setSourceOptions(): void { + const sourceSelect = this.getElementById('issue-source')! as HTMLSelectElement; + const { issueType, fileOnExtension } = this.issueReporterModel.getData(); + let selected = sourceSelect.selectedIndex; + if (selected === -1 && fileOnExtension !== undefined) { + selected = fileOnExtension ? 2 : 1; + } + + sourceSelect.innerHTML = ''; + if (issueType === IssueType.FeatureRequest) { + sourceSelect.append(...[ + this.makeOption('', localize('selectSource', "Select source"), true), + this.makeOption('false', localize('vscode', "Visual Studio Code"), false), + this.makeOption('true', localize('extension', "An extension"), false) + ]); + } else { + sourceSelect.append(...[ + this.makeOption('', localize('selectSource', "Select source"), true), + this.makeOption('false', localize('vscode', "Visual Studio Code"), false), + this.makeOption('true', localize('extension', "An extension"), false), + this.makeOption('', localize('unknown', "Don't Know"), false) + ]); + } + + if (selected !== -1 && selected < sourceSelect.options.length) { + sourceSelect.selectedIndex = selected; + } else { + sourceSelect.selectedIndex = 0; + hide(this.getElementById('problem-source-help-text')); + } } private renderBlocks(): void { // Depending on Issue Type, we render different blocks and text const { issueType, fileOnExtension } = this.issueReporterModel.getData(); - const blockContainer = document.getElementById('block-container'); + const blockContainer = this.getElementById('block-container'); const systemBlock = document.querySelector('.block-system'); const processBlock = document.querySelector('.block-process'); const workspaceBlock = document.querySelector('.block-workspace'); @@ -646,11 +757,10 @@ export class IssueReporter extends Disposable { const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions'); const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults'); - const problemSource = document.getElementById('problem-source'); - const problemSourceHelpText = document.getElementById('problem-source-help-text'); - const descriptionTitle = document.getElementById('issue-description-label'); - const descriptionSubtitle = document.getElementById('issue-description-subtitle'); - const extensionSelector = document.getElementById('extension-selection'); + const problemSource = this.getElementById('problem-source')!; + const descriptionTitle = this.getElementById('issue-description-label')!; + const descriptionSubtitle = this.getElementById('issue-description-subtitle')!; + const extensionSelector = this.getElementById('extension-selection')!; // Hide all by default hide(blockContainer); @@ -661,7 +771,6 @@ export class IssueReporter extends Disposable { hide(searchedExtensionsBlock); hide(settingsSearchResultsBlock); hide(problemSource); - hide(problemSourceHelpText); hide(extensionSelector); if (issueType === IssueType.Bug) { @@ -673,7 +782,6 @@ export class IssueReporter extends Disposable { show(extensionSelector); } else { show(extensionsBlock); - show(problemSourceHelpText); } descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} *`; @@ -689,7 +797,6 @@ export class IssueReporter extends Disposable { show(extensionSelector); } else { show(extensionsBlock); - show(problemSourceHelpText); } descriptionTitle.innerHTML = `${localize('stepsToReproduce', "Steps to Reproduce")} *`; @@ -713,7 +820,7 @@ export class IssueReporter extends Disposable { } private validateInput(inputId: string): boolean { - const inputElement = (document.getElementById(inputId)); + const inputElement = (this.getElementById(inputId)); if (!inputElement.value) { inputElement.classList.add('invalid-input'); return false; @@ -745,16 +852,20 @@ export class IssueReporter extends Disposable { (invalidInput[0]).focus(); } - document.getElementById('issue-title').addEventListener('input', (event) => { + this.addEventListener('issue-title', 'input', _ => { this.validateInput('issue-title'); }); - document.getElementById('description').addEventListener('input', (event) => { + this.addEventListener('description', 'input', _ => { this.validateInput('description'); }); + this.addEventListener('issue-source', 'change', _ => { + this.validateInput('issue-source'); + }); + if (this.issueReporterModel.fileOnExtension()) { - document.getElementById('extension-selector').addEventListener('change', (event) => { + this.addEventListener('extension-selector', 'change', _ => { this.validateInput('extension-selector'); }); } @@ -769,8 +880,9 @@ export class IssueReporter extends Disposable { } */ this.telemetryService.publicLog('issueReporterSubmit', { issueType: this.issueReporterModel.getData().issueType, numSimilarIssuesDisplayed: this.numberOfSearchResultsDisplayed }); + this.hasBeenSubmitted = true; - const baseUrl = this.getIssueUrlWithTitle((document.getElementById('issue-title')).value); + const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value); const issueBody = this.issueReporterModel.serialize(); let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`; @@ -810,21 +922,44 @@ export class IssueReporter extends Disposable { return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`; } - private updateSystemInfo = (state) => { + private updateSystemInfo(state: IssueReporterModelData) { const target = document.querySelector('.block-system .block-info'); - let tableHtml = ''; - Object.keys(state.systemInfo).forEach(k => { - const data = typeof state.systemInfo[k] === 'object' - ? Object.keys(state.systemInfo[k]).map(key => `${key}: ${state.systemInfo[k][key]}`).join('
') - : state.systemInfo[k]; + if (target) { + const systemInfo = state.systemInfo!; + let renderedData = ` + + + + + + + + +
CPUs${systemInfo.cpus}
GPU Status${Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('
')}
Load (avg)${systemInfo.load}
Memory (System)${systemInfo.memory}
Process Argv${systemInfo.processArgs}
Screen Reader${systemInfo.screenReader}
VM${systemInfo.vmHint}
`; - tableHtml += ` - - ${k} - ${data} - `; - }); - target.innerHTML = `${tableHtml}
`; + systemInfo.remoteData.forEach(remote => { + if (isRemoteDiagnosticError(remote)) { + renderedData += ` +
+ + + +
Remote${remote.hostName}
${remote.errorMessage}
`; + } else { + renderedData += ` +
+ + + + + + +
Remote${remote.hostName}
OS${remote.machineInfo.os}
CPUs${remote.machineInfo.cpus}
Memory (System)${remote.machineInfo.memory}
VM${remote.machineInfo.vmHint}
`; + } + }); + + target.innerHTML = renderedData; + } } private updateExtensionSelector(extensions: IssueReporterExtensionData[]): void { @@ -855,65 +990,76 @@ export class IssueReporter extends Disposable { return 0; }); - const makeOption = (extension: IOption) => ``; - const extensionsSelector = document.getElementById('extension-selector'); - extensionsSelector.innerHTML = '' + extensionOptions.map(makeOption).join('\n'); + const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData) => { + const selected = selectedExtension && extension.id === selectedExtension.id; + return ``; + }; - this.addEventListener('extension-selector', 'change', (e: Event) => { - const selectedExtensionId = (e.target).value; - const extensions = this.issueReporterModel.getData().allExtensions; - const matches = extensions.filter(extension => extension.id === selectedExtensionId); - if (matches.length) { - this.issueReporterModel.update({ selectedExtension: matches[0] }); + const extensionsSelector = this.getElementById('extension-selector'); + if (extensionsSelector) { + const { selectedExtension } = this.issueReporterModel.getData(); + extensionsSelector.innerHTML = '' + extensionOptions.map(extension => makeOption(extension, selectedExtension)).join('\n'); - const title = (document.getElementById('issue-title')).value; - this.searchExtensionIssues(title); - } else { - this.issueReporterModel.update({ selectedExtension: null }); - this.clearSearchResults(); - } - }); + this.addEventListener('extension-selector', 'change', (e: Event) => { + const selectedExtensionId = (e.target).value; + const extensions = this.issueReporterModel.getData().allExtensions; + const matches = extensions.filter(extension => extension.id === selectedExtensionId); + if (matches.length) { + this.issueReporterModel.update({ selectedExtension: matches[0] }); + + const title = (this.getElementById('issue-title')).value; + this.searchExtensionIssues(title); + } else { + this.issueReporterModel.update({ selectedExtension: undefined }); + this.clearSearchResults(); + } + }); + } } - private updateProcessInfo = (state) => { + private updateProcessInfo(state: IssueReporterModelData) { const target = document.querySelector('.block-process .block-info'); - target.innerHTML = `${state.processInfo}`; + if (target) { + target.innerHTML = `${state.processInfo}`; + } } - private updateWorkspaceInfo = (state) => { - document.querySelector('.block-workspace .block-info code').textContent = '\n' + state.workspaceInfo; + private updateWorkspaceInfo(state: IssueReporterModelData) { + document.querySelector('.block-workspace .block-info code')!.textContent = '\n' + state.workspaceInfo; } private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { const target = document.querySelector('.block-extensions .block-info'); + if (target) { + if (this.environmentService.disableExtensions) { + target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); + return; + } - if (this.environmentService.disableExtensions) { - target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); - return; + const themeExclusionStr = numThemeExtensions ? `\n(${numThemeExtensions} theme extensions excluded)` : ''; + extensions = extensions || []; + + if (!extensions.length) { + target.innerHTML = 'Extensions: none' + themeExclusionStr; + return; + } + + const table = this.getExtensionTableHtml(extensions); + target.innerHTML = `${table}
${themeExclusionStr}`; } - - const themeExclusionStr = numThemeExtensions ? `\n(${numThemeExtensions} theme extensions excluded)` : ''; - extensions = extensions || []; - - if (!extensions.length) { - target.innerHTML = 'Extensions: none' + themeExclusionStr; - return; - } - - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
${themeExclusionStr}`; } private updateSearchedExtensionTable(extensions: IssueReporterExtensionData[]): void { const target = document.querySelector('.block-searchedExtensions .block-info'); + if (target) { + if (!extensions.length) { + target.innerHTML = 'Extensions: none'; + return; + } - if (!extensions.length) { - target.innerHTML = 'Extensions: none'; - return; + const table = this.getExtensionTableHtml(extensions); + target.innerHTML = `${table}
`; } - - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
`; } private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): string { @@ -950,28 +1096,41 @@ export class IssueReporter extends Disposable { } } - private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { + private getElementById(elementId: string): HTMLElement | undefined { const element = document.getElementById(elementId); if (element) { - element.addEventListener(eventType, handler); + return element; } else { const error = new Error(`${elementId} not found.`); this.logService.error(error); /* __GDPR__ - "issueReporterAddEventListenerError" : { + "issueReporterGetElementError" : { "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" } } */ - this.telemetryService.publicLog('issueReporterAddEventListenerError', { message: error.message }); + this.telemetryService.publicLog('issueReporterGetElementError', { message: error.message }); + + return undefined; + } + } + + private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { + const element = this.getElementById(elementId); + if (element) { + element.addEventListener(eventType, handler); } } } // helper functions -function hide(el) { - el.classList.add('hidden'); +function hide(el: Element | undefined | null) { + if (el) { + el.classList.add('hidden'); + } } -function show(el) { - el.classList.remove('hidden'); +function show(el: Element | undefined | null) { + if (el) { + el.classList.remove('hidden'); + } } diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index 2ef6e5f8e0c..c1f78969153 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -5,25 +5,26 @@ import { assign } from 'vs/base/common/objects'; import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; +import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; export interface IssueReporterData { - issueType?: IssueType; + issueType: IssueType; issueDescription?: string; versionInfo?: any; - systemInfo?: any; + systemInfo?: SystemInfo; processInfo?: any; workspaceInfo?: any; - includeSystemInfo?: boolean; - includeWorkspaceInfo?: boolean; - includeProcessInfo?: boolean; - includeExtensions?: boolean; - includeSearchedExtensions?: boolean; - includeSettingsSearchDetails?: boolean; + includeSystemInfo: boolean; + includeWorkspaceInfo: boolean; + includeProcessInfo: boolean; + includeExtensions: boolean; + includeSearchedExtensions: boolean; + includeSettingsSearchDetails: boolean; numberOfThemeExtesions?: number; - allExtensions?: IssueReporterExtensionData[]; + allExtensions: IssueReporterExtensionData[]; enabledNonThemeExtesions?: IssueReporterExtensionData[]; extensionsDisabled?: boolean; fileOnExtension?: boolean; @@ -34,16 +35,18 @@ export interface IssueReporterData { } export class IssueReporterModel { - private _data: IssueReporterData; + private readonly _data: IssueReporterData; - constructor(initialData?: IssueReporterData) { + constructor(initialData?: Partial) { const defaultData = { + issueType: IssueType.Bug, includeSystemInfo: true, includeWorkspaceInfo: true, includeProcessInfo: true, includeExtensions: true, includeSearchedExtensions: true, - includeSettingsSearchDetails: true + includeSettingsSearchDetails: true, + allExtensions: [] }; this._data = initialData ? assign(defaultData, initialData) : defaultData; @@ -53,7 +56,7 @@ export class IssueReporterModel { return this._data; } - update(newData: IssueReporterData): void { + update(newData: Partial): void { assign(this._data, newData); } @@ -65,17 +68,26 @@ ${this._data.issueDescription} ${this.getExtensionVersion()} VS Code version: ${this._data.versionInfo && this._data.versionInfo.vscodeVersion} OS version: ${this._data.versionInfo && this._data.versionInfo.os} - +${this.getRemoteOSes()} ${this.getInfos()} `; } - fileOnExtension(): boolean { + private getRemoteOSes(): string { + if (this._data.systemInfo && this._data.systemInfo.remoteData.length) { + return this._data.systemInfo.remoteData + .map(remote => isRemoteDiagnosticError(remote) ? remote.errorMessage : `Remote OS version: ${remote.machineInfo.os}`).join('\n') + '\n'; + } + + return ''; + } + + fileOnExtension(): boolean | undefined { const fileOnExtensionSupported = this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue || this._data.issueType === IssueType.FeatureRequest; - return !!(fileOnExtensionSupported && this._data.fileOnExtension); + return fileOnExtensionSupported && this._data.fileOnExtension; } private getExtensionVersion(): string { @@ -102,7 +114,7 @@ ${this.getInfos()} let info = ''; if (this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue) { - if (this._data.includeSystemInfo) { + if (this._data.includeSystemInfo && this._data.systemInfo) { info += this.generateSystemInfoMd(); } } @@ -119,7 +131,7 @@ ${this.getInfos()} } if (this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue) { - if (this._data.includeExtensions) { + if (!this._data.fileOnExtension && this._data.includeExtensions) { info += this.generateExtensionsMd(); } } @@ -146,13 +158,32 @@ ${this.getInfos()} |---|---| `; - Object.keys(this._data.systemInfo).forEach(k => { - const data = typeof this._data.systemInfo[k] === 'object' - ? Object.keys(this._data.systemInfo[k]).map(key => `${key}: ${this._data.systemInfo[k][key]}`).join('
') - : this._data.systemInfo[k]; + if (this._data.systemInfo) { - md += `|${k}|${data}|\n`; - }); + md += `|CPUs|${this._data.systemInfo.cpus}| +|GPU Status|${Object.keys(this._data.systemInfo.gpuStatus).map(key => `${key}: ${this._data.systemInfo!.gpuStatus[key]}`).join('
')}| +|Load (avg)|${this._data.systemInfo.load}| +|Memory (System)|${this._data.systemInfo.memory}| +|Process Argv|${this._data.systemInfo.processArgs}| +|Screen Reader|${this._data.systemInfo.screenReader}| +|VM|${this._data.systemInfo.vmHint}|`; + + this._data.systemInfo.remoteData.forEach(remote => { + if (isRemoteDiagnosticError(remote)) { + md += `\n\n${remote.errorMessage}`; + } else { + md += ` + +|Item|Value| +|---|---| +|Remote|${remote.hostName}| +|OS|${remote.machineInfo.os}| +|CPUs|${remote.machineInfo.cpus}| +|Memory (System)|${remote.machineInfo.memory}| +|VM|${remote.machineInfo.vmHint}|`; + } + }); + } md += '\n'; @@ -194,7 +225,7 @@ ${this._data.workspaceInfo}; return 'Extensions: none' + themeExclusionStr; } - let tableHeader = `Extension|Author (truncated)|Version + const tableHeader = `Extension|Author (truncated)|Version ---|---|---`; const table = this._data.enabledNonThemeExtesions.map(e => { return `${e.name}|${e.publisher.substr(0, 3)}|${e.version}`; @@ -224,7 +255,7 @@ Literal matches: ${this._data.filterResultCount}`; return `No fuzzy results`; } - let tableHeader = `Setting|Extension|Score + const tableHeader = `Setting|Extension|Score ---|---|---`; const table = this._data.actualSearchResults.map(setting => { return `${setting.key}|${setting.extensionId}|${String(setting.score).slice(0, 5)}`; diff --git a/src/vs/code/electron-browser/issue/issueReporterPage.ts b/src/vs/code/electron-browser/issue/issueReporterPage.ts index 601f772d2a5..63f42d9b960 100644 --- a/src/vs/code/electron-browser/issue/issueReporterPage.ts +++ b/src/vs/code/electron-browser/issue/issueReporterPage.ts @@ -19,12 +19,11 @@ export default (): string => `
- - + -
${escape(localize('disableExtensionsLabelText', "Try to reproduce the problem after {0}. If the problem only reproduces when extensions are active, it is likely an issue with an extension.")) + @@ -63,9 +62,9 @@ export default (): string => `
+ key: 'sendSystemInfo', + comment: ['{0} is either "show" or "hide" and is a button to toggle the visibility of the system information'] + }, "Include my system information ({0})")).replace('{0}', `${escape(localize('show', "show"))}`)} @@ -73,9 +72,9 @@ export default (): string => `
+ key: 'sendProcessInfo', + comment: ['{0} is either "show" or "hide" and is a button to toggle the visibility of the process info'] + }, "Include my currently running processes ({0})")).replace('{0}', `${escape(localize('show', "show"))}`)}